diff --git a/pkg/v1/client.go b/pkg/v1/client.go index 6e8528a..3ff2f59 100644 --- a/pkg/v1/client.go +++ b/pkg/v1/client.go @@ -12,16 +12,18 @@ import ( ) const ( - ResourceURLCluster = "clusters" - ResourceURLKubeversion = "kubeversions" - ResourceURLKubeconfig = "kubeconfig" - ResourceURLRotateCerts = "rotate-certs" - ResourceURLUpgradePatchVersion = "upgrade-patch-version" - ResourceURLUpgradeMinorVersion = "upgrade-minor-version" - ResourceURLTask = "tasks" - ResourceURLNodegroup = "nodegroups" - ResourceURLResize = "resize" - ResourceURLReinstall = "reinstall" + ResourceURLCluster = "clusters" + ResourceURLKubeversion = "kubeversions" + ResourceURLKubeconfig = "kubeconfig" + ResourceURLRotateCerts = "rotate-certs" + ResourceURLUpgradePatchVersion = "upgrade-patch-version" + ResourceURLUpgradeMinorVersion = "upgrade-minor-version" + ResourceURLTask = "tasks" + ResourceURLNodegroup = "nodegroups" + ResourceURLResize = "resize" + ResourceURLReinstall = "reinstall" + ResourceURLFeatureGates = "feature-gates" + ResourceURLAdmissionControllers = "admission-controllers" ) const ( diff --git a/pkg/v1/cluster/doc.go b/pkg/v1/cluster/doc.go index fa5f859..3466537 100644 --- a/pkg/v1/cluster/doc.go +++ b/pkg/v1/cluster/doc.go @@ -63,7 +63,7 @@ Example of updating an existing cluster KubernetesOptions: &cluster.KubernetesOptions{ EnablePodSecurityPolicy: false, FeatureGates: []string{ - "BoundServiceAccountTokenVolume", + "TTLAfterFinished", }, AdmissionControllers: []string{ "NamespaceLifecycle", diff --git a/pkg/v1/cluster/testing/fixtures.go b/pkg/v1/cluster/testing/fixtures.go index 8f6c4b7..d69176b 100644 --- a/pkg/v1/cluster/testing/fixtures.go +++ b/pkg/v1/cluster/testing/fixtures.go @@ -34,7 +34,7 @@ const testGetClusterResponseRaw = ` "kubernetes_options": { "enable_pod_security_policy": true, "feature_gates": [ - "BoundServiceAccountTokenVolume", + "TTLAfterFinished", "CSIMigrationOpenStack" ], "admission_controllers": [ @@ -71,7 +71,7 @@ var expectedGetClusterResponse = &cluster.View{ KubernetesOptions: &cluster.KubernetesOptions{ EnablePodSecurityPolicy: true, FeatureGates: []string{ - "BoundServiceAccountTokenVolume", + "TTLAfterFinished", "CSIMigrationOpenStack", }, AdmissionControllers: []string{ @@ -107,7 +107,7 @@ const testGetZonalClusterResponseRaw = ` "kubernetes_options": { "enable_pod_security_policy": true, "feature_gates": [ - "BoundServiceAccountTokenVolume", + "TTLAfterFinished", "CSIMigrationOpenStack" ], "admission_controllers": [ @@ -142,7 +142,7 @@ var expectedGetZonalClusterResponse = &cluster.View{ KubernetesOptions: &cluster.KubernetesOptions{ EnablePodSecurityPolicy: true, FeatureGates: []string{ - "BoundServiceAccountTokenVolume", + "TTLAfterFinished", "CSIMigrationOpenStack", }, AdmissionControllers: []string{ diff --git a/pkg/v1/kubeoptions/doc.go b/pkg/v1/kubeoptions/doc.go new file mode 100644 index 0000000..87c4d65 --- /dev/null +++ b/pkg/v1/kubeoptions/doc.go @@ -0,0 +1,25 @@ +/* +Package kubeoptions provides the ability to retrieve all available Kubernetes +feature gates and admission controllers through the MKS V1 API. + +Example of getting available feature gates by Kubernetes version: + + availableFG, _, err := kubeoptions.ListFeatureGates(ctx, mksClient) + if err != nil { + log.Fatal(err) + } + for _, fgList := range availableFG { + fmt.Printf("%s: %v\n", fgList.KubeVersion, fgList.Names) + } + +Example of getting available admission controllers by Kubernetes version: + + availableAC, _, err := kubeoptions.ListAdmissionControllers(ctx, mksClient) + if err != nil { + log.Fatal(err) + } + for _, acList := range availableAC { + fmt.Printf("%s: %v\n", acList.KubeVersion, acList.Names) + } +*/ +package kubeoptions diff --git a/pkg/v1/kubeoptions/requests.go b/pkg/v1/kubeoptions/requests.go new file mode 100644 index 0000000..9ce7ed8 --- /dev/null +++ b/pkg/v1/kubeoptions/requests.go @@ -0,0 +1,55 @@ +package kubeoptions + +import ( + "context" + "net/http" + "strings" + + v1 "github.com/selectel/mks-go/pkg/v1" +) + +// ListFeatureGates gets a list of available feature gates by Kubernetes versions. +func ListFeatureGates(ctx context.Context, client *v1.ServiceClient) ([]*View, *v1.ResponseResult, error) { + url := strings.Join([]string{client.Endpoint, v1.ResourceURLFeatureGates}, "/") + responseResult, err := client.DoRequest(ctx, http.MethodGet, url, nil) + if err != nil { + return nil, nil, err + } + if responseResult.Err != nil { + return nil, responseResult, responseResult.Err + } + + // Extract available admission-controllers from the response body. + var result struct { + FGList []*View `json:"feature_gates"` + } + err = responseResult.ExtractResult(&result) + if err != nil { + return nil, responseResult, err + } + + return result.FGList, responseResult, nil +} + +// ListAdmissionControllers gets a list of available admission controllers by Kubernetes versions. +func ListAdmissionControllers(ctx context.Context, client *v1.ServiceClient) ([]*View, *v1.ResponseResult, error) { + url := strings.Join([]string{client.Endpoint, v1.ResourceURLAdmissionControllers}, "/") + responseResult, err := client.DoRequest(ctx, http.MethodGet, url, nil) + if err != nil { + return nil, nil, err + } + if responseResult.Err != nil { + return nil, responseResult, responseResult.Err + } + + // Extract available admission-controllers from the response body. + var result struct { + ACList []*View `json:"admission_controllers"` + } + err = responseResult.ExtractResult(&result) + if err != nil { + return nil, responseResult, err + } + + return result.ACList, responseResult, nil +} diff --git a/pkg/v1/kubeoptions/schemas.go b/pkg/v1/kubeoptions/schemas.go new file mode 100644 index 0000000..8dd7d26 --- /dev/null +++ b/pkg/v1/kubeoptions/schemas.go @@ -0,0 +1,10 @@ +package kubeoptions + +// View represents list of feature-gates/admission-controllers by kubernetes version. +type View struct { + // KubeVersion represents the Kubernetes minor version in format: "X.Y". + KubeVersion string `json:"KubeVersionMinor"` + + // Names represents list of feature-gate names. + Names []string `json:"Names"` +} diff --git a/pkg/v1/kubeoptions/testing/fixtures.go b/pkg/v1/kubeoptions/testing/fixtures.go new file mode 100644 index 0000000..6f1efdf --- /dev/null +++ b/pkg/v1/kubeoptions/testing/fixtures.go @@ -0,0 +1,414 @@ +package testing + +import "github.com/selectel/mks-go/pkg/v1/kubeoptions" + +var expectedFeatureGates = []*kubeoptions.View{ + { + KubeVersion: "1.15", + Names: []string{"ProcMountType", "RemainingItemCount", "APIResponseCompression", "CSIMigrationOpenStack", "CSIMigrationAzureDisk", "TTLAfterFinished", "VolumePVCDataSource", "ServerSideApply", "BalanceAttachedNodeVolumes", "HyperVContainer", "WinOverlay", "QOSReserved", "SCTPSupport", "CSIInlineVolume", "CustomCPUCFSQuotaPeriod", "VolumeSnapshotDataSource", "WindowsGMSA", "WatchBookmark", "NonPreemptingPriority", "CSIMigrationAzureFile", "ExpandCSIVolumes", "CSIMigration", "ServiceNodeExclusion", "RequestManagement", "ResourceLimitsPriorityFunction", "DynamicAuditing", "ServiceLoadBalancerFinalizer", "CSIMigrationAWS", "LocalStorageCapacityIsolationFSQuotaMonitoring", "WinDSR", "CustomResourceDefaulting", "CSIMigrationGCE"}, + }, + { + KubeVersion: "1.16", + Names: []string{"NonPreemptingPriority", "VolumeSnapshotDataSource", "CSIMigrationAzureFile", "EvenPodsSpread", "CSIMigration", "NodeDisruptionExclusion", "EndpointSlice", "ServiceNodeExclusion", "RequestManagement", "ResourceLimitsPriorityFunction", "CSIMigrationAWS", "LocalStorageCapacityIsolationFSQuotaMonitoring", "DynamicAuditing", "CSIMigrationGCE", "PodOverhead", "WinDSR", "WindowsRunAsUserName", "APIResponseCompression", "CSIMigrationOpenStack", "HPAScaleToZero", "ProcMountType", "RemainingItemCount", "CSIMigrationAzureDisk", "EphemeralContainers", "TTLAfterFinished", "IPv6DualStack", "StartupProbe", "TopologyManager", "BalanceAttachedNodeVolumes", "HyperVContainer", "WinOverlay", "CustomCPUCFSQuotaPeriod", "LegacyNodeRoleBehavior", "QOSReserved", "SCTPSupport"}, + }, + { + KubeVersion: "1.17", + Names: []string{"BalanceAttachedNodeVolumes", "CSIMigrationAzureFileComplete", "HyperVContainer", "WinOverlay", "QOSReserved", "SCTPSupport", "CustomCPUCFSQuotaPeriod", "LegacyNodeRoleBehavior", "NonPreemptingPriority", "EvenPodsSpread", "CSIMigrationAzureFile", "CSIMigrationAzureDiskComplete", "NodeDisruptionExclusion", "ServiceTopology", "ServiceNodeExclusion", "ResourceLimitsPriorityFunction", "CSIMigrationGCEComplete", "CSIMigrationOpenStackComplete", "APIPriorityAndFairness", "DynamicAuditing", "CSIMigrationAWSComplete", "LocalStorageCapacityIsolationFSQuotaMonitoring", "WinDSR", "PodOverhead", "ProcMountType", "RemainingItemCount", "APIResponseCompression", "CSIMigrationOpenStack", "HPAScaleToZero", "CSIMigrationAzureDisk", "EphemeralContainers", "TTLAfterFinished", "IPv6DualStack", "StartupProbe", "TopologyManager"}, + }, + { + KubeVersion: "1.18", + Names: []string{"AnyVolumeDataSource", "APIPriorityAndFairness", "APIResponseCompression", "BalanceAttachedNodeVolumes", "CSIMigrationAWSComplete", "CSIMigrationAzureDisk", "CSIMigrationAzureDiskComplete", "CSIMigrationAzureFile", "CSIMigrationAzureFileComplete", "CSIMigrationGCEComplete", "CSIMigrationOpenStack", "CSIMigrationOpenStackComplete", "ConfigurableFSGroupPolicy", "CustomCPUCFSQuotaPeriod", "DynamicAuditing", "EndpointSliceProxying", "EphemeralContainers", "HPAScaleToZero", "HugePageStorageMediumSize", "ImmutableEphemeralVolumes", "IPv6DualStack", "LegacyNodeRoleBehavior", "LocalStorageCapacityIsolationFSQuotaMonitoring", "NodeDisruptionExclusion", "NonPreemptingPriority", "PodOverhead", "ProcMountType", "QOSReserved", "RemainingItemCount", "ResourceLimitsPriorityFunction", "ServiceAccountIssuerDiscovery", "ServiceAppProtocol", "ServiceNodeExclusion", "ServiceTopology", "TTLAfterFinished", "TopologyManager"}, + }, +} + +var expectedAdmissionControllers = []*kubeoptions.View{ + { + KubeVersion: "1.15", + Names: []string{"ExtendedResourceToleration", "ServiceAccount", "DefaultTolerationSeconds", "CertificateApproval", "PodSecurityPolicy", "AlwaysPullImages", "StorageObjectInUseProtection", "ImagePolicyWebhook", "LimitRanger", "NamespaceLifecycle", "PodNodeSelector", "Priority", "EventRateLimit", "PersistentVolumeClaimResize", "PodPreset", "SecurityContextDeny", "LimitPodHardAntiAffinityTopology", "CertificateSubjectRestrictions", "OwnerReferencesPermissionEnforcement", "ResourceQuota", "ValidatingAdmissionWebhook", "CertificateSigning", "NamespaceExists", "PodTolerationRestriction", "TaintNodesByCondition", "DefaultStorageClass", "NamespaceAutoProvision", "MutatingAdmissionWebhook"}, + }, + { + KubeVersion: "1.16", + Names: []string{"AlwaysPullImages", "CertificateApproval", "PodSecurityPolicy", "StorageObjectInUseProtection", "Priority", "EventRateLimit", "ImagePolicyWebhook", "LimitRanger", "NamespaceLifecycle", "PodNodeSelector", "LimitPodHardAntiAffinityTopology", "PersistentVolumeClaimResize", "PodPreset", "SecurityContextDeny", "ValidatingAdmissionWebhook", "CertificateSubjectRestrictions", "OwnerReferencesPermissionEnforcement", "ResourceQuota", "RuntimeClass", "CertificateSigning", "NamespaceExists", "PodTolerationRestriction", "MutatingAdmissionWebhook", "TaintNodesByCondition", "DefaultStorageClass", "NamespaceAutoProvision", "DefaultTolerationSeconds", "ExtendedResourceToleration", "ServiceAccount"}, + }, + { + KubeVersion: "1.17", + Names: []string{"StorageObjectInUseProtection", "TaintNodesByCondition", "LimitPodHardAntiAffinityTopology", "SecurityContextDeny", "LimitRanger", "PodSecurityPolicy", "MutatingAdmissionWebhook", "ValidatingAdmissionWebhook", "AlwaysPullImages", "CertificateSigning", "EventRateLimit", "DefaultStorageClass", "PodPreset", "ResourceQuota", "ExtendedResourceToleration", "NamespaceExists", "OwnerReferencesPermissionEnforcement", "ServiceAccount", "RuntimeClass", "CertificateSubjectRestrictions", "DefaultTolerationSeconds", "NamespaceAutoProvision", "ImagePolicyWebhook", "PodNodeSelector", "PodTolerationRestriction", "CertificateApproval", "NamespaceLifecycle", "PersistentVolumeClaimResize", "Priority"}, + }, + { + KubeVersion: "1.18", + Names: []string{"StorageObjectInUseProtection", "TaintNodesByCondition", "LimitPodHardAntiAffinityTopology", "SecurityContextDeny", "LimitRanger", "PodSecurityPolicy", "MutatingAdmissionWebhook", "ValidatingAdmissionWebhook", "AlwaysPullImages", "CertificateSigning", "EventRateLimit", "DefaultStorageClass", "PodPreset", "ResourceQuota", "ExtendedResourceToleration", "NamespaceExists", "OwnerReferencesPermissionEnforcement", "ServiceAccount", "RuntimeClass", "CertificateSubjectRestrictions", "DefaultTolerationSeconds", "NamespaceAutoProvision", "ImagePolicyWebhook", "PodNodeSelector", "PodTolerationRestriction", "CertificateApproval", "NamespaceLifecycle", "PersistentVolumeClaimResize", "Priority"}, + }, +} + +var testListFeatureGatesResponseRaw = `{ + "feature_gates": [ + { + "KubeVersionMinor": "1.15", + "Names": [ + "ProcMountType", + "RemainingItemCount", + "APIResponseCompression", + "CSIMigrationOpenStack", + "CSIMigrationAzureDisk", + "TTLAfterFinished", + "VolumePVCDataSource", + "ServerSideApply", + "BalanceAttachedNodeVolumes", + "HyperVContainer", + "WinOverlay", + "QOSReserved", + "SCTPSupport", + "CSIInlineVolume", + "CustomCPUCFSQuotaPeriod", + "VolumeSnapshotDataSource", + "WindowsGMSA", + "WatchBookmark", + "NonPreemptingPriority", + "CSIMigrationAzureFile", + "ExpandCSIVolumes", + "CSIMigration", + "ServiceNodeExclusion", + "RequestManagement", + "ResourceLimitsPriorityFunction", + "DynamicAuditing", + "ServiceLoadBalancerFinalizer", + "CSIMigrationAWS", + "LocalStorageCapacityIsolationFSQuotaMonitoring", + "WinDSR", + "CustomResourceDefaulting", + "CSIMigrationGCE" + ] + }, + { + "KubeVersionMinor": "1.16", + "Names": [ + "NonPreemptingPriority", + "VolumeSnapshotDataSource", + "CSIMigrationAzureFile", + "EvenPodsSpread", + "CSIMigration", + "NodeDisruptionExclusion", + "EndpointSlice", + "ServiceNodeExclusion", + "RequestManagement", + "ResourceLimitsPriorityFunction", + "CSIMigrationAWS", + "LocalStorageCapacityIsolationFSQuotaMonitoring", + "DynamicAuditing", + "CSIMigrationGCE", + "PodOverhead", + "WinDSR", + "WindowsRunAsUserName", + "APIResponseCompression", + "CSIMigrationOpenStack", + "HPAScaleToZero", + "ProcMountType", + "RemainingItemCount", + "CSIMigrationAzureDisk", + "EphemeralContainers", + "TTLAfterFinished", + "IPv6DualStack", + "StartupProbe", + "TopologyManager", + "BalanceAttachedNodeVolumes", + "HyperVContainer", + "WinOverlay", + "CustomCPUCFSQuotaPeriod", + "LegacyNodeRoleBehavior", + "QOSReserved", + "SCTPSupport" + ] + }, + { + "KubeVersionMinor": "1.17", + "Names": [ + "BalanceAttachedNodeVolumes", + "CSIMigrationAzureFileComplete", + "HyperVContainer", + "WinOverlay", + "QOSReserved", + "SCTPSupport", + "CustomCPUCFSQuotaPeriod", + "LegacyNodeRoleBehavior", + "NonPreemptingPriority", + "EvenPodsSpread", + "CSIMigrationAzureFile", + "CSIMigrationAzureDiskComplete", + "NodeDisruptionExclusion", + "ServiceTopology", + "ServiceNodeExclusion", + "ResourceLimitsPriorityFunction", + "CSIMigrationGCEComplete", + "CSIMigrationOpenStackComplete", + "APIPriorityAndFairness", + "DynamicAuditing", + "CSIMigrationAWSComplete", + "LocalStorageCapacityIsolationFSQuotaMonitoring", + "WinDSR", + "PodOverhead", + "ProcMountType", + "RemainingItemCount", + "APIResponseCompression", + "CSIMigrationOpenStack", + "HPAScaleToZero", + "CSIMigrationAzureDisk", + "EphemeralContainers", + "TTLAfterFinished", + "IPv6DualStack", + "StartupProbe", + "TopologyManager" + ] + }, + { + "KubeVersionMinor": "1.18", + "Names": [ + "AnyVolumeDataSource", + "APIPriorityAndFairness", + "APIResponseCompression", + "BalanceAttachedNodeVolumes", + "CSIMigrationAWSComplete", + "CSIMigrationAzureDisk", + "CSIMigrationAzureDiskComplete", + "CSIMigrationAzureFile", + "CSIMigrationAzureFileComplete", + "CSIMigrationGCEComplete", + "CSIMigrationOpenStack", + "CSIMigrationOpenStackComplete", + "ConfigurableFSGroupPolicy", + "CustomCPUCFSQuotaPeriod", + "DynamicAuditing", + "EndpointSliceProxying", + "EphemeralContainers", + "HPAScaleToZero", + "HugePageStorageMediumSize", + "ImmutableEphemeralVolumes", + "IPv6DualStack", + "LegacyNodeRoleBehavior", + "LocalStorageCapacityIsolationFSQuotaMonitoring", + "NodeDisruptionExclusion", + "NonPreemptingPriority", + "PodOverhead", + "ProcMountType", + "QOSReserved", + "RemainingItemCount", + "ResourceLimitsPriorityFunction", + "ServiceAccountIssuerDiscovery", + "ServiceAppProtocol", + "ServiceNodeExclusion", + "ServiceTopology", + "TTLAfterFinished", + "TopologyManager" + ] + } + ] +}` + +var testFeatureGatesAsRawList = `[ + { + "KubeVersionMinor": "1.15", + "Names": [ + "ProcMountType", + "RemainingItemCount", + "APIResponseCompression", + ] + }, + { + "KubeVersionMinor": "1.16", + "Names": [ + "NonPreemptingPriority", + "VolumeSnapshotDataSource", + "CSIMigrationAzureFile", + ] + }, + { + "KubeVersionMinor": "1.17", + "Names": [ + "BalanceAttachedNodeVolumes", + "CSIMigrationAzureFileComplete", + "HyperVContainer", + ] + }, + { + "KubeVersionMinor": "1.18", + "Names": [ + "AnyVolumeDataSource", + "APIPriorityAndFairness", + "APIResponseCompression", + ] + } + ]` + +var testListAdmissionControllersResponseRaw = `{ + "admission_controllers": [ + { + "KubeVersionMinor": "1.15", + "Names": [ + "ExtendedResourceToleration", + "ServiceAccount", + "DefaultTolerationSeconds", + "CertificateApproval", + "PodSecurityPolicy", + "AlwaysPullImages", + "StorageObjectInUseProtection", + "ImagePolicyWebhook", + "LimitRanger", + "NamespaceLifecycle", + "PodNodeSelector", + "Priority", + "EventRateLimit", + "PersistentVolumeClaimResize", + "PodPreset", + "SecurityContextDeny", + "LimitPodHardAntiAffinityTopology", + "CertificateSubjectRestrictions", + "OwnerReferencesPermissionEnforcement", + "ResourceQuota", + "ValidatingAdmissionWebhook", + "CertificateSigning", + "NamespaceExists", + "PodTolerationRestriction", + "TaintNodesByCondition", + "DefaultStorageClass", + "NamespaceAutoProvision", + "MutatingAdmissionWebhook" + ] + }, + { + "KubeVersionMinor": "1.16", + "Names": [ + "AlwaysPullImages", + "CertificateApproval", + "PodSecurityPolicy", + "StorageObjectInUseProtection", + "Priority", + "EventRateLimit", + "ImagePolicyWebhook", + "LimitRanger", + "NamespaceLifecycle", + "PodNodeSelector", + "LimitPodHardAntiAffinityTopology", + "PersistentVolumeClaimResize", + "PodPreset", + "SecurityContextDeny", + "ValidatingAdmissionWebhook", + "CertificateSubjectRestrictions", + "OwnerReferencesPermissionEnforcement", + "ResourceQuota", + "RuntimeClass", + "CertificateSigning", + "NamespaceExists", + "PodTolerationRestriction", + "MutatingAdmissionWebhook", + "TaintNodesByCondition", + "DefaultStorageClass", + "NamespaceAutoProvision", + "DefaultTolerationSeconds", + "ExtendedResourceToleration", + "ServiceAccount" + ] + }, + { + "KubeVersionMinor": "1.17", + "Names": [ + "StorageObjectInUseProtection", + "TaintNodesByCondition", + "LimitPodHardAntiAffinityTopology", + "SecurityContextDeny", + "LimitRanger", + "PodSecurityPolicy", + "MutatingAdmissionWebhook", + "ValidatingAdmissionWebhook", + "AlwaysPullImages", + "CertificateSigning", + "EventRateLimit", + "DefaultStorageClass", + "PodPreset", + "ResourceQuota", + "ExtendedResourceToleration", + "NamespaceExists", + "OwnerReferencesPermissionEnforcement", + "ServiceAccount", + "RuntimeClass", + "CertificateSubjectRestrictions", + "DefaultTolerationSeconds", + "NamespaceAutoProvision", + "ImagePolicyWebhook", + "PodNodeSelector", + "PodTolerationRestriction", + "CertificateApproval", + "NamespaceLifecycle", + "PersistentVolumeClaimResize", + "Priority" + ] + }, + { + "KubeVersionMinor": "1.18", + "Names": [ + "StorageObjectInUseProtection", + "TaintNodesByCondition", + "LimitPodHardAntiAffinityTopology", + "SecurityContextDeny", + "LimitRanger", + "PodSecurityPolicy", + "MutatingAdmissionWebhook", + "ValidatingAdmissionWebhook", + "AlwaysPullImages", + "CertificateSigning", + "EventRateLimit", + "DefaultStorageClass", + "PodPreset", + "ResourceQuota", + "ExtendedResourceToleration", + "NamespaceExists", + "OwnerReferencesPermissionEnforcement", + "ServiceAccount", + "RuntimeClass", + "CertificateSubjectRestrictions", + "DefaultTolerationSeconds", + "NamespaceAutoProvision", + "ImagePolicyWebhook", + "PodNodeSelector", + "PodTolerationRestriction", + "CertificateApproval", + "NamespaceLifecycle", + "PersistentVolumeClaimResize", + "Priority" + ] + } + ] +}` + +var testAdmissionControllersAsRawList = `[ + { + "KubeVersionMinor": "1.15", + "Names": [ + "ExtendedResourceToleration", + "ServiceAccount", + "DefaultTolerationSeconds", + ] + }, + { + "KubeVersionMinor": "1.16", + "Names": [ + "AlwaysPullImages", + "CertificateApproval", + "PodSecurityPolicy", + ] + }, + { + "KubeVersionMinor": "1.17", + "Names": [ + "StorageObjectInUseProtection", + "TaintNodesByCondition", + "LimitPodHardAntiAffinityTopology", + ] + }, + { + "KubeVersionMinor": "1.18", + "Names": [ + "StorageObjectInUseProtection", + "TaintNodesByCondition", + "LimitPodHardAntiAffinityTopology", + ] + } + ]` diff --git a/pkg/v1/kubeoptions/testing/requests_test.go b/pkg/v1/kubeoptions/testing/requests_test.go new file mode 100644 index 0000000..84d1eb4 --- /dev/null +++ b/pkg/v1/kubeoptions/testing/requests_test.go @@ -0,0 +1,224 @@ +package testing + +import ( + "context" + "net/http" + "reflect" + "testing" + + "github.com/selectel/mks-go/pkg/testutils" + v1 "github.com/selectel/mks-go/pkg/v1" + "github.com/selectel/mks-go/pkg/v1/kubeoptions" +) + +func TestListFeatureGates(t *testing.T) { + endpointCalled := false + testEnv := testutils.SetupTestEnv() + defer testEnv.TearDownTestEnv() + + testutils.HandleReqWithoutBody(t, &testutils.HandleReqOpts{ + Mux: testEnv.Mux, + URL: "/v1/feature-gates", + RawResponse: testListFeatureGatesResponseRaw, + Method: http.MethodGet, + Status: http.StatusOK, + 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 := kubeoptions.ListFeatureGates(ctx, testClient) + + 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 List method") + } + if httpResponse.StatusCode != http.StatusOK { + t.Fatalf("expected %d status in the HTTP response, but got %d", + http.StatusOK, httpResponse.StatusCode) + } + if !reflect.DeepEqual(expectedFeatureGates, actual) { + t.Fatalf("expected %#v, \nbut got %#v", expectedFeatureGates, actual) + } +} + +func TestListFeatureGatesTimeoutError(t *testing.T) { + testEnv := testutils.SetupTestEnv() + testEnv.Server.Close() + defer testEnv.TearDownTestEnv() + + ctx := context.Background() + testClient := &v1.ServiceClient{ + HTTPClient: &http.Client{}, + TokenID: testutils.TokenID, + Endpoint: testEnv.Server.URL + "/v1", + UserAgent: testutils.UserAgent, + } + + actual, httpResponse, err := kubeoptions.ListFeatureGates(ctx, testClient) + + if actual != nil { + t.Fatal("expected no Kubernetes versions from the List method") + } + if httpResponse != nil { + t.Fatal("expected no HTTP response from the List method") + } + if err == nil { + t.Fatal("expected error from the List method") + } +} + +func TestListFeatureGatesUnmarshallError(t *testing.T) { + endpointCalled := false + testEnv := testutils.SetupTestEnv() + defer testEnv.TearDownTestEnv() + + testutils.HandleReqWithoutBody(t, &testutils.HandleReqOpts{ + Mux: testEnv.Mux, + URL: "/v1/feature-gates", + RawResponse: testFeatureGatesAsRawList, + Method: http.MethodGet, + Status: http.StatusOK, + 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 := kubeoptions.ListFeatureGates(ctx, testClient) + + if !endpointCalled { + t.Fatal("endpoint wasn't called") + } + if actual != nil { + t.Fatal("expected no Kubernetes versions from the List method") + } + if httpResponse == nil { + t.Fatal("expected an HTTP response from the List method") + } + if err == nil { + t.Fatal("expected error from the List method") + } +} + +func TestListAdmissionControllers(t *testing.T) { + endpointCalled := false + testEnv := testutils.SetupTestEnv() + defer testEnv.TearDownTestEnv() + + testutils.HandleReqWithoutBody(t, &testutils.HandleReqOpts{ + Mux: testEnv.Mux, + URL: "/v1/admission-controllers", + RawResponse: testListAdmissionControllersResponseRaw, + Method: http.MethodGet, + Status: http.StatusOK, + 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 := kubeoptions.ListAdmissionControllers(ctx, testClient) + + 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 List method") + } + if httpResponse.StatusCode != http.StatusOK { + t.Fatalf("expected %d status in the HTTP response, but got %d", + http.StatusOK, httpResponse.StatusCode) + } + if !reflect.DeepEqual(expectedAdmissionControllers, actual) { + t.Fatalf("expected %#v, \nbut got %#v", expectedAdmissionControllers, actual) + } +} + +func TestListAdmissionControllersTimeoutError(t *testing.T) { + testEnv := testutils.SetupTestEnv() + testEnv.Server.Close() + defer testEnv.TearDownTestEnv() + + ctx := context.Background() + testClient := &v1.ServiceClient{ + HTTPClient: &http.Client{}, + TokenID: testutils.TokenID, + Endpoint: testEnv.Server.URL + "/v1", + UserAgent: testutils.UserAgent, + } + + actual, httpResponse, err := kubeoptions.ListAdmissionControllers(ctx, testClient) + + if actual != nil { + t.Fatal("expected no Kubernetes versions from the List method") + } + if httpResponse != nil { + t.Fatal("expected no HTTP response from the List method") + } + if err == nil { + t.Fatal("expected error from the List method") + } +} + +func TestListAdmissionControllersUnmarshallError(t *testing.T) { + endpointCalled := false + testEnv := testutils.SetupTestEnv() + defer testEnv.TearDownTestEnv() + + testutils.HandleReqWithoutBody(t, &testutils.HandleReqOpts{ + Mux: testEnv.Mux, + URL: "/v1/admission-controllers", + RawResponse: testAdmissionControllersAsRawList, + Method: http.MethodGet, + Status: http.StatusOK, + 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 := kubeoptions.ListAdmissionControllers(ctx, testClient) + + if !endpointCalled { + t.Fatal("endpoint wasn't called") + } + if actual != nil { + t.Fatal("expected no Kubernetes versions from the List method") + } + if httpResponse == nil { + t.Fatal("expected an HTTP response from the List method") + } + if err == nil { + t.Fatal("expected error from the List method") + } +}