diff --git a/README.md b/README.md index 4d08461..fa19252 100644 --- a/README.md +++ b/README.md @@ -58,8 +58,12 @@ We have a deployment called `api` with labels `app=api` and `kubernetes.io/name= 6. **Wait all pods are ready**: we repeat the operation about the new deployment. 7. **Remove temporary deployment**: now that the deployment is ready, we can cut the temporary one and leaves in place the new deployment with modified label. 8. **[Optional] Add the label to service selector**: some labels are recommenced by Kubernetes to be present as default label and thoses labels might be added to the service selector. That means we are making the matching set stronger. - - See [documentation](https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/) - - For the moment, only `kubernetes.io/name` is considered to be added to the service + - See [documentation](https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/) + - For the moment, only `kubernetes.io/name` is considered to be added to the service + +Additional documentations: + +- [Service is not the only resource that target pods](docs/resources-targeting-pods.md ) ## Zero downtime testing @@ -102,11 +106,11 @@ echo 'GET YOUR_URL' | \ ## Roadmap 1.0.0 -- [ ] Sucessful migration in production with relatively high volume -- [ ] Handle the fact the edited label is the only one used for the service -- [ ] Documentation about potentials issues from scale (DB) -- [ ] Better handling of labels added to the service and well understand of K8S's good pratices -0.1.0 -- [x] Successful test with zero downtime - [x] Basic documentation +- [x] Successful test with zero downtime +- [ ] All resources that match pod are managed +- [ ] Better handling of labels added to the service and well understand of K8S's good pratices +- [ ] Documentation about potentials issues from scale (DB) +- [ ] Handle the fact the edited label is the only one used for the service +- [ ] Successful migration in production with relatively high volume diff --git a/checks.go b/checks.go new file mode 100644 index 0000000..8a67b49 --- /dev/null +++ b/checks.go @@ -0,0 +1,19 @@ +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/docs/resources-targeting-pods.md b/docs/resources-targeting-pods.md new file mode 100644 index 0000000..c05b0c9 --- /dev/null +++ b/docs/resources-targeting-pods.md @@ -0,0 +1,33 @@ +# Service is not the only resource that target pods + +We did the base migration considering pods are matched by service. But for a complexe app, there is a lot of resources that can match pods. + +## Identify resources + +- Kubernetes + - PodDisruptionBudget + +- Keda + - ❌ ScaledObject: — `.spec.scaleTargetRef.name` (equal to the deployment name) + - ➡️ It is acceptable to not manage this as the migration will be fast. +- Istio + - AuthorizationPolicy + - RequestAuthentication + - DestinationRule + - Virtual service (host match the DNS name, means it match the deployment name [docs](https://istio.io/latest/docs/reference/config/networking/virtual-service/#VirtualService)) +- Monitoring + - PrometheusRule (rules could match pod or deployment in the query) + - PodMonitor — `.spec.selector.matchLabels` + +## Tips + +```bash +# Display existing resources in the cluster +kubectl api-resources --verbs=list --namespaced -o name +``` + +export KUBERNETES_RESOURCE=ScaledObject +export NAME= +export NAMESPACE= + +kubectl get $KUBERNETES_RESOURCE $NAME -n $NAMESPACE -o yaml | yq '.spec.selector.matchLabels' \ No newline at end of file diff --git a/main.go b/main.go index f9c55f4..c442fa6 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "flag" + "fmt" "os" "k8s.io/client-go/kubernetes" @@ -48,6 +49,17 @@ 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) + } + displaySummary( namespace, deploymentName, @@ -61,6 +73,11 @@ func main() { logInfo("Operation aborted by the user") os.Exit(0) } + c2 := askForConfirmation("I confirm that I have no gitops tool overriding my config (e.g. ArgoCD auto-sync)") + if !c2 { + logInfo("Operation aborted by the user") + os.Exit(0) + } MigrationWorkflow( namespace,