From 99e89920081ae01d5bbe3c2117a6b9cf7f5340bc Mon Sep 17 00:00:00 2001 From: Nikita Kunets Date: Mon, 18 Jul 2022 18:10:19 +0300 Subject: [PATCH] Handle private_kube_api field for cluster requests (#69) * Handle private_kube_api field for cluster requests * Add info about new regions to readme --- README.md | 2 + pkg/v1/cluster/requests_opts.go | 4 + pkg/v1/cluster/schemas.go | 2 + pkg/v1/cluster/testing/fixtures.go | 122 +++++++++++++++++++++++- pkg/v1/cluster/testing/requests_test.go | 42 ++++++++ 5 files changed, 169 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8b30023..96594a2 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,8 @@ Selectel Managed Kubernetes Service currently has the following API endpoints: | https://ru-7.mks.selcloud.ru/v1 | ru-7 | | https://ru-8.mks.selcloud.ru/v1 | ru-8 | | https://ru-9.mks.selcloud.ru/v1 | ru-9 | +| https://uz-1.mks.selcloud.ru/v1 | uz-1 | +| https://nl-1.mks.selcloud.ru/v1 | nl-1 | You can also retrieve all available API endpoints from the Identity catalog. diff --git a/pkg/v1/cluster/requests_opts.go b/pkg/v1/cluster/requests_opts.go index 2a946fe..73b4820 100644 --- a/pkg/v1/cluster/requests_opts.go +++ b/pkg/v1/cluster/requests_opts.go @@ -50,6 +50,10 @@ type CreateOpts struct { // KubernetesOptions represents additional k8s options such as pod security policy, // feature gates (Alpha stage only) and admission controllers. KubernetesOptions *KubernetesOptions `json:"kubernetes_options,omitempty"` + + // PrivateKubeAPI specifies if kube API should be available from the Internet or not. + // By default false so kube API available from the Internet. + PrivateKubeAPI *bool `json:"private_kube_api,omitempty"` } // UpdateOpts represents options for the cluster Update request. diff --git a/pkg/v1/cluster/schemas.go b/pkg/v1/cluster/schemas.go index e4e6451..32d0aa7 100644 --- a/pkg/v1/cluster/schemas.go +++ b/pkg/v1/cluster/schemas.go @@ -125,6 +125,8 @@ type View struct { // KubernetesOptions represents additional k8s options such as pod security policy, // feature gates (Alpha stage only) and admission controllers. KubernetesOptions *KubernetesOptions `json:"kubernetes_options,omitempty"` + + PrivateKubeAPI bool `json:"private_kube_api"` } func (result *View) UnmarshalJSON(b []byte) error { diff --git a/pkg/v1/cluster/testing/fixtures.go b/pkg/v1/cluster/testing/fixtures.go index e050bab..0d57ef9 100644 --- a/pkg/v1/cluster/testing/fixtures.go +++ b/pkg/v1/cluster/testing/fixtures.go @@ -1093,6 +1093,7 @@ const testCreateClusterResponseRaw = ` "subnet_id": "", "updated_at": null, "zonal": false, + "private_kube_api": false, "kubernetes_options": { "enable_pod_security_policy": false, "feature_gates": [ @@ -1132,6 +1133,7 @@ var expectedCreateClusterResponse = &cluster.View{ FeatureGates: []string{"CSIMigrationOpenStack"}, AdmissionControllers: []string{"LimitRanger"}, }, + PrivateKubeAPI: false, } // testCreateClusterEnableBoolsOptsRaw represents marshalled options for the Create request @@ -1212,7 +1214,8 @@ const testCreateClusterDisableBoolsOptsRaw = ` "taints": [] } ], - "zonal": false + "zonal": false, + "private_kube_api": false } } ` @@ -1240,6 +1243,7 @@ var testCreateClusterDisableBoolsOpts = &cluster.CreateOpts{ Taints: []nodegroup.Taint{}, }, }, + PrivateKubeAPI: testutils.BoolToPtr(false), } // testCreateClusterDisableBoolsResponseRaw represents a raw response from the Create request @@ -1265,7 +1269,8 @@ const testCreateClusterDisableBoolsResponseRaw = ` "status": "PENDING_CREATE", "subnet_id": "", "updated_at": null, - "zonal": false + "zonal": false, + "private_kube_api": false } } ` @@ -1292,6 +1297,7 @@ var expectedCreateClusterDisableBoolsResponse = &cluster.View{ EnableAutorepair: false, EnablePatchVersionAutoUpgrade: false, Zonal: false, + PrivateKubeAPI: false, } // testCreateZonalClusterOptsRaw represents marshalled options for the Create request @@ -1367,7 +1373,8 @@ const testCreateZonalClusterResponseRaw = ` "status": "PENDING_CREATE", "subnet_id": "", "updated_at": null, - "zonal": true + "zonal": true, + "private_kube_api": false } } ` @@ -1395,6 +1402,115 @@ var expectedCreateZonalClusterResponse = &cluster.View{ Zonal: true, } +// testCreatePrivateKubeAPIClusterOptsRaw represents marshalled options for the Create request +// with private kube API attribute set to true. +const testCreatePrivateKubeAPIClusterOptsRaw = ` +{ + "cluster": { + "name": "test-private-cluster-0", + "kube_version": "1.16.9", + "region": "ru-3", + "enable_autorepair": true, + "enable_patch_version_auto_upgrade": false, + "nodegroups": [ + { + "count": 1, + "cpus": 1, + "ram_mb": 2048, + "volume_gb": 10, + "volume_type": "fast.ru-3a", + "keypair_name": "ssh-key", + "availability_zone": "ru-3a", + "labels": { + "test-label-key": "test-label-value" + }, + "taints": [] + } + ], + "zonal": false, + "private_kube_api": true + } +} +` + +// testCreatePrivateKubeAPIClusterOpts represents options for the Create request with private kube API attribute set to true. +var testCreatePrivateKubeAPIClusterOpts = &cluster.CreateOpts{ + Name: "test-private-cluster-0", + KubeVersion: "1.16.9", + Region: "ru-3", + EnableAutorepair: testutils.BoolToPtr(true), + EnablePatchVersionAutoUpgrade: testutils.BoolToPtr(false), + Nodegroups: []*nodegroup.CreateOpts{ + { + Count: 1, + CPUs: 1, + RAMMB: 2048, + VolumeGB: 10, + VolumeType: "fast.ru-3a", + KeypairName: "ssh-key", + AvailabilityZone: "ru-3a", + Labels: map[string]string{ + "test-label-key": "test-label-value", + }, + Taints: []nodegroup.Taint{}, + }, + }, + PrivateKubeAPI: testutils.BoolToPtr(true), + Zonal: testutils.BoolToPtr(false), +} + +// testCreatePrivateKubeAPIClusterResponseRaw represents a raw response from the Create cluster with private kube API request. +const testCreatePrivateKubeAPIClusterResponseRaw = ` +{ + "cluster": { + "additional_software": null, + "created_at": "2020-02-13T09:18:32.05753Z", + "enable_autorepair": true, + "enable_patch_version_auto_upgrade": false, + "id": "effe751d-501a-4b06-8e23-3f686dbfccf6", + "kube_api_ip": "", + "kube_version": "1.16.9", + "maintenance_last_start": "2020-02-13T09:18:32.05753Z", + "maintenance_window_end": "03:00:00", + "maintenance_window_start": "01:00:00", + "name": "test-private-cluster-0", + "network_id": "", + "pki_tree_updated_at": null, + "project_id": "69744a03bebe4fd0a77e5a4c882e3059", + "region": "ru-3", + "status": "PENDING_CREATE", + "subnet_id": "", + "updated_at": null, + "zonal": false, + "private_kube_api": true + } +} +` + +// expectedCreatePrivateKubeAPIClusterResponse represents an unmarshalled testCreatePrivateKubeAPIClusterResponseRaw. +var expectedCreatePrivateKubeAPIClusterResponse = &cluster.View{ + ID: "effe751d-501a-4b06-8e23-3f686dbfccf6", + CreatedAt: &clusterResponseTimestamp, + UpdatedAt: nil, + Name: "test-private-cluster-0", + Status: "PENDING_CREATE", + ProjectID: "69744a03bebe4fd0a77e5a4c882e3059", + NetworkID: "", + SubnetID: "", + KubeAPIIP: "", + KubeVersion: "1.16.9", + Region: "ru-3", + AdditionalSoftware: nil, + PKITreeUpdatedAt: nil, + MaintenanceWindowStart: "01:00:00", + MaintenanceWindowEnd: "03:00:00", + MaintenanceLastStart: &clusterResponseTimestamp, + EnableAutorepair: true, + EnablePatchVersionAutoUpgrade: false, + Zonal: false, + PrivateKubeAPI: true, +} + // testManyClustersInvalidResponseRaw represents a raw invalid response with several clusters. const testManyClustersInvalidResponseRaw = ` { diff --git a/pkg/v1/cluster/testing/requests_test.go b/pkg/v1/cluster/testing/requests_test.go index edd60c9..ff20704 100644 --- a/pkg/v1/cluster/testing/requests_test.go +++ b/pkg/v1/cluster/testing/requests_test.go @@ -519,6 +519,48 @@ func TestCreateZonalCluster(t *testing.T) { } } +func TestCreatePrivateKubeAPICluster(t *testing.T) { + endpointCalled := false + testEnv := testutils.SetupTestEnv() + defer testEnv.TearDownTestEnv() + + testutils.HandleReqWithBody(t, &testutils.HandleReqOpts{ + Mux: testEnv.Mux, + URL: "/v1/clusters", + RawResponse: testCreatePrivateKubeAPIClusterResponseRaw, + RawRequest: testCreatePrivateKubeAPIClusterOptsRaw, + Method: http.MethodPost, + Status: http.StatusCreated, + CallFlag: &endpointCalled, + }) + + ctx := context.Background() + testClient := &v1.ServiceClient{ + HTTPClient: &http.Client{}, + TokenID: testutils.TokenID, + Endpoint: testEnv.Server.URL + "/v1", + UserAgent: testutils.UserAgent, + } + + actual, httpResponse, err := cluster.Create(ctx, testClient, testCreatePrivateKubeAPIClusterOpts) + if err != nil { + t.Fatal(err) + } + if !endpointCalled { + t.Fatal("endpoint wasn't called") + } + if httpResponse == nil { + t.Fatal("expected an HTTP response from the Create method") + } + if httpResponse.StatusCode != http.StatusCreated { + t.Fatalf("expected %d status in the HTTP response, but got %d", + http.StatusCreated, httpResponse.StatusCode) + } + if !reflect.DeepEqual(expectedCreatePrivateKubeAPIClusterResponse, actual) { + t.Fatalf("expected %#v, but got %#v", expectedCreatePrivateKubeAPIClusterResponse, actual) + } +} + func TestCreateClusterHTTPError(t *testing.T) { endpointCalled := false testEnv := testutils.SetupTestEnv()