diff --git a/README.md b/README.md index ac95583..2b2bfad 100644 --- a/README.md +++ b/README.md @@ -288,3 +288,4 @@ kardinal flow telepresence-intercept {{flow-id}} {{service-name}} {{local-port}} - Explore our [docs](https://kardinal.dev/docs) to learn more about how Kardinal works. - Ask questions and get help in our community [forum](https://discuss.kardinal.dev). - Read our [blog](https://blog.kardinal.dev/) for tips from developers and creators. + diff --git a/kardinal-cli/cmd/root.go b/kardinal-cli/cmd/root.go index 03ef477..88a82c5 100644 --- a/kardinal-cli/cmd/root.go +++ b/kardinal-cli/cmd/root.go @@ -568,7 +568,7 @@ var gatewayCmd = &cobra.Command{ } } - if err := deployment.StartGateway(hostFlowIdMap); err != nil { + if err := deployment.StartGateway(ctx, hostFlowIdMap); err != nil { log.Fatal("An error occurred while creating a gateway", err) } }, diff --git a/kardinal-cli/deployment/gateway.go b/kardinal-cli/deployment/gateway.go index aca18b4..e640de0 100644 --- a/kardinal-cli/deployment/gateway.go +++ b/kardinal-cli/deployment/gateway.go @@ -35,7 +35,6 @@ const ( proxyPortRangeEnd = 60000 maxRetries = 10 retryInterval = 10 * time.Second - prodNamespace = "prod" ) func hashStringToRange(s string, maxRange int) int { @@ -67,7 +66,7 @@ func findAvailablePortInRange(host string, portsInUse *[]int) (int, error) { return port, nil } -func StartGateway(hostFlowIdMap map[string]string) error { +func StartGateway(ctx context.Context, hostFlowIdMap map[string]string) error { client, err := kubernetes_pack.CreateKubernetesClient() if err != nil { return fmt.Errorf("an error occurred while creating a kubernetes client:\n %v", err) @@ -76,10 +75,27 @@ func StartGateway(hostFlowIdMap map[string]string) error { for host, flowId := range hostFlowIdMap { logrus.Printf("Starting gateway for host: %s", host) - // Check for pods in the prod namespace - err = assertProdNamespaceReady(client.GetClientSet(), flowId) + // TODO move these values to a shared library between Kardinal Manager, Kontrol and Kardinal CLI + kardinalLabelKey := "kardinal.dev" + enabledKardinal := "enabled" + + namespaceLabels := map[string]string{ + kardinalLabelKey: enabledKardinal, + } + + namespaceList, err := client.GetNamespacesByLabels(ctx, namespaceLabels) + if err != nil { + return fmt.Errorf("failed to list namespaces from Kubernetes: %v", err) + } + if len(namespaceList.Items) > 1 { + return fmt.Errorf("cannot start gateway because more than one Kardinal namespace was found") + } + baselineNamespace := namespaceList.Items[0] + + // Check for pods in the baseline namespace + err = assertBaselineNamespaceReady(client.GetClientSet(), flowId, baselineNamespace.Name) if err != nil { - return fmt.Errorf("failed to assert that prod namespace is ready: %v", err) + return fmt.Errorf("failed to assert that baseline namespace is ready: %v", err) } // Check for the Envoy filter before proceeding @@ -155,17 +171,18 @@ func StartGateway(hostFlowIdMap map[string]string) error { } // TODO move to the kubernetes package -func assertProdNamespaceReady(client *kubernetes.Clientset, flowId string) error { +func assertBaselineNamespaceReady(client *kubernetes.Clientset, flowId string, baselineNamespace string) error { + for retry := 0; retry < maxRetries; retry++ { - pods, err := client.CoreV1().Pods(prodNamespace).List(context.Background(), metav1.ListOptions{}) + pods, err := client.CoreV1().Pods(baselineNamespace).List(context.Background(), metav1.ListOptions{}) if err != nil { - logrus.Printf("Error listing pods in prod namespace (attempt %d/%d): %v", retry+1, maxRetries, err) + logrus.Printf("Error listing pods in baseline namespace (attempt %d/%d): %v", retry+1, maxRetries, err) time.Sleep(retryInterval) continue } if len(pods.Items) == 0 { - logrus.Printf("No pods found in namespace %s (attempt %d/%d)", prodNamespace, retry+1, maxRetries) + logrus.Printf("No pods found in namespace %s (attempt %d/%d)", baselineNamespace, retry+1, maxRetries) time.Sleep(retryInterval) continue } @@ -190,7 +207,7 @@ func assertProdNamespaceReady(client *kubernetes.Clientset, flowId string) error } if allReady && flowIdFound { - logrus.Printf("All pods in namespace %s are ready and flowId %s found", prodNamespace, flowId) + logrus.Printf("All pods in namespace %s are ready and flowId %s found", baselineNamespace, flowId) return nil } @@ -198,7 +215,7 @@ func assertProdNamespaceReady(client *kubernetes.Clientset, flowId string) error time.Sleep(retryInterval) } - return fmt.Errorf("failed to assert all pods are ready and flowId %s found in namespace %s after %d attempts", flowId, prodNamespace, maxRetries) + return fmt.Errorf("failed to assert all pods are ready and flowId %s found in namespace %s after %d attempts", flowId, baselineNamespace, maxRetries) } func isPodReady(pod *corev1.Pod) bool { diff --git a/kardinal-cli/kubernetes/kubernetes_client.go b/kardinal-cli/kubernetes/kubernetes_client.go index aebb206..b1d1bd7 100644 --- a/kardinal-cli/kubernetes/kubernetes_client.go +++ b/kardinal-cli/kubernetes/kubernetes_client.go @@ -173,6 +173,31 @@ func (client *kubernetesClient) RemoveNamespaceResourcesByLabels(ctx context.Con return nil } +func (client *kubernetesClient) GetNamespacesByLabels(ctx context.Context, namespaceLabels map[string]string) (*corev1.NamespaceList, error) { + namespaceClient := client.clientSet.CoreV1().Namespaces() + + listOptions := buildListOptionsFromLabels(namespaceLabels) + namespaces, err := namespaceClient.List(ctx, listOptions) + if err != nil { + return nil, stacktrace.Propagate(err, "Failed to list namespaces with labels '%+v'", namespaceLabels) + } + + // Only return objects not tombstoned by Kubernetes + var namespacesNotMarkedForDeletionList []corev1.Namespace + for _, namespace := range namespaces.Items { + deletionTimestamp := namespace.GetObjectMeta().GetDeletionTimestamp() + if deletionTimestamp == nil { + namespacesNotMarkedForDeletionList = append(namespacesNotMarkedForDeletionList, namespace) + } + } + namespacesNotMarkedForDeletionnamespaceList := corev1.NamespaceList{ + Items: namespacesNotMarkedForDeletionList, + TypeMeta: namespaces.TypeMeta, + ListMeta: namespaces.ListMeta, + } + return &namespacesNotMarkedForDeletionnamespaceList, nil +} + func buildListOptionsFromLabels(labelsMap map[string]string) metav1.ListOptions { return metav1.ListOptions{ TypeMeta: metav1.TypeMeta{ diff --git a/kardinal-manager/kardinal-manager/cluster_manager/cluster_manager.go b/kardinal-manager/kardinal-manager/cluster_manager/cluster_manager.go index 18a1813..3d6da63 100644 --- a/kardinal-manager/kardinal-manager/cluster_manager/cluster_manager.go +++ b/kardinal-manager/kardinal-manager/cluster_manager/cluster_manager.go @@ -22,6 +22,10 @@ const ( istioLabel = "istio-injection" enabledIstioValue = "enabled" telepresenceRestartedAtAnnotation = "telepresence.getambassador.io/restartedAt" + + // TODO move these values to a shared library between Kardinal Manager, Kontrol and Kardinal CLI + kardinalLabelKey = "kardinal.dev" + enabledKardinal = "enabled" ) var ( @@ -389,7 +393,8 @@ func (manager *ClusterManager) ensureNamespace(ctx context.Context, name string) ObjectMeta: metav1.ObjectMeta{ Name: name, Labels: map[string]string{ - istioLabel: enabledIstioValue, + istioLabel: enabledIstioValue, + kardinalLabelKey: enabledKardinal, }, }, }