mirror of
https://github.com/fluxcd/flux2.git
synced 2025-10-29 15:28:04 +08:00
Implement --deploy-token-auth in GitLab bootstrapping
This change set implements support for the `--deploy-token-auth` option in the `flux bootstrap gitlab` command. That option will reconcile a GitLab Project Deploy Token to use for the authentication of the GitLab git repository. A GitLab Project Deploy Token can be used the same way as a Personal Access Token which is already supported via `--token-auth`. The difference with the GitLab Project Deploy Token is that the token is managed (created, updated, deleted) by Flux and not provided by the user. This change is transparent to the source-controller. A prerequisite for this change is the `fluxcd/go-git-providers` change here: * https://github.com/fluxcd/go-git-providers/pull/191 See related discussion here: https://github.com/fluxcd/flux2/discussions/3595 GitLab Issue here: https://gitlab.com/gitlab-org/gitlab/-/issues/392605 Signed-off-by: Timo Furrer <tuxtimo@gmail.com>
This commit is contained in:
committed by
Hidde Beydals
parent
91d1e1df48
commit
2e1721ca85
@ -65,7 +65,11 @@ the bootstrap command will perform an upgrade if needed.`,
|
|||||||
flux bootstrap gitlab --owner=<group> --repository=<repository name> --hostname=<domain> --token-auth
|
flux bootstrap gitlab --owner=<group> --repository=<repository name> --hostname=<domain> --token-auth
|
||||||
|
|
||||||
# Run bootstrap for a an existing repository with a branch named main
|
# Run bootstrap for a an existing repository with a branch named main
|
||||||
flux bootstrap gitlab --owner=<organization> --repository=<repository name> --branch=main --token-auth`,
|
flux bootstrap gitlab --owner=<organization> --repository=<repository name> --branch=main --token-auth
|
||||||
|
|
||||||
|
# Run bootstrap for a private repository using Deploy Token authentication
|
||||||
|
flux bootstrap gitlab --owner=<group> --repository=<repository name> --deploy-token-auth
|
||||||
|
`,
|
||||||
RunE: bootstrapGitLabCmdRun,
|
RunE: bootstrapGitLabCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,6 +91,7 @@ type gitlabFlags struct {
|
|||||||
teams []string
|
teams []string
|
||||||
readWriteKey bool
|
readWriteKey bool
|
||||||
reconcile bool
|
reconcile bool
|
||||||
|
deployTokenAuth bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var gitlabArgs gitlabFlags
|
var gitlabArgs gitlabFlags
|
||||||
@ -102,6 +107,7 @@ func init() {
|
|||||||
bootstrapGitLabCmd.Flags().Var(&gitlabArgs.path, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path")
|
bootstrapGitLabCmd.Flags().Var(&gitlabArgs.path, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path")
|
||||||
bootstrapGitLabCmd.Flags().BoolVar(&gitlabArgs.readWriteKey, "read-write-key", false, "if true, the deploy key is configured with read/write permissions")
|
bootstrapGitLabCmd.Flags().BoolVar(&gitlabArgs.readWriteKey, "read-write-key", false, "if true, the deploy key is configured with read/write permissions")
|
||||||
bootstrapGitLabCmd.Flags().BoolVar(&gitlabArgs.reconcile, "reconcile", false, "if true, the configured options are also reconciled if the repository already exists")
|
bootstrapGitLabCmd.Flags().BoolVar(&gitlabArgs.reconcile, "reconcile", false, "if true, the configured options are also reconciled if the repository already exists")
|
||||||
|
bootstrapGitLabCmd.Flags().BoolVar(&gitlabArgs.deployTokenAuth, "deploy-token-auth", false, "when enabled, a Project Deploy Token is generated and will be used instead of the SSH deploy token")
|
||||||
|
|
||||||
bootstrapCmd.AddCommand(bootstrapGitLabCmd)
|
bootstrapCmd.AddCommand(bootstrapGitLabCmd)
|
||||||
}
|
}
|
||||||
@ -123,6 +129,10 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if bootstrapArgs.tokenAuth && gitlabArgs.deployTokenAuth {
|
||||||
|
return fmt.Errorf("--token-auth and --deploy-token-auth cannot be set both.")
|
||||||
|
}
|
||||||
|
|
||||||
if err := bootstrapValidate(); err != nil {
|
if err := bootstrapValidate(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -225,6 +235,9 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
secretOpts.Username = "git"
|
secretOpts.Username = "git"
|
||||||
secretOpts.Password = glToken
|
secretOpts.Password = glToken
|
||||||
secretOpts.CAFile = caBundle
|
secretOpts.CAFile = caBundle
|
||||||
|
} else if gitlabArgs.deployTokenAuth {
|
||||||
|
// the actual deploy token will be reconciled later
|
||||||
|
secretOpts.CAFile = caBundle
|
||||||
} else {
|
} else {
|
||||||
keypair, err := sourcesecret.LoadKeyPairFromPath(bootstrapArgs.privateKeyFile, gitArgs.password)
|
keypair, err := sourcesecret.LoadKeyPairFromPath(bootstrapArgs.privateKeyFile, gitArgs.password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -274,9 +287,12 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
if bootstrapArgs.sshHostname != "" {
|
if bootstrapArgs.sshHostname != "" {
|
||||||
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname))
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname))
|
||||||
}
|
}
|
||||||
if bootstrapArgs.tokenAuth {
|
if bootstrapArgs.tokenAuth || gitlabArgs.deployTokenAuth {
|
||||||
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSyncTransportType("https"))
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSyncTransportType("https"))
|
||||||
}
|
}
|
||||||
|
if gitlabArgs.deployTokenAuth {
|
||||||
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithDeployTokenAuth())
|
||||||
|
}
|
||||||
if !gitlabArgs.private {
|
if !gitlabArgs.private {
|
||||||
bootstrapOpts = append(bootstrapOpts, bootstrap.WithProviderRepositoryConfig("", "", "public"))
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithProviderRepositoryConfig("", "", "public"))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -59,6 +59,8 @@ type GitProviderBootstrapper struct {
|
|||||||
|
|
||||||
sshHostname string
|
sshHostname string
|
||||||
|
|
||||||
|
useDeployTokenAuth bool
|
||||||
|
|
||||||
provider gitprovider.Client
|
provider gitprovider.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,6 +186,16 @@ func (o reconcileOption) applyGitProvider(b *GitProviderBootstrapper) {
|
|||||||
b.reconcile = true
|
b.reconcile = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithDeployTokenAuth() GitProviderOption {
|
||||||
|
return deployTokenAuthOption(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
type deployTokenAuthOption bool
|
||||||
|
|
||||||
|
func (o deployTokenAuthOption) applyGitProvider(b *GitProviderBootstrapper) {
|
||||||
|
b.useDeployTokenAuth = true
|
||||||
|
}
|
||||||
|
|
||||||
func (b *GitProviderBootstrapper) ReconcileSyncConfig(ctx context.Context, options sync.Options) error {
|
func (b *GitProviderBootstrapper) ReconcileSyncConfig(ctx context.Context, options sync.Options) error {
|
||||||
if b.repository == nil {
|
if b.repository == nil {
|
||||||
return errors.New("repository is required")
|
return errors.New("repository is required")
|
||||||
@ -208,6 +220,26 @@ func (b *GitProviderBootstrapper) ReconcileSyncConfig(ctx context.Context, optio
|
|||||||
return b.PlainGitBootstrapper.ReconcileSyncConfig(ctx, options)
|
return b.PlainGitBootstrapper.ReconcileSyncConfig(ctx, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *GitProviderBootstrapper) ReconcileSourceSecret(ctx context.Context, options sourcesecret.Options) error {
|
||||||
|
if b.repository == nil {
|
||||||
|
return errors.New("repository is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.useDeployTokenAuth {
|
||||||
|
deployTokenInfo, err := b.reconcileDeployToken(ctx, options)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if deployTokenInfo != nil {
|
||||||
|
options.Username = deployTokenInfo.Username
|
||||||
|
options.Password = deployTokenInfo.Token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.PlainGitBootstrapper.ReconcileSourceSecret(ctx, options)
|
||||||
|
}
|
||||||
|
|
||||||
// ReconcileRepository reconciles an organization or user repository with the
|
// ReconcileRepository reconciles an organization or user repository with the
|
||||||
// GitProviderBootstrapper configuration. On success, the URL in the embedded
|
// GitProviderBootstrapper configuration. On success, the URL in the embedded
|
||||||
// PlainGitBootstrapper is set to clone URL for the configured protocol.
|
// PlainGitBootstrapper is set to clone URL for the configured protocol.
|
||||||
@ -261,6 +293,32 @@ func (b *GitProviderBootstrapper) reconcileDeployKey(ctx context.Context, secret
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *GitProviderBootstrapper) reconcileDeployToken(ctx context.Context, options sourcesecret.Options) (*gitprovider.DeployTokenInfo, error) {
|
||||||
|
dts, err := b.repository.DeployTokens()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
b.logger.Actionf("checking to reconcile deploy token for source secret")
|
||||||
|
name := deployTokenName(options.Namespace, b.branch, options.Name, options.TargetPath)
|
||||||
|
deployTokenInfo := gitprovider.DeployTokenInfo{Name: name}
|
||||||
|
|
||||||
|
deployToken, changed, err := dts.Reconcile(ctx, deployTokenInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if changed {
|
||||||
|
b.logger.Successf("configured deploy token %q for %q", deployTokenInfo.Name, b.repository.Repository().String())
|
||||||
|
deployTokenInfo := deployToken.Get()
|
||||||
|
return &deployTokenInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
b.logger.Successf("reconciled deploy token for source secret")
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// reconcileOrgRepository reconciles a gitprovider.OrgRepository
|
// reconcileOrgRepository reconciles a gitprovider.OrgRepository
|
||||||
// with the GitProviderBootstrapper values, including any
|
// with the GitProviderBootstrapper values, including any
|
||||||
// gitprovider.TeamAccessInfo configurations.
|
// gitprovider.TeamAccessInfo configurations.
|
||||||
@ -554,6 +612,17 @@ func deployKeyName(namespace, secretName, branch, path string) string {
|
|||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func deployTokenName(namespace, secretName, branch, path string) string {
|
||||||
|
var elems []string
|
||||||
|
for _, v := range []string{namespace, secretName, branch, path} {
|
||||||
|
if v == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
elems = append(elems, v)
|
||||||
|
}
|
||||||
|
return strings.Join(elems, "-")
|
||||||
|
}
|
||||||
|
|
||||||
// setHostname is a helper to replace the hostname of the given URL.
|
// setHostname is a helper to replace the hostname of the given URL.
|
||||||
// TODO(hidde): support for this should be added in go-git-providers.
|
// TODO(hidde): support for this should be added in go-git-providers.
|
||||||
func setHostname(URL, hostname string) (string, error) {
|
func setHostname(URL, hostname string) (string, error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user