github.com/argoproj/argo-cd/v3@v3.2.1/cmd/argocd/commands/repocreds.go (about)

     1  package commands
     2  
     3  import (
     4  	stderrors "errors"
     5  	"fmt"
     6  	"os"
     7  	"text/tabwriter"
     8  
     9  	log "github.com/sirupsen/logrus"
    10  	"github.com/spf13/cobra"
    11  
    12  	"github.com/argoproj/argo-cd/v3/cmd/argocd/commands/headless"
    13  	"github.com/argoproj/argo-cd/v3/cmd/argocd/commands/utils"
    14  	cmdutil "github.com/argoproj/argo-cd/v3/cmd/util"
    15  	"github.com/argoproj/argo-cd/v3/common"
    16  	argocdclient "github.com/argoproj/argo-cd/v3/pkg/apiclient"
    17  	repocredspkg "github.com/argoproj/argo-cd/v3/pkg/apiclient/repocreds"
    18  	appsv1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
    19  	"github.com/argoproj/argo-cd/v3/util/cli"
    20  	"github.com/argoproj/argo-cd/v3/util/errors"
    21  	"github.com/argoproj/argo-cd/v3/util/git"
    22  	utilio "github.com/argoproj/argo-cd/v3/util/io"
    23  	"github.com/argoproj/argo-cd/v3/util/templates"
    24  )
    25  
    26  // NewRepoCredsCommand returns a new instance of an `argocd repocreds` command
    27  func NewRepoCredsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
    28  	command := &cobra.Command{
    29  		Use:   "repocreds",
    30  		Short: "Manage credential templates for repositories",
    31  		Example: templates.Examples(`
    32  			# Add credentials with user/pass authentication to use for all repositories under the specified URL
    33  			argocd repocreds add URL --username USERNAME --password PASSWORD
    34  
    35  			# List all the configured repository credentials
    36  			argocd repocreds list
    37  
    38  			# Remove credentials for the repositories with specified URL
    39  			argocd repocreds rm URL
    40  		`),
    41  		Run: func(c *cobra.Command, args []string) {
    42  			c.HelpFunc()(c, args)
    43  			os.Exit(1)
    44  		},
    45  	}
    46  
    47  	command.AddCommand(NewRepoCredsAddCommand(clientOpts))
    48  	command.AddCommand(NewRepoCredsListCommand(clientOpts))
    49  	command.AddCommand(NewRepoCredsRemoveCommand(clientOpts))
    50  	return command
    51  }
    52  
    53  // NewRepoCredsAddCommand returns a new instance of an `argocd repocreds add` command
    54  func NewRepoCredsAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
    55  	var (
    56  		repo                     appsv1.RepoCreds
    57  		upsert                   bool
    58  		sshPrivateKeyPath        string
    59  		tlsClientCertPath        string
    60  		tlsClientCertKeyPath     string
    61  		githubAppPrivateKeyPath  string
    62  		gcpServiceAccountKeyPath string
    63  	)
    64  
    65  	// For better readability and easier formatting
    66  	repocredsAddExamples := `  # Add credentials with user/pass authentication to use for all repositories under https://git.example.com/repos
    67    argocd repocreds add https://git.example.com/repos/ --username git --password secret
    68  
    69    # Add credentials with bearer token authentication to use for all BitBucket Data Center repositories under https://bitbucket.example.com/scm
    70    argocd repocreds add https://bitbucket.example.com/scm/ --bearer-token secret-token
    71  
    72    # Add credentials with SSH private key authentication to use for all repositories under ssh://git@git.example.com/repos
    73    argocd repocreds add ssh://git@git.example.com/repos/ --ssh-private-key-path ~/.ssh/id_rsa
    74  
    75    # Add credentials with GitHub App authentication to use for all repositories under https://github.com/repos
    76    argocd repocreds add https://github.com/repos/ --github-app-id 1 --github-app-installation-id 2 --github-app-private-key-path test.private-key.pem
    77  
    78    # Add credentials with GitHub App authentication to use for all repositories under https://ghe.example.com/repos
    79    argocd repocreds add https://ghe.example.com/repos/ --github-app-id 1 --github-app-installation-id 2 --github-app-private-key-path test.private-key.pem --github-app-enterprise-base-url https://ghe.example.com/api/v3
    80  
    81    # Add credentials with helm oci registry so that these oci registry urls do not need to be added as repos individually.
    82    argocd repocreds add localhost:5000/myrepo --enable-oci --type helm 
    83  
    84    # Add credentials with GCP credentials for all repositories under https://source.developers.google.com/p/my-google-cloud-project/r/
    85    argocd repocreds add https://source.developers.google.com/p/my-google-cloud-project/r/ --gcp-service-account-key-path service-account-key.json
    86  `
    87  
    88  	command := &cobra.Command{
    89  		Use:     "add REPOURL",
    90  		Short:   "Add git repository connection parameters",
    91  		Example: repocredsAddExamples,
    92  		Run: func(c *cobra.Command, args []string) {
    93  			ctx := c.Context()
    94  
    95  			if len(args) != 1 {
    96  				c.HelpFunc()(c, args)
    97  				os.Exit(1)
    98  			}
    99  
   100  			// Repository URL
   101  			repo.URL = args[0]
   102  
   103  			// Specifying ssh-private-key-path is only valid for SSH repositories
   104  			if sshPrivateKeyPath != "" {
   105  				if ok, _ := git.IsSSHURL(repo.URL); ok {
   106  					keyData, err := os.ReadFile(sshPrivateKeyPath)
   107  					if err != nil {
   108  						log.Fatal(err)
   109  					}
   110  					repo.SSHPrivateKey = string(keyData)
   111  				} else {
   112  					errors.Fatal(errors.ErrorGeneric, "--ssh-private-key-path is only supported for SSH repositories.")
   113  				}
   114  			}
   115  
   116  			// tls-client-cert-path and tls-client-cert-key-key-path must always be
   117  			// specified together
   118  			if (tlsClientCertPath != "" && tlsClientCertKeyPath == "") || (tlsClientCertPath == "" && tlsClientCertKeyPath != "") {
   119  				errors.Fatal(errors.ErrorGeneric, "--tls-client-cert-path and --tls-client-cert-key-path must be specified together")
   120  			}
   121  
   122  			// Specifying tls-client-cert-path is only valid for HTTPS repositories
   123  			if tlsClientCertPath != "" {
   124  				if git.IsHTTPSURL(repo.URL) {
   125  					tlsCertData, err := os.ReadFile(tlsClientCertPath)
   126  					errors.CheckError(err)
   127  					tlsCertKey, err := os.ReadFile(tlsClientCertKeyPath)
   128  					errors.CheckError(err)
   129  					repo.TLSClientCertData = string(tlsCertData)
   130  					repo.TLSClientCertKey = string(tlsCertKey)
   131  				} else {
   132  					err := stderrors.New("--tls-client-cert-path is only supported for HTTPS repositories")
   133  					errors.CheckError(err)
   134  				}
   135  			}
   136  
   137  			// Specifying github-app-private-key-path is only valid for HTTPS repositories
   138  			if githubAppPrivateKeyPath != "" {
   139  				if git.IsHTTPSURL(repo.URL) {
   140  					githubAppPrivateKey, err := os.ReadFile(githubAppPrivateKeyPath)
   141  					errors.CheckError(err)
   142  					repo.GithubAppPrivateKey = string(githubAppPrivateKey)
   143  				} else {
   144  					err := stderrors.New("--github-app-private-key-path is only supported for HTTPS repositories")
   145  					errors.CheckError(err)
   146  				}
   147  			}
   148  
   149  			// Specifying gcpServiceAccountKeyPath is only valid for HTTPS repositories
   150  			if gcpServiceAccountKeyPath != "" {
   151  				if git.IsHTTPSURL(repo.URL) {
   152  					gcpServiceAccountKey, err := os.ReadFile(gcpServiceAccountKeyPath)
   153  					errors.CheckError(err)
   154  					repo.GCPServiceAccountKey = string(gcpServiceAccountKey)
   155  				} else {
   156  					err := stderrors.New("--gcp-service-account-key-path is only supported for HTTPS repositories")
   157  					errors.CheckError(err)
   158  				}
   159  			}
   160  
   161  			conn, repoIf := headless.NewClientOrDie(clientOpts, c).NewRepoCredsClientOrDie()
   162  			defer utilio.Close(conn)
   163  
   164  			// If the user set a username, but didn't supply password via --password,
   165  			// then we prompt for it
   166  			if repo.Username != "" && repo.Password == "" {
   167  				repo.Password = cli.PromptPassword(repo.Password)
   168  			}
   169  
   170  			err := cmdutil.ValidateBearerTokenAndPasswordCombo(repo.BearerToken, repo.Password)
   171  			errors.CheckError(err)
   172  			err = cmdutil.ValidateBearerTokenForGitOnly(repo.BearerToken, repo.Type)
   173  			errors.CheckError(err)
   174  			err = cmdutil.ValidateBearerTokenForHTTPSRepoOnly(repo.BearerToken, git.IsHTTPSURL(repo.URL))
   175  			errors.CheckError(err)
   176  
   177  			repoCreateReq := repocredspkg.RepoCredsCreateRequest{
   178  				Creds:  &repo,
   179  				Upsert: upsert,
   180  			}
   181  
   182  			createdRepo, err := repoIf.CreateRepositoryCredentials(ctx, &repoCreateReq)
   183  			errors.CheckError(err)
   184  			fmt.Printf("Repository credentials for '%s' added\n", createdRepo.URL)
   185  		},
   186  	}
   187  	command.Flags().StringVar(&repo.Username, "username", "", "username to the repository")
   188  	command.Flags().StringVar(&repo.Password, "password", "", "password to the repository")
   189  	command.Flags().StringVar(&repo.BearerToken, "bearer-token", "", "bearer token to the Git repository")
   190  	command.Flags().StringVar(&sshPrivateKeyPath, "ssh-private-key-path", "", "path to the private ssh key (e.g. ~/.ssh/id_rsa)")
   191  	command.Flags().StringVar(&tlsClientCertPath, "tls-client-cert-path", "", "path to the TLS client cert (must be PEM format)")
   192  	command.Flags().StringVar(&tlsClientCertKeyPath, "tls-client-cert-key-path", "", "path to the TLS client cert's key (must be PEM format)")
   193  	command.Flags().Int64Var(&repo.GithubAppId, "github-app-id", 0, "id of the GitHub Application")
   194  	command.Flags().Int64Var(&repo.GithubAppInstallationId, "github-app-installation-id", 0, "installation id of the GitHub Application")
   195  	command.Flags().StringVar(&githubAppPrivateKeyPath, "github-app-private-key-path", "", "private key of the GitHub Application")
   196  	command.Flags().StringVar(&repo.GitHubAppEnterpriseBaseURL, "github-app-enterprise-base-url", "", "base url to use when using GitHub Enterprise (e.g. https://ghe.example.com/api/v3")
   197  	command.Flags().BoolVar(&upsert, "upsert", false, "Override an existing repository with the same name even if the spec differs")
   198  	command.Flags().BoolVar(&repo.EnableOCI, "enable-oci", false, "Specifies whether helm-oci support should be enabled for this repo")
   199  	command.Flags().StringVar(&repo.Type, "type", common.DefaultRepoType, "type of the repository, \"git\" or \"helm\"")
   200  	command.Flags().StringVar(&gcpServiceAccountKeyPath, "gcp-service-account-key-path", "", "service account key for the Google Cloud Platform")
   201  	command.Flags().BoolVar(&repo.ForceHttpBasicAuth, "force-http-basic-auth", false, "whether to force basic auth when connecting via HTTP")
   202  	command.Flags().BoolVar(&repo.UseAzureWorkloadIdentity, "use-azure-workload-identity", false, "whether to use azure workload identity for authentication")
   203  	command.Flags().StringVar(&repo.Proxy, "proxy-url", "", "If provided, this URL will be used to connect via proxy")
   204  	return command
   205  }
   206  
   207  // NewRepoCredsRemoveCommand returns a new instance of an `argocd repocreds rm` command
   208  func NewRepoCredsRemoveCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
   209  	command := &cobra.Command{
   210  		Use:   "rm CREDSURL",
   211  		Short: "Remove repository credentials",
   212  		Example: templates.Examples(`
   213  			# Remove credentials for the repositories with URL https://git.example.com/repos
   214  			argocd repocreds rm https://git.example.com/repos/
   215  		`),
   216  		Run: func(c *cobra.Command, args []string) {
   217  			ctx := c.Context()
   218  
   219  			if len(args) == 0 {
   220  				c.HelpFunc()(c, args)
   221  				os.Exit(1)
   222  			}
   223  			conn, repoIf := headless.NewClientOrDie(clientOpts, c).NewRepoCredsClientOrDie()
   224  			defer utilio.Close(conn)
   225  
   226  			promptUtil := utils.NewPrompt(clientOpts.PromptsEnabled)
   227  
   228  			for _, repoURL := range args {
   229  				canDelete := promptUtil.Confirm(fmt.Sprintf("Are you sure you want to remove '%s'? [y/n] ", repoURL))
   230  				if canDelete {
   231  					_, err := repoIf.DeleteRepositoryCredentials(ctx, &repocredspkg.RepoCredsDeleteRequest{Url: repoURL})
   232  					errors.CheckError(err)
   233  					fmt.Printf("Repository credentials for '%s' removed\n", repoURL)
   234  				} else {
   235  					fmt.Printf("The command to remove '%s' was cancelled.\n", repoURL)
   236  				}
   237  			}
   238  		},
   239  	}
   240  	return command
   241  }
   242  
   243  // Print the repository credentials as table
   244  func printRepoCredsTable(repos []appsv1.RepoCreds) {
   245  	w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
   246  	fmt.Fprintf(w, "URL PATTERN\tUSERNAME\tSSH_CREDS\tTLS_CREDS\n")
   247  	for _, r := range repos {
   248  		if r.Username == "" {
   249  			r.Username = "-"
   250  		}
   251  		fmt.Fprintf(w, "%s\t%s\t%v\t%v\n", r.URL, r.Username, r.SSHPrivateKey != "", r.TLSClientCertData != "")
   252  	}
   253  	_ = w.Flush()
   254  }
   255  
   256  // Print list of repo urls or url patterns for repository credentials
   257  func printRepoCredsUrls(repos []appsv1.RepoCreds) {
   258  	for _, r := range repos {
   259  		fmt.Println(r.URL)
   260  	}
   261  }
   262  
   263  // NewRepoCredsListCommand returns a new instance of an `argocd repo list` command
   264  func NewRepoCredsListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
   265  	var output string
   266  	command := &cobra.Command{
   267  		Use:   "list",
   268  		Short: "List configured repository credentials",
   269  		Example: templates.Examples(`
   270  			# List all repo urls 
   271  			argocd repocreds list
   272  
   273  			# List all repo urls in json format
   274  			argocd repocreds list -o json
   275  
   276  			# List all repo urls in yaml format
   277  			argocd repocreds list -o yaml
   278  
   279  			# List all repo urls in url format
   280  			argocd repocreds list -o url
   281  		`),
   282  		Run: func(c *cobra.Command, _ []string) {
   283  			ctx := c.Context()
   284  
   285  			conn, repoIf := headless.NewClientOrDie(clientOpts, c).NewRepoCredsClientOrDie()
   286  			defer utilio.Close(conn)
   287  			repos, err := repoIf.ListRepositoryCredentials(ctx, &repocredspkg.RepoCredsQuery{})
   288  			errors.CheckError(err)
   289  			switch output {
   290  			case "yaml", "json":
   291  				err := PrintResourceList(repos.Items, output, false)
   292  				errors.CheckError(err)
   293  			case "url":
   294  				printRepoCredsUrls(repos.Items)
   295  			case "wide", "":
   296  				printRepoCredsTable(repos.Items)
   297  			default:
   298  				errors.CheckError(fmt.Errorf("unknown output format: %s", output))
   299  			}
   300  		},
   301  	}
   302  	command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide|url")
   303  	return command
   304  }