diff --git a/README.md b/README.md index fa19252..6030718 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,8 @@ If you need help, the help flag can show you available flags (`kubernetes-labels ```bash kubernetes-labels-migrator \ -deployment="my-application" \ - -label="kubernetes.io/app" \ -namespace="default" \ + -label="kubernetes.io/app" \ -value="my-application" ``` @@ -102,6 +102,9 @@ echo 'GET YOUR_URL' | \ latency.p95+latency.p50+latency.p25 \ bytes_in.sum+bytes_out.sum ``` +### Tips + +- There is not diff in ArgoCD if the label does not impact matched labels ## Roadmap diff --git a/checks.go b/checks.go deleted file mode 100644 index 8a67b49..0000000 --- a/checks.go +++ /dev/null @@ -1,19 +0,0 @@ -package main - -import ( - "context" - - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" -) - -func isTheEditedLabelTheOnlyOne( - namespace string, - clientset *kubernetes.Clientset, - deploymentName string, - changingLabelKey string, -) bool { - deployment, _ := clientset.AppsV1().Deployments(namespace).Get(context.TODO(), deploymentName, v1.GetOptions{}) - labels := deployment.Spec.Template.ObjectMeta.Labels - return len(labels) == 1 && labels[changingLabelKey] != "" -} diff --git a/go.mod b/go.mod index 122b719..0889454 100644 --- a/go.mod +++ b/go.mod @@ -1,18 +1,18 @@ module github.com/Tchoupinax/k8s-labels-migrator -go 1.22.1 +go 1.22.2 require ( github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 github.com/fatih/color v1.16.0 github.com/go-git/go-git/v5 v5.11.0 - k8s.io/client-go v0.29.3 + k8s.io/client-go v0.30.0 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect - github.com/go-logr/logr v1.3.0 // indirect + github.com/go-logr/logr v1.4.1 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.3 // indirect @@ -36,22 +36,26 @@ require ( github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/net v0.19.0 // indirect + golang.org/x/net v0.23.0 // indirect golang.org/x/oauth2 v0.10.0 // indirect golang.org/x/sys v0.18.0 // indirect - golang.org/x/term v0.16.0 // indirect + golang.org/x/term v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect google.golang.org/protobuf v1.33.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.29.3 // indirect - k8s.io/apimachinery v0.29.3 // indirect - k8s.io/klog/v2 v2.110.1 // indirect - k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect + istio.io/api v1.22.0-alpha.1.0.20240502155341-0d73c8a3d5a7 // indirect + istio.io/client-go v1.22.0-alpha.1.0.20240502160042-ad08094057f6 // indirect + k8s.io/api v0.30.0 // indirect + k8s.io/apiextensions-apiserver v0.30.0 // indirect + k8s.io/apimachinery v0.30.0 // indirect + k8s.io/klog/v2 v2.120.1 // indirect + k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect diff --git a/go.sum b/go.sum index 7e4a4a6..bd29658 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,8 @@ github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3c github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY= github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= @@ -96,6 +98,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -114,6 +118,8 @@ golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -133,6 +139,9 @@ golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSm golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -146,16 +155,32 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +istio.io/api v1.22.0-alpha.1.0.20240502155341-0d73c8a3d5a7 h1:Mko3hqBGLFlEtNZ0Y+vXpPmJWp5c9/Z2WK9DCA6X6YY= +istio.io/api v1.22.0-alpha.1.0.20240502155341-0d73c8a3d5a7/go.mod h1:WKz6cReGS673+9crbXmIARZvk6wxQIao7u7Y4xUgbOM= +istio.io/client-go v1.22.0-alpha.1.0.20240502160042-ad08094057f6 h1:A3bhEDSQDbZk6nXZeKo484mQC9g/Q90jtcUVk7n/V0M= +istio.io/client-go v1.22.0-alpha.1.0.20240502160042-ad08094057f6/go.mod h1:/hopXdRUKzCu1euj2GDMBCy8EiPaNAoS8icH6bWYSm8= k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw= k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80= +k8s.io/api v0.30.0 h1:siWhRq7cNjy2iHssOB9SCGNCl2spiF1dO3dABqZ8niA= +k8s.io/api v0.30.0/go.mod h1:OPlaYhoHs8EQ1ql0R/TsUgaRPhpKNxIMrKQfWUp8QSE= +k8s.io/apiextensions-apiserver v0.30.0 h1:jcZFKMqnICJfRxTgnC4E+Hpcq8UEhT8B2lhBcQ+6uAs= +k8s.io/apiextensions-apiserver v0.30.0/go.mod h1:N9ogQFGcrbWqAY9p2mUAL5mGxsLqwgtUce127VtRX5Y= k8s.io/apimachinery v0.29.3 h1:2tbx+5L7RNvqJjn7RIuIKu9XTsIZ9Z5wX2G22XAa5EU= k8s.io/apimachinery v0.29.3/go.mod h1:hx/S4V2PNW4OMg3WizRrHutyB5la0iCUbZym+W0EQIU= +k8s.io/apimachinery v0.30.0 h1:qxVPsyDM5XS96NIh9Oj6LavoVFYff/Pon9cZeDIkHHA= +k8s.io/apimachinery v0.30.0/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg= k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0= +k8s.io/client-go v0.30.0 h1:sB1AGGlhY/o7KCyCEQ0bPWzYDL0pwOZO4vAtTSh/gJQ= +k8s.io/client-go v0.30.0/go.mod h1:g7li5O5256qe6TYdAMyX/otJqMhIiGgTapdLchhmOaY= k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= +k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= +k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= diff --git a/main.go b/main.go index c442fa6..484b571 100644 --- a/main.go +++ b/main.go @@ -2,9 +2,9 @@ package main import ( "flag" - "fmt" "os" + istio "istio.io/client-go/pkg/clientset/versioned" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" ) @@ -19,6 +19,7 @@ func main() { } clientset, err := kubernetes.NewForConfig(config) + istioClient, err := istio.NewForConfig(config) if err != nil { panic(err.Error()) } @@ -49,17 +50,9 @@ func main() { os.Exit(1) } - onlyOneLabel := isTheEditedLabelTheOnlyOne( - namespace, - clientset, - deploymentName, - labelToChangeKey, - ) - if onlyOneLabel { - logError(fmt.Sprintf("The label \"%s\" can not be edited because it's the only one in the matching set.", labelToChangeKey)) - os.Exit(1) - } - + logInfo("Analyzing your cluster...") + resourcesAnalyze(clientset, istioClient, namespace, deploymentName, labelToChangeKey) + logSuccess("Cluster ready") displaySummary( namespace, deploymentName, diff --git a/setup.sh b/setup.sh new file mode 100755 index 0000000..21507bf --- /dev/null +++ b/setup.sh @@ -0,0 +1,9 @@ +k3d cluster create k8s-labels-migrator \ + --kubeconfig-update-default + +helm repo add istio https://istio-release.storage.googleapis.com/charts +helm repo update + +kubectl create namespace istio-system +helm install istio-base istio/base -n istio-system --set defaultRevision=default +helm install istio-istiod istio/istiod -n istio-system --set defaultRevision=default diff --git a/summary.go b/summary.go index 56359c1..8ef7d98 100644 --- a/summary.go +++ b/summary.go @@ -1,10 +1,14 @@ package main import ( + "context" "fmt" "os" "github.com/jedib0t/go-pretty/v6/table" + istio "istio.io/client-go/pkg/clientset/versioned" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" ) func displaySummary( @@ -30,3 +34,58 @@ func displaySummary( t.Render() fmt.Println() } + +func resourcesAnalyze( + clientset *kubernetes.Clientset, + istioClient *istio.Clientset, + namespace string, + deploymentName string, + changingLabelKey string, +) { + deployment, _ := clientset.AppsV1().Deployments(namespace).Get(context.TODO(), deploymentName, v1.GetOptions{}) + service, _ := clientset.CoreV1().Services(namespace).Get(context.TODO(), deploymentName, v1.GetOptions{}) + destinationRule, _ := istioClient.NetworkingV1alpha3().DestinationRules(namespace).Get(context.TODO(), deploymentName, v1.GetOptions{}) + + deploymentSelectorLabels := deployment.Spec.Template.ObjectMeta.Labels + serviceSelectorLabels := service.Spec.Selector + destinationRuleSelectorLabels := destinationRule.Spec.Subsets[0].Labels + + fmt.Println() + t := table.NewWriter() + t.SetOutputMirror(os.Stdout) + t.AppendHeader(table.Row{"Type", "Name", "Detected", "labels count", "valid"}) + t.AppendRows([]table.Row{{ + "Deployment", + If(deployment.Name != "", deployment.Name, "—"), + If(deployment != nil, "✅", "❌"), + len(deploymentSelectorLabels), + If(len(deploymentSelectorLabels) == 1 && deploymentSelectorLabels[changingLabelKey] != "", "❌", "✅"), + }}) + t.AppendRows([]table.Row{{ + "Service", + If(service.Name != "", service.Name, "—"), + If(service.Name != "", "✅", "❌"), + len(serviceSelectorLabels), + If(len(serviceSelectorLabels) == 1 && serviceSelectorLabels[changingLabelKey] != "", "❌", "✅"), + }}) + t.AppendRows([]table.Row{{ + " DestinationRule", + If(service.Name != "", destinationRule.Name, "—"), + If(service.Name != "", "✅", "❌"), + len(destinationRuleSelectorLabels), + If(len(destinationRuleSelectorLabels) == 1 && destinationRuleSelectorLabels[changingLabelKey] != "", "❌", "✅"), + }}) + t.SetStyle(table.StyleColoredBright) + t.Render() + fmt.Println() + + if len(deploymentSelectorLabels) == 1 && deploymentSelectorLabels[changingLabelKey] != "" { + logError(fmt.Sprintf("The label \"%s\" can not be edited because it's the only one in the matching set for the deployment", changingLabelKey)) + os.Exit(1) + } + + if len(serviceSelectorLabels) == 1 && serviceSelectorLabels[changingLabelKey] != "" { + logError(fmt.Sprintf("The label \"%s\" can not be edited because it's the only one in the matching set for the service", changingLabelKey)) + os.Exit(1) + } +}