mirror of
https://github.com/hanzoai/deploy.git
synced 2026-10-07 08:44:50 +00:00
go.mod still declared `module github.com/hanzoai/deploy` after the repo was renamed. A Go module path is not cosmetic — it is the resolvable location. So `go get github.com/hanzoai/cd` could not work, and anything importing this module had to spell a name that no longer exists at that address. The build log of v3.6.0 is where it surfaced: the compiler was still emitting github.com/hanzoai/deploy/cmd/cd/commands. 3541 import paths across 690 .go, 19 .proto, 4 .yaml, 3 .sh, 2 .mod, Procfile and Makefile. Plus the 62 ghcr.io/hanzoai/deploy image references, moved to ghcr.io/hanzoai/cd to match hanzo.yml's build target — a distinct string from the module path, replaced separately so neither could clobber the other. go mod tidy clean, CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build ./... clean. Co-authored-by: Hanzo Dev <[email protected]>
201 lines
8.6 KiB
Go
201 lines
8.6 KiB
Go
package admin
|
|
|
|
import (
|
|
stderrors "errors"
|
|
"os"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
"github.com/spf13/cobra"
|
|
corev1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/client-go/kubernetes/fake"
|
|
|
|
cmdutil "github.com/hanzoai/cd/cmd/util"
|
|
"github.com/hanzoai/cd/common"
|
|
"github.com/hanzoai/cd/util/cli"
|
|
"github.com/hanzoai/cd/util/db"
|
|
"github.com/hanzoai/cd/util/errors"
|
|
"github.com/hanzoai/cd/util/git"
|
|
"github.com/hanzoai/cd/util/settings"
|
|
)
|
|
|
|
const (
|
|
ArgoCDNamespace = "cd"
|
|
repoSecretPrefix = "repo"
|
|
)
|
|
|
|
func NewRepoCommand() *cobra.Command {
|
|
command := &cobra.Command{
|
|
Use: "repo",
|
|
Short: "Manage repositories configuration",
|
|
Run: func(c *cobra.Command, args []string) {
|
|
c.HelpFunc()(c, args)
|
|
},
|
|
}
|
|
command.AddCommand(NewGenRepoSpecCommand())
|
|
|
|
return command
|
|
}
|
|
|
|
func NewGenRepoSpecCommand() *cobra.Command {
|
|
var (
|
|
repoOpts cmdutil.RepoOptions
|
|
outputFormat string
|
|
)
|
|
|
|
// For better readability and easier formatting
|
|
repoAddExamples := `
|
|
# Add a Git repository via SSH using a private key for authentication, ignoring the server's host key:
|
|
cd admin repo generate-spec [email protected]:repos/repo --insecure-ignore-host-key --ssh-private-key-path ~/id_rsa
|
|
|
|
# Add a Git repository via SSH on a non-default port - need to use ssh:// style URLs here
|
|
cd admin repo generate-spec ssh://[email protected]:2222/repos/repo --ssh-private-key-path ~/id_rsa
|
|
|
|
# Add a private Git repository via HTTPS using username/password and TLS client certificates:
|
|
cd admin repo generate-spec https://git.example.com/repos/repo --username git --password secret --tls-client-cert-path ~/mycert.crt --tls-client-cert-key-path ~/mycert.key
|
|
|
|
# Add a private Git BitBucket Data Center repository via HTTPS using bearer token:
|
|
cd admin repo generate-spec https://bitbucket.example.com/scm/proj/repo --bearer-token secret-token
|
|
|
|
# Add a private Git repository via HTTPS using username/password without verifying the server's TLS certificate
|
|
cd admin repo generate-spec https://git.example.com/repos/repo --username git --password secret --insecure-skip-server-verification
|
|
|
|
# Add a public Helm repository named 'stable' via HTTPS
|
|
cd admin repo generate-spec https://charts.helm.sh/stable --type helm --name stable
|
|
|
|
# Add a private Helm repository named 'stable' via HTTPS
|
|
cd admin repo generate-spec https://charts.helm.sh/stable --type helm --name stable --username test --password test
|
|
|
|
# Add a private Helm OCI-based repository named 'stable' via HTTPS
|
|
cd admin repo generate-spec helm-oci-registry.cn-zhangjiakou.cr.aliyuncs.com --type helm --name stable --enable-oci --username test --password test
|
|
|
|
# Add a private HTTPS OCI repository named 'stable'
|
|
cd admin repo generate-spec oci://helm-oci-registry.cn-zhangjiakou.cr.aliyuncs.com --type oci --name stable --username test --password test
|
|
|
|
# Add a private OCI repository named 'stable' without verifying the server's TLS certificate
|
|
cd admin repo generate-spec oci://helm-oci-registry.cn-zhangjiakou.cr.aliyuncs.com --type oci --name stable --username test --password test --insecure-skip-server-verification
|
|
|
|
# Add a private HTTP OCI repository named 'stable'
|
|
cd admin repo generate-spec oci://helm-oci-registry.cn-zhangjiakou.cr.aliyuncs.com --type oci --name stable --username test --password test --insecure-oci-force-http
|
|
|
|
# Add a private Git repository on GitHub.com via GitHub App. github-app-installation-id is optional, if not provided, the installation id will be fetched from the GitHub API.
|
|
cd admin repo generate-spec https://git.example.com/repos/repo --github-app-id 1 --github-app-installation-id 2 --github-app-private-key-path test.private-key.pem
|
|
|
|
# Add a private Git repository on GitHub Enterprise via GitHub App. github-app-installation-id is optional, if not provided, the installation id will be fetched from the GitHub API.
|
|
cd admin repo generate-spec https://ghe.example.com/repos/repo --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
|
|
|
|
# Add a private Git repository on Google Cloud Sources via GCP service account credentials
|
|
cd admin repo generate-spec https://source.developers.google.com/p/my-google-cloud-project/r/my-repo --gcp-service-account-key-path service-account-key.json
|
|
`
|
|
|
|
command := &cobra.Command{
|
|
Use: "generate-spec REPOURL",
|
|
Short: "Generate declarative config for a repo",
|
|
Example: repoAddExamples,
|
|
Run: func(c *cobra.Command, args []string) {
|
|
ctx := c.Context()
|
|
|
|
log.SetLevel(log.WarnLevel)
|
|
if len(args) != 1 {
|
|
c.HelpFunc()(c, args)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Repository URL
|
|
repoOpts.Repo.Repo = args[0]
|
|
|
|
// Specifying ssh-private-key-path is only valid for SSH repositories
|
|
if repoOpts.SshPrivateKeyPath != "" {
|
|
if ok, _ := git.IsSSHURL(repoOpts.Repo.Repo); ok {
|
|
keyData, err := os.ReadFile(repoOpts.SshPrivateKeyPath)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
repoOpts.Repo.SSHPrivateKey = string(keyData)
|
|
} else {
|
|
err := stderrors.New("--ssh-private-key-path is only supported for SSH repositories")
|
|
errors.CheckError(err)
|
|
}
|
|
}
|
|
|
|
// tls-client-cert-path and tls-client-cert-key-key-path must always be
|
|
// specified together
|
|
if (repoOpts.TlsClientCertPath != "" && repoOpts.TlsClientCertKeyPath == "") || (repoOpts.TlsClientCertPath == "" && repoOpts.TlsClientCertKeyPath != "") {
|
|
err := stderrors.New("--tls-client-cert-path and --tls-client-cert-key-path must be specified together")
|
|
errors.CheckError(err)
|
|
}
|
|
|
|
// Specifying tls-client-cert-path is only valid for HTTPS repositories
|
|
if repoOpts.TlsClientCertPath != "" {
|
|
if git.IsHTTPSURL(repoOpts.Repo.Repo) {
|
|
tlsCertData, err := os.ReadFile(repoOpts.TlsClientCertPath)
|
|
errors.CheckError(err)
|
|
tlsCertKey, err := os.ReadFile(repoOpts.TlsClientCertKeyPath)
|
|
errors.CheckError(err)
|
|
repoOpts.Repo.TLSClientCertData = string(tlsCertData)
|
|
repoOpts.Repo.TLSClientCertKey = string(tlsCertKey)
|
|
} else {
|
|
err := stderrors.New("--tls-client-cert-path is only supported for HTTPS repositories")
|
|
errors.CheckError(err)
|
|
}
|
|
}
|
|
|
|
// Set repository connection properties only when creating repository, not
|
|
// when creating repository credentials.
|
|
// InsecureIgnoreHostKey is deprecated and only here for backwards compat
|
|
repoOpts.Repo.InsecureIgnoreHostKey = repoOpts.InsecureIgnoreHostKey
|
|
repoOpts.Repo.Insecure = repoOpts.InsecureSkipServerVerification
|
|
repoOpts.Repo.EnableLFS = repoOpts.EnableLfs
|
|
repoOpts.Repo.EnableOCI = repoOpts.EnableOci
|
|
repoOpts.Repo.UseAzureWorkloadIdentity = repoOpts.UseAzureWorkloadIdentity
|
|
repoOpts.Repo.InsecureOCIForceHttp = repoOpts.InsecureOCIForceHTTP
|
|
repoOpts.Repo.WebhookManifestCacheWarmDisabled = repoOpts.WebhookManifestCacheWarmDisabled
|
|
|
|
if repoOpts.Repo.Type == "helm" && repoOpts.Repo.Name == "" {
|
|
errors.CheckError(stderrors.New("must specify --name for repos of type 'helm'"))
|
|
}
|
|
|
|
// If the user set a username, but didn't supply password via --password,
|
|
// then we prompt for it
|
|
if repoOpts.Repo.Username != "" && repoOpts.Repo.Password == "" {
|
|
repoOpts.Repo.Password = cli.PromptPassword(repoOpts.Repo.Password)
|
|
}
|
|
|
|
err := cmdutil.ValidateBearerTokenAndPasswordCombo(repoOpts.Repo.BearerToken, repoOpts.Repo.Password)
|
|
errors.CheckError(err)
|
|
err = cmdutil.ValidateBearerTokenForHTTPSRepoOnly(repoOpts.Repo.BearerToken, git.IsHTTPSURL(repoOpts.Repo.Repo))
|
|
errors.CheckError(err)
|
|
err = cmdutil.ValidateBearerTokenForGitOnly(repoOpts.Repo.BearerToken, repoOpts.Repo.Type)
|
|
errors.CheckError(err)
|
|
|
|
argoCDCM := &corev1.ConfigMap{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "ConfigMap",
|
|
APIVersion: "v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: common.ArgoCDConfigMapName,
|
|
Namespace: ArgoCDNamespace,
|
|
Labels: map[string]string{
|
|
"app.kubernetes.io/part-of": "hanzocd",
|
|
},
|
|
},
|
|
}
|
|
kubeClientset := fake.NewClientset(argoCDCM)
|
|
settingsMgr := settings.NewSettingsManager(ctx, kubeClientset, ArgoCDNamespace)
|
|
argoDB := db.NewDB(ArgoCDNamespace, settingsMgr, kubeClientset)
|
|
|
|
_, err = argoDB.CreateRepository(ctx, &repoOpts.Repo)
|
|
errors.CheckError(err)
|
|
|
|
secret, err := kubeClientset.CoreV1().Secrets(ArgoCDNamespace).Get(ctx, db.RepoURLToSecretName(repoSecretPrefix, repoOpts.Repo.Repo, repoOpts.Repo.Project), metav1.GetOptions{})
|
|
errors.CheckError(err)
|
|
|
|
errors.CheckError(PrintResources(outputFormat, os.Stdout, secret))
|
|
},
|
|
}
|
|
command.Flags().StringVarP(&outputFormat, "output", "o", "yaml", "Output format. One of: json|yaml")
|
|
cmdutil.AddRepoFlags(command, &repoOpts)
|
|
return command
|
|
}
|