-
Notifications
You must be signed in to change notification settings - Fork 607
fix: use a new k8s_namespace column and use label based lookups #4436
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
493a7db
6d1b04c
dc48096
bfe3236
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -6,6 +6,7 @@ import ( | |||||||||||||||||||
| "strings" | ||||||||||||||||||||
|
|
||||||||||||||||||||
| "connectrpc.com/connect" | ||||||||||||||||||||
| "github.com/unkeyed/unkey/go/apps/krane/backend/kubernetes/labels" | ||||||||||||||||||||
| kranev1 "github.com/unkeyed/unkey/go/gen/proto/krane/v1" | ||||||||||||||||||||
| "github.com/unkeyed/unkey/go/pkg/ptr" | ||||||||||||||||||||
| appsv1 "k8s.io/api/apps/v1" | ||||||||||||||||||||
|
|
@@ -67,12 +68,14 @@ import ( | |||||||||||||||||||
| // Returns DEPLOYMENT_STATUS_PENDING as pods may not be immediately scheduled | ||||||||||||||||||||
| // and ready for traffic after creation. | ||||||||||||||||||||
| func (k *k8s) CreateDeployment(ctx context.Context, req *connect.Request[kranev1.CreateDeploymentRequest]) (*connect.Response[kranev1.CreateDeploymentResponse], error) { | ||||||||||||||||||||
| k8sDeploymentID := safeIDForK8s(req.Msg.GetDeployment().GetDeploymentId()) | ||||||||||||||||||||
| namespace := safeIDForK8s(req.Msg.GetDeployment().GetNamespace()) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| namespace := req.Msg.GetDeployment().GetNamespace() | ||||||||||||||||||||
| deploymentID := req.Msg.GetDeployment().GetDeploymentId() | ||||||||||||||||||||
| const krane = "krane" | ||||||||||||||||||||
|
|
||||||||||||||||||||
| k.logger.Info("creating deployment", | ||||||||||||||||||||
| "namespace", namespace, | ||||||||||||||||||||
| "deployment_id", k8sDeploymentID, | ||||||||||||||||||||
| "deployment_id", deploymentID, | ||||||||||||||||||||
| ) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| service, err := k.clientset.CoreV1(). | ||||||||||||||||||||
|
|
@@ -88,23 +91,20 @@ func (k *k8s) CreateDeployment(ctx context.Context, req *connect.Request[kranev1 | |||||||||||||||||||
| //nolint:exhaustruct | ||||||||||||||||||||
| &corev1.Service{ | ||||||||||||||||||||
| ObjectMeta: metav1.ObjectMeta{ | ||||||||||||||||||||
| Name: k8sDeploymentID, | ||||||||||||||||||||
| Namespace: namespace, | ||||||||||||||||||||
| GenerateName: "svc-", | ||||||||||||||||||||
| Namespace: namespace, | ||||||||||||||||||||
| Labels: map[string]string{ | ||||||||||||||||||||
| "unkey.deployment.id": k8sDeploymentID, | ||||||||||||||||||||
| "unkey.managed.by": "krane", | ||||||||||||||||||||
| }, | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Annotations: map[string]string{ | ||||||||||||||||||||
| "unkey.deployment.id": k8sDeploymentID, | ||||||||||||||||||||
| labels.DeploymentID: deploymentID, | ||||||||||||||||||||
| labels.ManagedBy: krane, | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| }, | ||||||||||||||||||||
|
|
||||||||||||||||||||
| //nolint:exhaustruct | ||||||||||||||||||||
| Spec: corev1.ServiceSpec{ | ||||||||||||||||||||
| Type: corev1.ServiceTypeClusterIP, // Use ClusterIP for internal communication | ||||||||||||||||||||
| Selector: map[string]string{ | ||||||||||||||||||||
| "unkey.deployment.id": k8sDeploymentID, | ||||||||||||||||||||
| labels.DeploymentID: deploymentID, | ||||||||||||||||||||
| labels.ManagedBy: krane, | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| ClusterIP: "None", | ||||||||||||||||||||
| PublishNotReadyAddresses: true, | ||||||||||||||||||||
|
|
@@ -130,11 +130,11 @@ func (k *k8s) CreateDeployment(ctx context.Context, req *connect.Request[kranev1 | |||||||||||||||||||
| //nolint: exhaustruct | ||||||||||||||||||||
| &appsv1.StatefulSet{ | ||||||||||||||||||||
| ObjectMeta: metav1.ObjectMeta{ | ||||||||||||||||||||
| Name: k8sDeploymentID, | ||||||||||||||||||||
| Namespace: namespace, | ||||||||||||||||||||
| GenerateName: "dpl-", | ||||||||||||||||||||
| Namespace: namespace, | ||||||||||||||||||||
| Labels: map[string]string{ | ||||||||||||||||||||
| "unkey.deployment.id": k8sDeploymentID, | ||||||||||||||||||||
| "unkey.managed.by": "krane", | ||||||||||||||||||||
| labels.DeploymentID: deploymentID, | ||||||||||||||||||||
| labels.ManagedBy: krane, | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| }, | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
@@ -144,14 +144,14 @@ func (k *k8s) CreateDeployment(ctx context.Context, req *connect.Request[kranev1 | |||||||||||||||||||
| Replicas: ptr.P(int32(req.Msg.GetDeployment().GetReplicas())), //nolint: gosec | ||||||||||||||||||||
| Selector: &metav1.LabelSelector{ | ||||||||||||||||||||
| MatchLabels: map[string]string{ | ||||||||||||||||||||
| "unkey.deployment.id": k8sDeploymentID, | ||||||||||||||||||||
| labels.DeploymentID: deploymentID, | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| Template: corev1.PodTemplateSpec{ | ||||||||||||||||||||
| ObjectMeta: metav1.ObjectMeta{ | ||||||||||||||||||||
| Labels: map[string]string{ | ||||||||||||||||||||
| "unkey.deployment.id": k8sDeploymentID, | ||||||||||||||||||||
| "unkey.managed.by": "krane", | ||||||||||||||||||||
| labels.DeploymentID: deploymentID, | ||||||||||||||||||||
| labels.ManagedBy: krane, | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| Annotations: map[string]string{}, | ||||||||||||||||||||
| }, | ||||||||||||||||||||
|
|
@@ -171,7 +171,7 @@ func (k *k8s) CreateDeployment(ctx context.Context, req *connect.Request[kranev1 | |||||||||||||||||||
| RestartPolicy: corev1.RestartPolicyAlways, | ||||||||||||||||||||
| Containers: []corev1.Container{ | ||||||||||||||||||||
| { | ||||||||||||||||||||
| Name: "todo", | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Image: req.Msg.GetDeployment().GetImage(), | ||||||||||||||||||||
|
Comment on lines
172
to
175
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing required Kubernetes requires every container to have a Containers: []corev1.Container{
{
-
+ Name: "app",
Image: req.Msg.GetDeployment().GetImage(),📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||
| Ports: []corev1.ContainerPort{ | ||||||||||||||||||||
| { | ||||||||||||||||||||
|
|
@@ -219,7 +219,7 @@ func (k *k8s) CreateDeployment(ctx context.Context, req *connect.Request[kranev1 | |||||||||||||||||||
| { | ||||||||||||||||||||
| APIVersion: "apps/v1", | ||||||||||||||||||||
| Kind: "StatefulSet", | ||||||||||||||||||||
| Name: k8sDeploymentID, | ||||||||||||||||||||
| Name: sfs.Name, | ||||||||||||||||||||
| UID: sfs.UID, | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,7 +3,6 @@ package kubernetes | |
| import ( | ||
| "context" | ||
| "fmt" | ||
| "strings" | ||
|
|
||
| "connectrpc.com/connect" | ||
| kranev1 "github.com/unkeyed/unkey/go/gen/proto/krane/v1" | ||
|
|
@@ -15,32 +14,72 @@ import ( | |
| // DeleteDeployment removes a deployment and all associated Kubernetes resources. | ||
| // | ||
| // This method performs a complete cleanup of a deployment by removing both | ||
| // the Service and StatefulSet resources. The cleanup follows Kubernetes | ||
| // best practices for resource deletion with background propagation to | ||
| // ensure associated pods and other resources are properly terminated. | ||
| // the Service and StatefulSet resources. Resources are selected by their | ||
| // deployment-id label rather than by name, following Kubernetes best practices | ||
| // for resource management. | ||
| func (k *k8s) DeleteDeployment(ctx context.Context, req *connect.Request[kranev1.DeleteDeploymentRequest]) (*connect.Response[kranev1.DeleteDeploymentResponse], error) { | ||
| k8sDeploymentID := strings.ReplaceAll(req.Msg.GetDeploymentId(), "_", "-") | ||
| namespace := safeIDForK8s(req.Msg.GetNamespace()) | ||
| deploymentID := req.Msg.GetDeploymentId() | ||
| namespace := req.Msg.GetNamespace() | ||
|
|
||
| k.logger.Info("deleting deployment", | ||
| "namespace", namespace, | ||
| "deployment_id", k8sDeploymentID, | ||
| "deployment_id", deploymentID, | ||
| ) | ||
|
|
||
| // Create label selector for this deployment | ||
| labelSelector := fmt.Sprintf("deployment-id=%s", deploymentID) | ||
|
Comment on lines
+29
to
+30
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use the centralized label constant for consistency. The label selector uses the hard-coded string +import "github.com/unkeyed/unkey/go/apps/krane/backend/kubernetes/labels"
+
// Create label selector for this deployment
-labelSelector := fmt.Sprintf("deployment-id=%s", deploymentID)
+labelSelector := fmt.Sprintf("%s=%s", labels.DeploymentID, deploymentID)
🤖 Prompt for AI Agents |
||
|
|
||
| //nolint: exhaustruct | ||
| err := k.clientset.CoreV1().Services(namespace).Delete(ctx, k8sDeploymentID, metav1.DeleteOptions{ | ||
| deleteOptions := metav1.DeleteOptions{ | ||
| PropagationPolicy: ptr.P(metav1.DeletePropagationBackground), | ||
| } | ||
|
|
||
| // List and delete Services with this deployment-id label | ||
| //nolint: exhaustruct | ||
| serviceList, err := k.clientset.CoreV1().Services(namespace).List(ctx, metav1.ListOptions{ | ||
| LabelSelector: labelSelector, | ||
| }) | ||
| if err != nil && !apierrors.IsNotFound(err) { | ||
| return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("failed to delete service: %w", err)) | ||
| if err != nil { | ||
| return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("failed to list services: %w", err)) | ||
| } | ||
|
|
||
| for _, service := range serviceList.Items { | ||
| k.logger.Debug("deleting service", | ||
| "name", service.Name, | ||
| "deployment_id", deploymentID, | ||
| ) | ||
| err = k.clientset.CoreV1().Services(namespace).Delete(ctx, service.Name, deleteOptions) | ||
| if err != nil && !apierrors.IsNotFound(err) { | ||
| return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("failed to delete service %s: %w", service.Name, err)) | ||
| } | ||
| } | ||
|
|
||
| // List and delete StatefulSets with this deployment-id label | ||
| //nolint: exhaustruct | ||
| err = k.clientset.AppsV1().StatefulSets(namespace).Delete(ctx, k8sDeploymentID, metav1.DeleteOptions{ | ||
| PropagationPolicy: ptr.P(metav1.DeletePropagationBackground), | ||
| statefulSetList, err := k.clientset.AppsV1().StatefulSets(namespace).List(ctx, metav1.ListOptions{ | ||
| LabelSelector: labelSelector, | ||
| }) | ||
| if err != nil && !apierrors.IsNotFound(err) { | ||
| return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("failed to delete deployment: %w", err)) | ||
| if err != nil { | ||
| return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("failed to list statefulsets: %w", err)) | ||
| } | ||
|
|
||
| for _, statefulSet := range statefulSetList.Items { | ||
| k.logger.Debug("deleting statefulset", | ||
| "name", statefulSet.Name, | ||
| "deployment_id", deploymentID, | ||
| ) | ||
| err = k.clientset.AppsV1().StatefulSets(namespace).Delete(ctx, statefulSet.Name, deleteOptions) | ||
| if err != nil && !apierrors.IsNotFound(err) { | ||
| return nil, connect.NewError(connect.CodeInternal, fmt.Errorf("failed to delete statefulset %s: %w", statefulSet.Name, err)) | ||
| } | ||
| } | ||
|
|
||
| k.logger.Info("deployment deleted successfully", | ||
| "namespace", namespace, | ||
| "deployment_id", deploymentID, | ||
| "services_deleted", len(serviceList.Items), | ||
| "statefulsets_deleted", len(statefulSetList.Items), | ||
| ) | ||
|
|
||
| return connect.NewResponse(&kranev1.DeleteDeploymentResponse{}), nil | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Extract
kraneconstant to package level for reuse.This constant is duplicated/missing in
deployment_get.go. Based on the AI summary, there should be a shared constant ingo/apps/krane/backend/kubernetes/krane.go. Consider using that or moving this to package level.Or import from the shared location if
krane.goexports it:🤖 Prompt for AI Agents