From e1dedb01571a5e5c395fad8236352615553334ea Mon Sep 17 00:00:00 2001 From: Alex Idowu Date: Thu, 19 Feb 2026 16:32:20 +0100 Subject: [PATCH] fix(agent): auto-detect config for install/update when agent exists - Add cmd/agent/cluster_config.go to read existing agent config from cluster - Update cmd/agent/install.go to use existing config if token/cluster name missing - Update cmd/agent/update.go to use existing config if token/cluster name missing - Allow running install/update without args if config exists in cluster --- cmd/agent/cluster_config.go | 66 +++++++++++++++++++++++++++++++++++++ cmd/agent/install.go | 26 +++++++++++++++ cmd/agent/update.go | 23 ++++++++++--- 3 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 cmd/agent/cluster_config.go diff --git a/cmd/agent/cluster_config.go b/cmd/agent/cluster_config.go new file mode 100644 index 0000000..35143ae --- /dev/null +++ b/cmd/agent/cluster_config.go @@ -0,0 +1,66 @@ +package agent + +import ( + "encoding/json" + "fmt" + "log" + "os/exec" +) + +// AgentClusterConfig holds the configuration retrieved from the cluster +type AgentClusterConfig struct { + Token string + ClusterName string + APIURL string +} + +// getAgentConfigFromCluster attempts to retrieve the agent configuration from the running cluster. +// It checks for the 'pipeops-agent-config' secret in the 'pipeops-system' namespace. +func getAgentConfigFromCluster() (*AgentClusterConfig, error) { + // Check if kubectl is available + if _, err := exec.LookPath("kubectl"); err != nil { + return nil, fmt.Errorf("kubectl not found") + } + + // 1. Get the secret in JSON format + // kubectl get secret pipeops-agent-config -n pipeops-system -o json + cmd := exec.Command("kubectl", "get", "secret", "pipeops-agent-config", "-n", "pipeops-system", "-o", "json") + output, err := cmd.Output() + if err != nil { + return nil, fmt.Errorf("failed to get pipeops-agent-config secret: %w", err) + } + + // 2. Parse the secret + var secret struct { + Data map[string][]byte `json:"data"` + } + if err := json.Unmarshal(output, &secret); err != nil { + return nil, fmt.Errorf("failed to parse secret json: %w", err) + } + + config := &AgentClusterConfig{} + + // 3. Extract and decode values + if tokenData, ok := secret.Data["PIPEOPS_TOKEN"]; ok { + config.Token = string(tokenData) + } else if tokenData, ok := secret.Data["AGENT_TOKEN"]; ok { // Fallback for older versions + config.Token = string(tokenData) + } + + if clusterNameData, ok := secret.Data["PIPEOPS_CLUSTER_NAME"]; ok { + config.ClusterName = string(clusterNameData) + } else if clusterNameData, ok := secret.Data["CLUSTER_NAME"]; ok { // Fallback + config.ClusterName = string(clusterNameData) + } + + if apiURLData, ok := secret.Data["PIPEOPS_API_URL"]; ok { + config.APIURL = string(apiURLData) + } + + if config.Token == "" { + return nil, fmt.Errorf("token not found in secret") + } + + log.Println("[INFO] Detected existing PipeOps agent configuration in cluster") + return config, nil +} diff --git a/cmd/agent/install.go b/cmd/agent/install.go index d25a10e..3598a5a 100644 --- a/cmd/agent/install.go +++ b/cmd/agent/install.go @@ -91,6 +91,21 @@ Examples: if clusterName == "" { clusterName = os.Getenv("CLUSTER_NAME") } + + // Try to auto-detect existing configuration if token or cluster name is missing + if token == "" || clusterName == "" { + if existingConfig, err := getAgentConfigFromCluster(); err == nil { + if token == "" { + token = existingConfig.Token + log.Println("[INFO] Using token from existing cluster configuration") + } + if clusterName == "" { + clusterName = existingConfig.ClusterName + log.Printf("[INFO] Using cluster name '%s' from existing configuration", clusterName) + } + } + } + if clusterName == "" { clusterName = "pipeops-cluster" } @@ -141,6 +156,11 @@ Examples: return nil } + // Check for existing agent configuration in the cluster + if _, err := getAgentConfigFromCluster(); err == nil { + return nil + } + return fmt.Errorf("Error: PipeOps token is required. Provide token as argument: 'pipeops agent install ' or set PIPEOPS_TOKEN environment variable") } return nil @@ -216,6 +236,12 @@ func installNewCluster(cmd *cobra.Command, token, clusterName, clusterType strin func installOnExistingCluster(cmd *cobra.Command, token, clusterName string, enableMonitoring bool) { log.Println("Installing PipeOps agent on existing cluster...") + if token == "" { + if existingConfig, err := getAgentConfigFromCluster(); err == nil { + token = existingConfig.Token + } + } + if token == "" { log.Fatalf("Error: PipeOps token is required. Please provide it as an argument or set PIPEOPS_TOKEN environment variable.") } diff --git a/cmd/agent/update.go b/cmd/agent/update.go index c4b50f6..865408c 100644 --- a/cmd/agent/update.go +++ b/cmd/agent/update.go @@ -16,15 +16,30 @@ var updateCmd = &cobra.Command{ Long: `The "update" command updates the PipeOps agent installed on your Kubernetes cluster to the latest available version.`, Run: func(cmd *cobra.Command, args []string) { token := getPipeOpsToken(cmd, args) - if token == "" { - log.Fatalf("Error: PipeOps token is required. Please login or set PIPEOPS_TOKEN environment variable.") - } clusterName, _ := cmd.Flags().GetString("cluster-name") if clusterName == "" { clusterName = os.Getenv("CLUSTER_NAME") } + // Try to auto-detect existing configuration if token or cluster name is missing + if token == "" || clusterName == "" { + if existingConfig, err := getAgentConfigFromCluster(); err == nil { + if token == "" { + token = existingConfig.Token + log.Println("[INFO] Using token from existing cluster configuration") + } + if clusterName == "" { + clusterName = existingConfig.ClusterName + log.Printf("[INFO] Using cluster name '%s' from existing configuration", clusterName) + } + } + } + + if token == "" { + log.Fatalf("Error: PipeOps token is required. Please login or set PIPEOPS_TOKEN environment variable.") + } + clusterType, _ := cmd.Flags().GetString("cluster-type") if clusterType == "" { clusterType = os.Getenv("CLUSTER_TYPE") @@ -38,7 +53,7 @@ var updateCmd = &cobra.Command{ if err != nil { log.Fatalf("Error: %v", err) } - + envVars := []string{ fmt.Sprintf("PIPEOPS_TOKEN=%s", token), "UPDATE=true",