From 79f562a28244accd202701de2044933905ba6f2e Mon Sep 17 00:00:00 2001 From: Mauren Berti Date: Fri, 23 Dec 2022 14:42:12 -0500 Subject: [PATCH] Allow removal of deprecated API manifests. * Allow the removal of deprecated API manifests that do not have a successor API (such as PodSecurityPolicies). * Add unit tests for the changes introduced. Signed-off-by: Mauren Berti (cherry picked from commit b817c92553cf2f50295c2522c74dba4aabaa94c2) --- Makefile | 4 + config/Map.yaml | 3 +- go.mod | 16 +- go.sum | 29 +-- pkg/common/common.go | 60 +++++- pkg/common/common_test.go | 425 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 511 insertions(+), 26 deletions(-) create mode 100644 pkg/common/common_test.go diff --git a/Makefile b/Makefile index 5ea9029..74e1325 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,10 @@ bootstrap: export GOPROXY=$(MOD_PROXY_URL) && \ go mod download +.PHONY: test +test: + go test -v ./... + .PHONY: tag tag: @scripts/tag.sh diff --git a/config/Map.yaml b/config/Map.yaml index 0d891b5..15fb4a7 100644 --- a/config/Map.yaml +++ b/config/Map.yaml @@ -195,4 +195,5 @@ mappings: newAPI: "apiVersion: policy/v1\nkind: PodDisruptionBudget\n" deprecatedInVersion: "v1.21" removedInVersion: "v1.25" - + - deprecatedAPI: "apiVersion: policy/v1beta1\nkind: PodSecurityPolicy\n" + removedInVersion: "v1.25" diff --git a/go.mod b/go.mod index 7e9bc90..173b7b7 100644 --- a/go.mod +++ b/go.mod @@ -4,10 +4,13 @@ go 1.18 require ( github.com/maorfr/helm-plugin-utils v0.6.0 + github.com/onsi/ginkgo/v2 v2.6.1 + github.com/onsi/gomega v1.24.2 github.com/pkg/errors v0.9.1 github.com/spf13/cobra v1.5.0 github.com/spf13/pflag v1.0.5 - golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 + golang.org/x/mod v0.7.0 + gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.10.3 sigs.k8s.io/yaml v1.3.0 ) @@ -59,7 +62,7 @@ require ( github.com/golang/protobuf v1.5.2 // indirect github.com/google/btree v1.0.1 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/google/go-cmp v0.5.8 // indirect + github.com/google/go-cmp v0.5.9 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.2.0 // indirect @@ -113,12 +116,12 @@ require ( go.etcd.io/etcd/api/v3 v3.5.4 // indirect go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect - golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect + golang.org/x/net v0.4.0 // indirect golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect - golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/sys v0.3.0 // indirect + golang.org/x/term v0.3.0 // indirect + golang.org/x/text v0.5.0 // indirect golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect @@ -126,7 +129,6 @@ require ( google.golang.org/protobuf v1.28.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.25.2 // indirect k8s.io/apiextensions-apiserver v0.25.2 // indirect k8s.io/apimachinery v0.25.2 // indirect diff --git a/go.sum b/go.sum index 69283ed..2e43259 100644 --- a/go.sum +++ b/go.sum @@ -301,8 +301,8 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= @@ -503,13 +503,14 @@ github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6 github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU= +github.com/onsi/ginkgo/v2 v2.6.1 h1:1xQPCjcqYw/J5LchOcp4/2q/jzJFjiAOc25chhnDw+Q= +github.com/onsi/ginkgo/v2 v2.6.1/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= +github.com/onsi/gomega v1.24.2 h1:J/tulyYK6JwBldPViHJReihxxZ+22FHs0piGjQAvoUE= +github.com/onsi/gomega v1.24.2/go.mod h1:gs3J10IS7Z7r7eXRoNJIrNqU4ToQukCJhFtKrWgHWnk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec= @@ -695,8 +696,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -743,8 +744,8 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -847,12 +848,13 @@ golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -861,8 +863,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/pkg/common/common.go b/pkg/common/common.go index c5877ce..053ac8b 100644 --- a/pkg/common/common.go +++ b/pkg/common/common.go @@ -67,6 +67,18 @@ func ReplaceManifestUnSupportedAPIs(origManifest, mapFile string, kubeConfig Kub } // Check for deprecated or removed APIs and map accordingly to supported versions + modifiedManifest, err = ReplaceManifestData(mapMetadata, modifiedManifest, kubeVersionStr) + if err != nil { + return "", err + } + + return modifiedManifest, nil +} + +// ReplaceManifestData scans the release manifest string for deprecated APIs in a given Kubernetes version and replaces +// their groups and versions if there is a successor, or fully removes the manifest for that specific resource if no +// successors exist (such as the PodSecurityPolicy API). +func ReplaceManifestData(mapMetadata *mapping.Metadata, modifiedManifest string, kubeVersionStr string) (string, error) { for _, mapping := range mapMetadata.Mappings { deprecatedAPI := mapping.DeprecatedAPI supportedAPI := mapping.NewAPI @@ -76,25 +88,63 @@ func ReplaceManifestUnSupportedAPIs(origManifest, mapFile string, kubeConfig Kub } else { apiVersionStr = mapping.RemovedInVersion } + if !semver.IsValid(apiVersionStr) { return "", errors.Errorf("Failed to get the deprecated or removed Kubernetes version for API: %s", strings.ReplaceAll(deprecatedAPI, "\n", " ")) } + if semver.Compare(apiVersionStr, kubeVersionStr) > 0 { + log.Printf("The following API does not require mapping as the "+ + "API is not deprecated or removed in Kubernetes '%s':\n\"%s\"\n", apiVersionStr, + deprecatedAPI) + // skip to next mapping + continue + } + if count := strings.Count(modifiedManifest, deprecatedAPI); count > 0 { - if semver.Compare(apiVersionStr, kubeVersionStr) > 0 { - log.Printf("The following API does not require mapping as the "+ - "API is not deprecated or removed in Kubernetes '%s':\n\"%s\"\n", apiVersionStr, - deprecatedAPI) + if supportedAPI == "" { + log.Printf("Found %d instances of deprecated or removed Kubernetes API:\n\"%s\"\n", count, deprecatedAPI) + modifiedManifest = removeDeprecatedAPIWithoutSuccessor(count, deprecatedAPI, modifiedManifest) } else { log.Printf("Found %d instances of deprecated or removed Kubernetes API:\n\"%s\"\nSupported API equivalent:\n\"%s\"\n", count, deprecatedAPI, supportedAPI) modifiedManifest = strings.ReplaceAll(modifiedManifest, deprecatedAPI, supportedAPI) } } } - return modifiedManifest, nil } +// removeDeprecatedAPIWithoutSuccessor removes a deprecated API that has no successor specified in the mapping file. +func removeDeprecatedAPIWithoutSuccessor(count int, deprecatedAPI string, modifiedManifest string) string { + for repl := 0; repl < count; repl++ { + // find the position where the API header is + apiIndex := strings.Index(modifiedManifest, deprecatedAPI) + + // find the next separator index + separatorIndex := strings.Index(modifiedManifest[apiIndex:], "---\n") + + // find the previous separator index + previousSeparatorIndex := strings.LastIndex(modifiedManifest[:apiIndex], "---\n") + + /* + * if no previous separator index was found, it means the resource is at the beginning and not + * prefixed by --- + */ + if previousSeparatorIndex == -1 { + previousSeparatorIndex = 0 + } + + if separatorIndex == -1 { // this means we reached the end of input + modifiedManifest = modifiedManifest[:previousSeparatorIndex] + } else { + modifiedManifest = modifiedManifest[:previousSeparatorIndex] + modifiedManifest[separatorIndex+apiIndex:] + } + } + + modifiedManifest = strings.Trim(modifiedManifest, "\n") + return modifiedManifest +} + func getKubernetesServerVersion(kubeConfig KubeConfig) (string, error) { clientSet := utils.GetClientSetWithKubeConfig(kubeConfig.File, kubeConfig.Context) if clientSet == nil { diff --git a/pkg/common/common_test.go b/pkg/common/common_test.go new file mode 100644 index 0000000..799a943 --- /dev/null +++ b/pkg/common/common_test.go @@ -0,0 +1,425 @@ +package common_test + +import ( + "bytes" + "github.com/helm/helm-mapkubeapis/pkg/common" + "github.com/helm/helm-mapkubeapis/pkg/mapping" + "github.com/pkg/errors" + "gopkg.in/yaml.v3" + "io" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestCommon(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Deprecated APIs replacement suite") +} + +// CheckDecode verifies that the passed YAML is parsing correctly +// It doesn't check semantic correctness +func CheckDecode(manifest string) error { + decoder := yaml.NewDecoder(bytes.NewBufferString(manifest)) + + for { + var value interface{} + + err := decoder.Decode(&value) + if errors.Is(err, io.EOF) { + break + } + + if err != nil { + return err + } + } + + return nil +} + +var _ = Describe("replacing deprecated APIs", Ordered, func() { + var mapFile *mapping.Metadata + + var deprecatedPodDisruptionBudget string + var newPodDisruptionBudget string + + var deprecatedDeployment string + var newDeployment string + + var deprecatedPodSecurityPolicy string + + BeforeAll(func() { + deprecatedPodDisruptionBudget = "apiVersion: policy/v1beta1\nkind: PodDisruptionBudget\n" + newPodDisruptionBudget = "apiVersion: policy/v1\nkind: PodDisruptionBudget\n" + + deprecatedDeployment = "apiVersion: apps/v1beta2\nkind: Deployment\n" + newDeployment = "apiVersion: apps/v1\nkind: Deployment\n" + + deprecatedPodSecurityPolicy = "apiVersion: policy/v1beta1\nkind: PodSecurityPolicy\n" + + mapFile = &mapping.Metadata{ + Mappings: []*mapping.Mapping{ + { + // - deprecatedAPI: "apiVersion: policy/v1beta1\nkind: PodDisruptionBudget\n" + // newAPI: "apiVersion: policy/v1\nkind: PodDisruptionBudget\n" + // deprecatedInVersion: "v1.21" + // removedInVersion: "v1.25" + DeprecatedAPI: deprecatedPodDisruptionBudget, + NewAPI: newPodDisruptionBudget, + DeprecatedInVersion: "v1.21", + RemovedInVersion: "v1.25", + }, + { + // - deprecatedAPI: "apiVersion: apps/v1beta2\nkind: Deployment\n" + // newAPI: "apiVersion: apps/v1\nkind: Deployment\n" + // deprecatedInVersion: "v1.9" + // removedInVersion: "v1.16" + DeprecatedAPI: deprecatedDeployment, + NewAPI: newDeployment, + DeprecatedInVersion: "v1.9", + RemovedInVersion: "v1.16", + }, + { + // - deprecatedAPI: "apiVersion: policy/v1beta1\nkind: PodSecurityPolicy" + // deprecatedInVersion: "v1.21" + // removedInVersion: "v1.25" + DeprecatedAPI: deprecatedPodSecurityPolicy, + RemovedInVersion: "v1.25", + }, + }, + } + }) + + When("a deprecated API exists in the manifest", func() { + When("it is a superseded API", func() { + var ( + deploymentManifest string + expectedResultingDeploymentManifest string + podDisruptionBudgetManifest string + expectedResultingPodDisruptionBudgetManifest string + ) + + BeforeAll(func() { + deploymentManifest = `--- +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: test + namespace: test-ns +spec: + template: + containers: + - name: test-container + image: test-image` + + expectedResultingDeploymentManifest = `--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test + namespace: test-ns +spec: + template: + containers: + - name: test-container + image: test-image` + + podDisruptionBudgetManifest = `--- +apiVersion: policy/v1beta1 +kind: PodDisruptionBudget +metadata: + name: pdb-test + namespace: test-ns` + + expectedResultingPodDisruptionBudgetManifest = `--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: pdb-test + namespace: test-ns` + }) + + It("replaces deprecated resources with a new version in Kubernetes v1.25", func() { + kubeVersion125 := "v1.25" + modifiedDeploymentManifest, err := common.ReplaceManifestData(mapFile, deploymentManifest, kubeVersion125) + + Expect(err).ToNot(HaveOccurred()) + Expect(modifiedDeploymentManifest).To(Equal(expectedResultingDeploymentManifest)) + + modifiedPdbManifest, err := common.ReplaceManifestData(mapFile, podDisruptionBudgetManifest, kubeVersion125) + Expect(err).ToNot(HaveOccurred()) + Expect(modifiedPdbManifest).To(Equal(expectedResultingPodDisruptionBudgetManifest)) + + err = CheckDecode(modifiedDeploymentManifest) + Expect(err).ToNot(HaveOccurred()) + }) + }) + + When("it is a removed API", func() { + var kubeVersion125 = "v1.25" + var expectedResultManifest = `--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test + namespace: test-ns +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: test-sa + namespace: test-ns` + + When("it is in the beginning of the manifest", func() { + var podSecurityPolicyManifest = `--- +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: test-psp +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test + namespace: test-ns +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: test-sa + namespace: test-ns` + + It("removes the deprecated API manifest and leaves a valid YAML", func() { + modifiedDeploymentManifest, err := common.ReplaceManifestData(mapFile, podSecurityPolicyManifest, kubeVersion125) + + Expect(err).ToNot(HaveOccurred()) + Expect(modifiedDeploymentManifest).ToNot(ContainSubstring(deprecatedPodSecurityPolicy)) + Expect(expectedResultManifest).To(Equal(expectedResultManifest)) + + err = CheckDecode(modifiedDeploymentManifest) + Expect(err).ToNot(HaveOccurred()) + }) + }) + + When("it is at the end of the manifest", func() { + var podSecurityPolicyManifest = `--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test + namespace: test-ns +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: test-sa + namespace: test-ns +--- +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: test-psp` + + It("removes the deprecated API manifest and leaves a valid YAML", func() { + modifiedDeploymentManifest, err := common.ReplaceManifestData(mapFile, podSecurityPolicyManifest, kubeVersion125) + + Expect(err).ToNot(HaveOccurred()) + Expect(modifiedDeploymentManifest).ToNot(ContainSubstring(deprecatedPodSecurityPolicy)) + Expect(modifiedDeploymentManifest).To(Equal(expectedResultManifest)) + + err = CheckDecode(modifiedDeploymentManifest) + Expect(err).ToNot(HaveOccurred()) + }) + }) + + When("it is in the middle of other manifests", func() { + var podSecurityPolicyManifest = `--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test + namespace: test-ns +--- +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: test-psp +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: test-sa + namespace: test-ns` + + It("removes the deprecated API manifest and leaves a valid YAML", func() { + modifiedDeploymentManifest, err := common.ReplaceManifestData(mapFile, podSecurityPolicyManifest, kubeVersion125) + + Expect(err).ToNot(HaveOccurred()) + Expect(modifiedDeploymentManifest).ToNot(ContainSubstring(deprecatedPodSecurityPolicy)) + Expect(modifiedDeploymentManifest).To(Equal(expectedResultManifest)) + + err = CheckDecode(modifiedDeploymentManifest) + Expect(err).ToNot(HaveOccurred()) + }) + }) + + When("a three-dash is missing at the beginning", func() { + var podSecurityPolicyManifest = `apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: test-psp +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test + namespace: test-ns +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: test-sa + namespace: test-ns` + + It("removes the deprecated API manifest and leaves a valid YAML", func() { + modifiedDeploymentManifest, err := common.ReplaceManifestData(mapFile, podSecurityPolicyManifest, kubeVersion125) + + Expect(err).ToNot(HaveOccurred()) + Expect(modifiedDeploymentManifest).ToNot(ContainSubstring(deprecatedPodSecurityPolicy)) + Expect(modifiedDeploymentManifest).To(Equal(expectedResultManifest)) + + err = CheckDecode(modifiedDeploymentManifest) + Expect(err).ToNot(HaveOccurred()) + }) + }) + + When("apiVersion is not the first field", func() { + var podSecurityPolicyManifest = `--- +metadata: + name: test-psp +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test + namespace: test-ns +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: test-sa + namespace: test-ns` + + It("removes the deprecated API manifest and leaves a valid YAML", func() { + modifiedDeploymentManifest, err := common.ReplaceManifestData(mapFile, podSecurityPolicyManifest, kubeVersion125) + + Expect(err).ToNot(HaveOccurred()) + Expect(modifiedDeploymentManifest).ToNot(ContainSubstring(deprecatedPodSecurityPolicy)) + Expect(modifiedDeploymentManifest).To(Equal(expectedResultManifest)) + + err = CheckDecode(modifiedDeploymentManifest) + Expect(err).ToNot(HaveOccurred()) + }) + }) + + When("apiVersion is not the first field and a three-dash is missing at the beginning of the manifest", func() { + var podSecurityPolicyManifest = `metadata: + name: test-psp +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test + namespace: test-ns +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: test-sa + namespace: test-ns` + + It("removes the deprecated API manifest and leaves a valid YAML", func() { + modifiedDeploymentManifest, err := common.ReplaceManifestData(mapFile, podSecurityPolicyManifest, kubeVersion125) + + Expect(err).ToNot(HaveOccurred()) + Expect(modifiedDeploymentManifest).ToNot(ContainSubstring(deprecatedPodSecurityPolicy)) + Expect(modifiedDeploymentManifest).To(Equal(expectedResultManifest)) + + err = CheckDecode(modifiedDeploymentManifest) + Expect(err).ToNot(HaveOccurred()) + }) + }) + + When("apiVersion is not the first field and the resource is in the middle of the manifest", func() { + var podSecurityPolicyManifest = `--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test + namespace: test-ns +--- +metadata: + name: test-psp +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: test-sa + namespace: test-ns` + + It("removes the deprecated API manifest and leaves a valid YAML", func() { + modifiedDeploymentManifest, err := common.ReplaceManifestData(mapFile, podSecurityPolicyManifest, kubeVersion125) + + Expect(err).ToNot(HaveOccurred()) + Expect(modifiedDeploymentManifest).ToNot(ContainSubstring(deprecatedPodSecurityPolicy)) + Expect(modifiedDeploymentManifest).To(Equal(expectedResultManifest)) + + err = CheckDecode(modifiedDeploymentManifest) + Expect(err).ToNot(HaveOccurred()) + }) + }) + + When("apiVersion is not the first field and the resource is at the end of the manifest", func() { + var podSecurityPolicyManifest = `--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test + namespace: test-ns +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: test-sa + namespace: test-ns +--- +metadata: + name: test-psp +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +spec: + allowPrivilegeEscalation: true +` + + It("removes the deprecated API manifest and leaves a valid YAML", func() { + modifiedDeploymentManifest, err := common.ReplaceManifestData(mapFile, podSecurityPolicyManifest, kubeVersion125) + + Expect(err).ToNot(HaveOccurred()) + Expect(modifiedDeploymentManifest).ToNot(ContainSubstring(deprecatedPodSecurityPolicy)) + Expect(modifiedDeploymentManifest).To(Equal(expectedResultManifest)) + + err = CheckDecode(modifiedDeploymentManifest) + Expect(err).ToNot(HaveOccurred()) + }) + }) + }) + }) +})