From 828bacc51f2cc2600e53486371310118bba18576 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Tue, 10 Aug 2021 23:09:05 -0700 Subject: [PATCH 1/2] Extract component versions to a static file This should make it easier to add new components, or change versions. Embed the file into the binary with Go Embed. Signed-off-by: David Calavera --- cmd/check_versions.go | 37 ++++++++++++------------- cmd/check_versions_test.go | 55 ++++++++++++++++++++++++++++++++++++++ cmd/config/components.json | 36 +++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 20 deletions(-) create mode 100644 cmd/check_versions_test.go create mode 100644 cmd/config/components.json diff --git a/cmd/check_versions.go b/cmd/check_versions.go index 554c8ba..c56a410 100644 --- a/cmd/check_versions.go +++ b/cmd/check_versions.go @@ -2,6 +2,8 @@ package cmd import ( "context" + "encoding/json" + "fmt" "os" "strings" @@ -12,6 +14,8 @@ import ( "k8s.io/cli-runtime/pkg/printers" "k8s.io/client-go/kubernetes" + _ "embed" + "github.com/blang/semver/v4" ) @@ -49,22 +53,8 @@ type Services []Service // that can be used for reference type KubernetesVersions map[string]Services -// This is the source of truth for what versions -// belong on which servers -var ( - versions = KubernetesVersions{ - "v1.19": Services{ - {Name: "cert-manager", Version: "1.4.0"}, - {Name: "coredns", Version: "1.8.4"}, - {Name: "kube-proxy", Version: "1.19.6-eksbuild.2"}, - {Name: "kube-state-metrics", Version: "2.1.0"}, - {Name: "metrics-server", Version: "0.5.0"}, - {Name: "node-problem-detector", Version: "0.8.9"}, - {Name: "nvidia-device-plugin", Version: "0.9.0"}, - {Name: "cluster-autoscaler", Version: "1.19"}, - }, - } -) +//go:embed config/components.json +var componentsConfig []byte var getVersionsCmd = &cobra.Command{ Use: "versions", @@ -92,7 +82,10 @@ var getVersionsCmd = &cobra.Command{ for _, s := range svcs { // walk through required versions and // find the matching service - req := getRequiredVersion(kubeVersion, s.Name) + req, err := getRequiredVersion(kubeVersion, s.Name) + if err != nil { + return err + } if req == "unknown" { continue } @@ -128,13 +121,17 @@ func isOutOfDate(required, current string) (bool, error) { return r.Compare(c) > 0, nil } -func getRequiredVersion(kubernetesVersion string, serviceName string) string { +func getRequiredVersion(kubernetesVersion string, serviceName string) (string, error) { + var versions KubernetesVersions + if err := json.Unmarshal(componentsConfig, &versions); err != nil { + return "", fmt.Errorf("unable to read components configuration: %w", err) + } for _, e := range versions[kubernetesVersion] { if e.Name == serviceName { - return e.Version + return e.Version, nil } } - return "unknown" + return "unknown", nil } func getDeployments(clientset *kubernetes.Clientset, namespace string) (Services, error) { diff --git a/cmd/check_versions_test.go b/cmd/check_versions_test.go new file mode 100644 index 0000000..078ea92 --- /dev/null +++ b/cmd/check_versions_test.go @@ -0,0 +1,55 @@ +package cmd + +import "testing" + +func TestGetRequiredVersions(t *testing.T) { + cases := Services{ + { + Name: "cert-manager", + Version: "1.4.0", + }, + { + Name: "coredns", + Version: "1.8.4", + }, + { + Name: "kube-proxy", + Version: "1.19.6-eksbuild.2", + }, + { + Name: "kube-state-metrics", + Version: "2.1.0", + }, + { + Name: "metrics-server", + Version: "0.5.0", + }, + { + Name: "node-problem-detector", + Version: "0.8.9", + }, + { + Name: "nvidia-device-plugin", + Version: "0.9.0", + }, + { + Name: "cluster-autoscaler", + Version: "1.19", + }, + } + + for _, test := range cases { + actual, err := getRequiredVersion("v1.19", test.Name) + if err != nil { + t.Fatal(err) + } + assertVersion(t, test.Name, test.Version, actual) + } +} + +func assertVersion(t *testing.T, component, expected, actual string) { + t.Helper() + if expected != actual { + t.Fatalf("expected %s version %s, got %s", component, expected, actual) + } +} diff --git a/cmd/config/components.json b/cmd/config/components.json new file mode 100644 index 0000000..52af286 --- /dev/null +++ b/cmd/config/components.json @@ -0,0 +1,36 @@ +{ + "v1.19": [ + { + "Name": "cert-manager", + "Version": "1.4.0" + }, + { + "Name": "coredns", + "Version": "1.8.4" + }, + { + "Name": "kube-proxy", + "Version": "1.19.6-eksbuild.2" + }, + { + "Name": "kube-state-metrics", + "Version": "2.1.0" + }, + { + "Name": "metrics-server", + "Version": "0.5.0" + }, + { + "Name": "node-problem-detector", + "Version": "0.8.9" + }, + { + "Name": "nvidia-device-plugin", + "Version": "0.9.0" + }, + { + "Name": "cluster-autoscaler", + "Version": "1.19" + } + ] +} \ No newline at end of file From 67cef918133fdbe0b8c9ca70bb04a843fcaedda6 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Tue, 10 Aug 2021 23:16:25 -0700 Subject: [PATCH 2/2] Load versions only once. Don't unmarshall the json file for every component. Signed-off-by: David Calavera --- cmd/check_versions.go | 21 +++++++++++++++------ cmd/check_versions_test.go | 7 ++++++- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/cmd/check_versions.go b/cmd/check_versions.go index c56a410..fbd2667 100644 --- a/cmd/check_versions.go +++ b/cmd/check_versions.go @@ -78,11 +78,16 @@ var getVersionsCmd = &cobra.Command{ return err } + versions, err := loadKubernetesVersions() + if err != nil { + return err + } + // loop through services for _, s := range svcs { // walk through required versions and // find the matching service - req, err := getRequiredVersion(kubeVersion, s.Name) + req, err := getRequiredVersion(versions, kubeVersion, s.Name) if err != nil { return err } @@ -121,11 +126,7 @@ func isOutOfDate(required, current string) (bool, error) { return r.Compare(c) > 0, nil } -func getRequiredVersion(kubernetesVersion string, serviceName string) (string, error) { - var versions KubernetesVersions - if err := json.Unmarshal(componentsConfig, &versions); err != nil { - return "", fmt.Errorf("unable to read components configuration: %w", err) - } +func getRequiredVersion(versions KubernetesVersions, kubernetesVersion string, serviceName string) (string, error) { for _, e := range versions[kubernetesVersion] { if e.Name == serviceName { return e.Version, nil @@ -177,3 +178,11 @@ func getAllServices(clientset *kubernetes.Clientset, namespaces []string) (Servi } return svcs, nil } + +func loadKubernetesVersions() (KubernetesVersions, error) { + var versions KubernetesVersions + if err := json.Unmarshal(componentsConfig, &versions); err != nil { + return nil, fmt.Errorf("unable to read components configuration: %w", err) + } + return versions, nil +} diff --git a/cmd/check_versions_test.go b/cmd/check_versions_test.go index 078ea92..98b2b62 100644 --- a/cmd/check_versions_test.go +++ b/cmd/check_versions_test.go @@ -38,8 +38,13 @@ func TestGetRequiredVersions(t *testing.T) { }, } + versions, err := loadKubernetesVersions() + if err != nil { + t.Fatal(err) + } + for _, test := range cases { - actual, err := getRequiredVersion("v1.19", test.Name) + actual, err := getRequiredVersion(versions, "v1.19", test.Name) if err != nil { t.Fatal(err) }