github.com/argoproj/argo-cd/v3@v3.2.1/cmd/util/cluster.go (about) 1 package util 2 3 import ( 4 "context" 5 stderrors "errors" 6 "fmt" 7 "os" 8 "sort" 9 "strings" 10 "text/tabwriter" 11 12 "github.com/spf13/cobra" 13 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 14 "k8s.io/client-go/kubernetes" 15 "k8s.io/client-go/rest" 16 "k8s.io/client-go/tools/clientcmd" 17 clientcmdapiv1 "k8s.io/client-go/tools/clientcmd/api/v1" 18 "sigs.k8s.io/yaml" 19 20 argoappv1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1" 21 "github.com/argoproj/argo-cd/v3/util/errors" 22 ) 23 24 type ClusterEndpoint string 25 26 const ( 27 KubeConfigEndpoint ClusterEndpoint = "kubeconfig" 28 KubePublicEndpoint ClusterEndpoint = "kube-public" 29 KubeInternalEndpoint ClusterEndpoint = "internal" 30 ) 31 32 func PrintKubeContexts(ca clientcmd.ConfigAccess) { 33 config, err := ca.GetStartingConfig() 34 errors.CheckError(err) 35 w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) 36 defer func() { _ = w.Flush() }() 37 columnNames := []string{"CURRENT", "NAME", "CLUSTER", "SERVER"} 38 _, err = fmt.Fprintf(w, "%s\n", strings.Join(columnNames, "\t")) 39 errors.CheckError(err) 40 41 // sort names so output is deterministic 42 contextNames := make([]string, 0) 43 for name := range config.Contexts { 44 contextNames = append(contextNames, name) 45 } 46 sort.Strings(contextNames) 47 48 if config.Clusters == nil { 49 return 50 } 51 52 for _, name := range contextNames { 53 // ignore malformed kube config entries 54 context := config.Contexts[name] 55 if context == nil { 56 continue 57 } 58 cluster := config.Clusters[context.Cluster] 59 if cluster == nil { 60 continue 61 } 62 prefix := " " 63 if config.CurrentContext == name { 64 prefix = "*" 65 } 66 _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", prefix, name, context.Cluster, cluster.Server) 67 errors.CheckError(err) 68 } 69 } 70 71 func NewCluster(name string, namespaces []string, clusterResources bool, conf *rest.Config, managerBearerToken string, awsAuthConf *argoappv1.AWSAuthConfig, execProviderConf *argoappv1.ExecProviderConfig, labels, annotations map[string]string) *argoappv1.Cluster { 72 tlsClientConfig := argoappv1.TLSClientConfig{ 73 Insecure: conf.Insecure, 74 ServerName: conf.ServerName, 75 CAData: conf.CAData, 76 CertData: conf.CertData, 77 KeyData: conf.KeyData, 78 } 79 if len(conf.CAData) == 0 && conf.CAFile != "" { 80 data, err := os.ReadFile(conf.CAFile) 81 errors.CheckError(err) 82 tlsClientConfig.CAData = data 83 } 84 if len(conf.CertData) == 0 && conf.CertFile != "" { 85 data, err := os.ReadFile(conf.CertFile) 86 errors.CheckError(err) 87 tlsClientConfig.CertData = data 88 } 89 if len(conf.KeyData) == 0 && conf.KeyFile != "" { 90 data, err := os.ReadFile(conf.KeyFile) 91 errors.CheckError(err) 92 tlsClientConfig.KeyData = data 93 } 94 95 clst := argoappv1.Cluster{ 96 Server: conf.Host, 97 Name: name, 98 Namespaces: namespaces, 99 ClusterResources: clusterResources, 100 Config: argoappv1.ClusterConfig{ 101 TLSClientConfig: tlsClientConfig, 102 AWSAuthConfig: awsAuthConf, 103 ExecProviderConfig: execProviderConf, 104 DisableCompression: conf.DisableCompression, 105 }, 106 Labels: labels, 107 Annotations: annotations, 108 } 109 // it's a tradeoff to get proxy url from rest config 110 // more detail: https://github.com/kubernetes/kubernetes/pull/81443 111 if conf.Proxy != nil { 112 if url, err := conf.Proxy(nil); err == nil { 113 clst.Config.ProxyUrl = url.String() 114 } 115 } 116 // Bearer token will preferentially be used for auth if present, 117 // Even in presence of key/cert credentials 118 // So set bearer token only if the key/cert data is absent 119 if len(tlsClientConfig.CertData) == 0 || len(tlsClientConfig.KeyData) == 0 { 120 clst.Config.BearerToken = managerBearerToken 121 } 122 123 return &clst 124 } 125 126 // GetKubePublicEndpoint returns the kubernetes apiserver endpoint and certificate authority data as published 127 // in the kube-public. 128 func GetKubePublicEndpoint(client kubernetes.Interface) (string, []byte, error) { 129 clusterInfo, err := client.CoreV1().ConfigMaps("kube-public").Get(context.TODO(), "cluster-info", metav1.GetOptions{}) 130 if err != nil { 131 return "", nil, err 132 } 133 kubeconfig, ok := clusterInfo.Data["kubeconfig"] 134 if !ok { 135 return "", nil, stderrors.New("cluster-info does not contain a public kubeconfig") 136 } 137 // Parse Kubeconfig and get server address 138 config := &clientcmdapiv1.Config{} 139 err = yaml.Unmarshal([]byte(kubeconfig), config) 140 if err != nil { 141 return "", nil, fmt.Errorf("failed to parse cluster-info kubeconfig: %w", err) 142 } 143 if len(config.Clusters) == 0 { 144 return "", nil, stderrors.New("cluster-info kubeconfig does not have any clusters") 145 } 146 147 endpoint := config.Clusters[0].Cluster.Server 148 certificateAuthorityData := config.Clusters[0].Cluster.CertificateAuthorityData 149 return endpoint, certificateAuthorityData, nil 150 } 151 152 type ClusterOptions struct { 153 InCluster bool 154 Upsert bool 155 ServiceAccount string 156 AwsRoleArn string 157 AwsProfile string 158 AwsClusterName string 159 SystemNamespace string 160 Namespaces []string 161 ClusterResources bool 162 Name string 163 Project string 164 Shard int64 165 ExecProviderCommand string 166 ExecProviderArgs []string 167 ExecProviderEnv map[string]string 168 ExecProviderAPIVersion string 169 ExecProviderInstallHint string 170 ClusterEndpoint string 171 DisableCompression bool 172 ProxyUrl string //nolint:revive //FIXME(var-naming) 173 } 174 175 // InClusterEndpoint returns true if ArgoCD should reference the in-cluster 176 // endpoint when registering the target cluster. 177 func (o ClusterOptions) InClusterEndpoint() bool { 178 return o.InCluster || o.ClusterEndpoint == string(KubeInternalEndpoint) 179 } 180 181 func AddClusterFlags(command *cobra.Command, opts *ClusterOptions) { 182 command.Flags().BoolVar(&opts.InCluster, "in-cluster", false, "Indicates Argo CD resides inside this cluster and should connect using the internal k8s hostname (kubernetes.default.svc)") 183 command.Flags().StringVar(&opts.AwsClusterName, "aws-cluster-name", "", "AWS Cluster name if set then aws cli eks token command will be used to access cluster") 184 command.Flags().StringVar(&opts.AwsRoleArn, "aws-role-arn", "", "Optional AWS role arn. If set then AWS IAM Authenticator assumes a role to perform cluster operations instead of the default AWS credential provider chain.") 185 command.Flags().StringVar(&opts.AwsProfile, "aws-profile", "", "Optional AWS profile. If set then AWS IAM Authenticator uses this profile to perform cluster operations instead of the default AWS credential provider chain.") 186 command.Flags().StringArrayVar(&opts.Namespaces, "namespace", nil, "List of namespaces which are allowed to manage") 187 command.Flags().BoolVar(&opts.ClusterResources, "cluster-resources", false, "Indicates if cluster level resources should be managed. The setting is used only if list of managed namespaces is not empty.") 188 command.Flags().StringVar(&opts.Name, "name", "", "Overwrite the cluster name") 189 command.Flags().StringVar(&opts.Project, "project", "", "project of the cluster") 190 command.Flags().Int64Var(&opts.Shard, "shard", -1, "Cluster shard number; inferred from hostname if not set") 191 command.Flags().StringVar(&opts.ExecProviderCommand, "exec-command", "", "Command to run to provide client credentials to the cluster. You may need to build a custom ArgoCD image to ensure the command is available at runtime.") 192 command.Flags().StringArrayVar(&opts.ExecProviderArgs, "exec-command-args", nil, "Arguments to supply to the --exec-command executable") 193 command.Flags().StringToStringVar(&opts.ExecProviderEnv, "exec-command-env", nil, "Environment vars to set when running the --exec-command executable") 194 command.Flags().StringVar(&opts.ExecProviderAPIVersion, "exec-command-api-version", "", "Preferred input version of the ExecInfo for the --exec-command executable") 195 command.Flags().StringVar(&opts.ExecProviderInstallHint, "exec-command-install-hint", "", "Text shown to the user when the --exec-command executable doesn't seem to be present") 196 command.Flags().StringVar(&opts.ClusterEndpoint, "cluster-endpoint", "", "Cluster endpoint to use. Can be one of the following: 'kubeconfig', 'kube-public', or 'internal'.") 197 command.Flags().BoolVar(&opts.DisableCompression, "disable-compression", false, "Bypasses automatic GZip compression requests to the server") 198 }