使用Go SDK发布AgroCD应用

ACK One GitOps是ACK One面向混合云、多云、多集群等场景,提供的多集群应用的GitOps持续交付能力,它通过托管开源Argo CD实现,完全兼容Argo CD API(如Application)。和开源ArgoCD相比,具有多集群分发能力、多用户权限管理、开箱即用免运维等优势。如果您需要在您的应用发布系统中集成ACK One GitOps,可以使用Go SDK来创建、删除或同步(Sync)ArgoCD Application。本文基于Argo CD v2.9.3版本,为您介绍如何使用Go SDK创建Application。

Demo代码配置

步骤一:获取目标集群的Server或Name

ArgoCD Application中需要指定目标集群的Server或者Name

  1. ACK One控制台获取Fleet实例的KubeConfig,并通过kubectl连接至Fleet实例。

    具体操作,请参见获取集群KubeConfig并通过kubectl工具连接集群

  2. 执行以下命令获取所有关联集群的Name列表或者Server列表。二者选择其中一个即可。Name具有更好的可读性,默认格式为<cluster id>-<cluster name>

    • 获取Name列表

      kubectl get secret -nargocd -l argocd.argoproj.io/secret-type=cluster |awk 'NR>1 {print $1}'|xargs -I {} sh -c 'kubectl get secret -nargocd {} -ojsonpath="{.data.name}"|base64 -d; echo'
    • 获取Server列表

      kubectl get secret -nargocd -l argocd.argoproj.io/secret-type=cluster |awk 'NR>1 {print $1}'|xargs -I {} sh -c 'kubectl get secret -nargocd {} -ojsonpath="{.data.server}"|base64 -d; echo'

步骤二:获取Token

在Fleet实例中创建一个单独的Local User,为其生成Token,用于管理Application的操作。

  1. 创建Local User。

    1. 执行以下命令,编辑ArgoCD argocd-cm的ConfigMap文件。

      kubectl edit cm argocd-cm -n argocd
    2. argocd-cm的ConfigMap文件中,添加如下所示的Local Userlocaluser1

      data:
        accounts.localuser1: login,apiKey    # 可以UI/CLI登录,并可以生成apiKey Token。
        accounts.localuser1.enabled: "true"  # 创建localuser1。
    3. 执行以下命令,查看Local User。

      argocd account list

      预期输出:

      NAME        ENABLED  CAPABILITIES
      admin       true     login
      localuser1  true     login,apiKey   # 此处为上一步已创建的localuser1。
  2. argocd-rbac-cm中为已创建的Local User添加ArgoCD的Kubernetes资源的RBAC权限,如设置admin管理员权限(application、cluster、project等资源都有读写权限)。

    ArgoCD RBAC权限详细内容,请参见为用户配置ArgoCD RBAC

    1. 执行以下命令,编辑ArgoCD argocd-rbac-cm的ConfigMap文件。

      kubectl edit cm argocd-rbac-cm -n argocd
    2. argocd-rbac-cm的ConfigMap文件中,参考以下示例为Local User进行授权。

      重要

      请勿修改此ConfigMap文件中已有的配置。

        data:
        policy.csv: |
          g, "14***01", role:admin          # 现有配置,请保留。
          g, localuser1, role:admin         # 新增配置,映射localuser1到角色admin。
        scopes: '[uid]'  
  3. 为Local User生成Token。

    通过ArgoCD CLI方式生成

    执行以下命令,设置密码并生成认证Token。

    # 设置密码。
    argocd account update-password \
      --account localuser1 \
      --current-password <admin password> \
      --new-password <localuser1-password>
    
    # 获取localuser1认证Token。
    argocd account generate-token --account localuser1
    eyJhb......

    通过ArgoCD UI生成

    1. 登录ACK One控制台,在左侧导航栏选择舰队 > GitOps

    2. Gitops页面左上角选择目标舰队集群,然后单击GitOps控制台,在弹出的登录页面输入用户名密码完成登录。

    3. 在ArgoCD控制台左侧导航栏选择Settings,然后选择Accounts,找到添加的Local User并单击用户名称。本文以localuser1为例。

    4. 在用户页面localuser1的Token区域中单击Generate New生成Token,并保存Token内容。

步骤三:操作Application示例代码

以下提供应用操作的示例代码,包括创建应用、同步应用、获取应用详情、更新应用、删除应用。

  • Main入口

    需要替换AddressToken,Token为步骤二:获取Token中获取的Token内容。

    展开查看示例代码

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	conn := &Connection{
                    //如argocd.xxxx.cn-hongkong.alicontainer.com
    		Address: "argocd.<your-fleet-id>.<region>.alicontainer.com",
    		Token:   "local user token",
    	}
    	argoCDClient, err := NewArgoCDClient(conn)
    	if err != nil {
    		panic(fmt.Sprintf("Failed to create argocd client: %v\n", err))
    	}
    
    	//if err := argoCDClient.CreateApplication(false, true); err != nil {
    	//	panic(fmt.Sprintf("Failed to create application: %v\n", err))
    	//}
    
    	if err := argoCDClient.SyncApplication("argocd", "demo-argocd-app-manual", "default"); err != nil {
    		panic(fmt.Sprintf("Failed to sync application: %v\n", err))
    	}
    }
  • 应用管理示例代码

    基于ArgoCD的apiclient实现。需要替换ClusterNameClusterServerGitRepoURL

    展开查看示例代码

    package main
    
    import (
    	"context"
    	"fmt"
    	"github.com/argoproj/argo-cd/v2/pkg/apiclient"
    	"github.com/argoproj/argo-cd/v2/pkg/apiclient/application"
    	"github.com/argoproj/argo-cd/v2/pkg/apiclient/cluster"
    	"github.com/argoproj/argo-cd/v2/pkg/apiclient/project"
    	argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
    	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    )
    
    const (
    	GitRepoURL = "https://github.com/AliyunContainerService/gitops-demo.git"
    
    	ClusterServer = "https://172.16.xx.xx:6443"
    	ClusterName   = "cc0e3a41fe49c4a7bbcd5a1f0xxxxx-test"
    )
    
    type ArgoCDClient struct {
    	projectClient project.ProjectServiceClient
    	clusterClient cluster.ClusterServiceClient
    	appClient     application.ApplicationServiceClient
    }
    
    type Connection struct {
    	Address string
    	Token   string
    }
    
    func NewArgoCDClient(conn *Connection) (*ArgoCDClient, error) {
    	argocdApiClient, err := apiclient.NewClient(&apiclient.ClientOptions{
    		ServerAddr: fmt.Sprintf(conn.Address),
    		Insecure:   true,
    		AuthToken:  conn.Token,
    	})
    	if err != nil {
    		return nil, err
    	}
    
    	_, appClient, err := argocdApiClient.NewApplicationClient()
    	if err != nil {
    		return nil, err
    	}
    
    	_, projectClient, err := argocdApiClient.NewProjectClient()
    	if err != nil {
    		return nil, err
    	}
    
    	_, clusterClient, err := argocdApiClient.NewClusterClient()
    	if err != nil {
    		return nil, err
    	}
    
    	return &ArgoCDClient{
    		projectClient: projectClient,
    		clusterClient: clusterClient,
    		appClient:     appClient,
    	}, nil
    }
    
    // CreateApplication 创建应用
    /**
     * 创建应用
     * upsert:Allows to override application with the same name even if supplied application spec is different from existing spec
     * validate: Validation of repo and cluster
     */
    func (c *ArgoCDClient) CreateApplication(upsert, validate bool) error {
    	app := &argoappv1.Application{
    		ObjectMeta: v1.ObjectMeta{
    			Name:      "demo-argocd-app-manual",
    			Namespace: "argocd",
    		},
    		Spec: argoappv1.ApplicationSpec{
    			Project: "default",
    			Source: &argoappv1.ApplicationSource{
    				RepoURL:        GitRepoURL,
    				Path:           "manifests/helm/echo-server",
    				TargetRevision: "HEAD",
    			},
    			Destination: argoappv1.ApplicationDestination{
    				//Server:    ClusterServer,
    				Name:      ClusterName,
    				Namespace: "echo-server-demo2",
    			},
    			SyncPolicy: &argoappv1.SyncPolicy{
    				SyncOptions: argoappv1.SyncOptions{
    					"CreateNamespace=true",
    				},
    			},
    		},
    	}
    
    	if _, err := c.appClient.Create(context.Background(), &application.ApplicationCreateRequest{
    		Application: app,
    		Upsert:      &upsert,
    		Validate:    &validate,
    	}); err != nil {
    		return err
    	}
    
    	return nil
    }
    
    // SyncApplication 同步应用
    // ApplicationSyncRequest中的参数可以参考`argocd app sync --help`
    func (c *ArgoCDClient) SyncApplication(namespace, name, project string) error {
    	if _, err := c.appClient.Sync(context.Background(), &application.ApplicationSyncRequest{
    		Name:         &name,
    		AppNamespace: &namespace,
    		Revision:     nil,
    		//DryRun:        nil,
    		//Prune:         nil,
    		//Strategy:      nil,
    		//Resources:     nil,
    		//Manifests:     nil,
    		//Infos:         nil,
    		//RetryStrategy: nil,
    		//SyncOptions: syncOptionsFactory,
    		Project: &project,
    	}); err != nil {
    		return err
    	}
    
    	return nil
    }
    
    // GetApplication 获取应用详情
    func (c *ArgoCDClient) GetApplication(namespace, name string) (*argoappv1.Application, error) {
    	if app, err := c.appClient.Get(context.Background(), &application.ApplicationQuery{
    		Name:         &name,
    		AppNamespace: &namespace,
    	}); err != nil {
    		return nil, err
    	} else {
    		return app, nil
    	}
    }
    
    // DeleteApplication 删除应用
    func (c *ArgoCDClient) DeleteApplication(namespace, name string) error {
    	if _, err := c.appClient.Delete(context.Background(), &application.ApplicationDeleteRequest{
    		Name:         &name,
    		AppNamespace: &namespace,
    	}); err != nil {
    		return err
    	}
    	return nil
    }
    
    // UpdateApplication 更新应用
    func (c *ArgoCDClient) UpdateApplication(newApp *argoappv1.Application, validate bool, project string) error {
    	if _, err := c.appClient.Update(context.Background(), &application.ApplicationUpdateRequest{
    		Application: newApp,
    		Validate:    &validate,
    		Project:     &project,
    	}); err != nil {
    		return err
    	}
    	return nil
    }
    

go.mod配置

Import ArgoCD go packages到您的Go项目时,在下载dependencies时,会遇到像"unknown revision v0.0.0"的错误,这是因为ArgoCD直接依赖于一些Kubernetes packages,这些packages的go.mod中包含这些未知的v0.0.0版本。

该问题可参考社区的Importing Argo CD go packages,通过增加replace来解决。但不同的ArgoCD版本对应的replace依赖的版本也不一样,需要将replace中的kubernetes packages的版本设置为和ArgoCD版本一致。

本文提供的Demo中使用的是ArgoCD v2.9.3版本的go packages,因此需要将replace中所有的kubernetes packages设置为和v2.9.3版本对应的v0.24.17版本。完整的go.mod示例如下:

展开查看示例代码

module awesomeProject1

go 1.19

require (
	github.com/argoproj/argo-cd/v2 v2.9.3
	k8s.io/apimachinery v0.24.17
	sigs.k8s.io/controller-runtime v0.11.0
)

require (
	cloud.google.com/go/compute v1.20.1 // indirect
	cloud.google.com/go/compute/metadata v0.2.3 // indirect
	dario.cat/mergo v1.0.0 // indirect
	github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
	github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd // indirect
	github.com/Masterminds/semver/v3 v3.2.1 // indirect
	github.com/Microsoft/go-winio v0.6.1 // indirect
	github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect
	github.com/argoproj/gitops-engine v0.7.1-0.20230906152414-b0fffe419a0f // indirect
	github.com/argoproj/pkg v0.13.7-0.20230626144333-d56162821bd1 // indirect
	github.com/bmatcuk/doublestar/v4 v4.6.0 // indirect
	github.com/bombsimon/logrusr/v2 v2.0.1 // indirect
	github.com/bradleyfalzon/ghinstallation/v2 v2.6.0 // indirect
	github.com/cespare/xxhash/v2 v2.2.0 // indirect
	github.com/chai2010/gettext-go v0.0.0-20170215093142-bf70f2a70fb1 // indirect
	github.com/cloudflare/circl v1.3.3 // indirect
	github.com/coreos/go-oidc/v3 v3.6.0 // indirect
	github.com/cyphar/filepath-securejoin v0.2.4 // indirect
	github.com/davecgh/go-spew v1.1.1 // indirect
	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
	github.com/docker/distribution v2.8.2+incompatible // indirect
	github.com/emicklei/go-restful/v3 v3.8.0 // indirect
	github.com/emirpasic/gods v1.18.1 // indirect
	github.com/evanphx/json-patch v5.6.0+incompatible // indirect
	github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
	github.com/fatih/camelcase v1.0.0 // indirect
	github.com/felixge/httpsnoop v1.0.3 // indirect
	github.com/fvbommel/sortorder v1.0.1 // indirect
	github.com/go-errors/errors v1.4.2 // indirect
	github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
	github.com/go-git/go-billy/v5 v5.5.0 // indirect
	github.com/go-git/go-git/v5 v5.11.0 // indirect
	github.com/go-jose/go-jose/v3 v3.0.0 // indirect
	github.com/go-logr/logr v1.2.4 // indirect
	github.com/go-logr/stdr v1.2.2 // indirect
	github.com/go-openapi/jsonpointer v0.19.5 // indirect
	github.com/go-openapi/jsonreference v0.20.0 // indirect
	github.com/go-openapi/swag v0.22.3 // indirect
	github.com/go-redis/cache/v9 v9.0.0 // indirect
	github.com/gobwas/glob v0.2.3 // indirect
	github.com/gogo/protobuf v1.3.2 // indirect
	github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
	github.com/golang/protobuf v1.5.3 // indirect
	github.com/google/btree v1.1.2 // indirect
	github.com/google/gnostic v0.6.9 // indirect
	github.com/google/go-cmp v0.6.0 // indirect
	github.com/google/go-github/v53 v53.2.0 // indirect
	github.com/google/go-querystring v1.1.0 // indirect
	github.com/google/gofuzz v1.2.0 // indirect
	github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
	github.com/google/uuid v1.3.0 // indirect
	github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
	github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
	github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
	github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
	github.com/hashicorp/go-retryablehttp v0.7.4 // indirect
	github.com/imdario/mergo v0.3.16 // indirect
	github.com/inconshreveable/mousetrap v1.1.0 // indirect
	github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
	github.com/jonboulle/clockwork v0.2.2 // indirect
	github.com/josharian/intern v1.0.0 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
	github.com/kevinburke/ssh_config v1.2.0 // indirect
	github.com/klauspost/compress v1.16.5 // indirect
	github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
	github.com/mailru/easyjson v0.7.7 // indirect
	github.com/mitchellh/go-wordwrap v1.0.0 // indirect
	github.com/moby/spdystream v0.2.0 // indirect
	github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.2 // indirect
	github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
	github.com/opencontainers/go-digest v1.0.0 // indirect
	github.com/opencontainers/image-spec v1.1.0-rc4 // indirect
	github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
	github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
	github.com/pjbgf/sha1cd v0.3.0 // indirect
	github.com/pkg/errors v0.9.1 // indirect
	github.com/r3labs/diff v1.1.0 // indirect
	github.com/redis/go-redis/v9 v9.0.5 // indirect
	github.com/robfig/cron/v3 v3.0.1 // indirect
	github.com/russross/blackfriday v1.6.0 // indirect
	github.com/sergi/go-diff v1.1.0 // indirect
	github.com/sirupsen/logrus v1.9.3 // indirect
	github.com/skeema/knownhosts v1.2.1 // indirect
	github.com/spf13/cobra v1.7.0 // indirect
	github.com/spf13/pflag v1.0.5 // indirect
	github.com/vmihailenco/go-tinylfu v0.2.2 // indirect
	github.com/vmihailenco/msgpack/v5 v5.3.4 // indirect
	github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
	github.com/xanzy/ssh-agent v0.3.3 // indirect
	github.com/xlab/treeprint v1.1.0 // indirect
	go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0 // indirect
	go.opentelemetry.io/otel v1.16.0 // indirect
	go.opentelemetry.io/otel/metric v1.16.0 // indirect
	go.opentelemetry.io/otel/trace v1.16.0 // indirect
	go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd // indirect
	golang.org/x/crypto v0.16.0 // indirect
	golang.org/x/mod v0.12.0 // indirect
	golang.org/x/net v0.19.0 // indirect
	golang.org/x/oauth2 v0.11.0 // indirect
	golang.org/x/sync v0.3.0 // indirect
	golang.org/x/sys v0.15.0 // indirect
	golang.org/x/term v0.15.0 // indirect
	golang.org/x/text v0.14.0 // indirect
	golang.org/x/time v0.3.0 // indirect
	golang.org/x/tools v0.13.0 // indirect
	google.golang.org/appengine v1.6.7 // indirect
	google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc // indirect
	google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect
	google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
	google.golang.org/grpc v1.56.2 // indirect
	google.golang.org/protobuf v1.31.0 // indirect
	gopkg.in/inf.v0 v0.9.1 // indirect
	gopkg.in/warnings.v0 v0.1.2 // indirect
	gopkg.in/yaml.v2 v2.4.0 // indirect
	gopkg.in/yaml.v3 v3.0.1 // indirect
	k8s.io/api v0.24.17 // indirect
	k8s.io/apiextensions-apiserver v0.24.2 // indirect
	k8s.io/apiserver v0.24.17 // indirect
	k8s.io/cli-runtime v0.24.17 // indirect
	k8s.io/client-go v0.24.17 // indirect
	k8s.io/component-base v0.24.17 // indirect
	k8s.io/component-helpers v0.24.17 // indirect
	k8s.io/klog/v2 v2.70.1 // indirect
	k8s.io/kube-aggregator v0.24.2 // indirect
	k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8 // indirect
	k8s.io/kubectl v0.24.2 // indirect
	k8s.io/kubernetes v1.24.17 // indirect
	k8s.io/utils v0.0.0-20220706174534-f6158b442e7c // indirect
	oras.land/oras-go/v2 v2.3.0 // indirect
	sigs.k8s.io/json v0.0.0-20220525155127-227cbc7cc124 // indirect
	sigs.k8s.io/kustomize/api v0.11.5 // indirect
	sigs.k8s.io/kustomize/kyaml v0.13.7 // indirect
	sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect
	sigs.k8s.io/yaml v1.3.0 // indirect
)

replace (
	// https://github.com/golang/go/issues/33546#issuecomment-519656923
	github.com/go-check/check => github.com/go-check/check v0.0.0-20180628173108-788fd7840127

	github.com/golang/protobuf => github.com/golang/protobuf v1.4.2
	github.com/gorilla/websocket => github.com/gorilla/websocket v1.4.2
	github.com/grpc-ecosystem/grpc-gateway => github.com/grpc-ecosystem/grpc-gateway v1.16.0
	github.com/improbable-eng/grpc-web => github.com/improbable-eng/grpc-web v0.0.0-20181111100011-16092bd1d58a

	// Avoid CVE-2022-28948
	gopkg.in/yaml.v3 => gopkg.in/yaml.v3 v3.0.1

	// https://github.com/kubernetes/kubernetes/issues/79384#issuecomment-505627280
	k8s.io/api => k8s.io/api v0.24.17
	k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.24.17
	k8s.io/apimachinery => k8s.io/apimachinery v0.24.1
	k8s.io/apiserver => k8s.io/apiserver v0.24.17
	k8s.io/cli-runtime => k8s.io/cli-runtime v0.24.17
	k8s.io/client-go => k8s.io/client-go v0.24.17
	k8s.io/cloud-provider => k8s.io/cloud-provider v0.24.17
	k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.24.17
	k8s.io/code-generator => k8s.io/code-generator v0.24.17
	k8s.io/component-base => k8s.io/component-base v0.24.17
	k8s.io/component-helpers => k8s.io/component-helpers v0.24.17
	k8s.io/controller-manager => k8s.io/controller-manager v0.24.17
	k8s.io/cri-api => k8s.io/cri-api v0.24.17
	k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.24.17
	k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.24.17
	k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.24.17
	k8s.io/kube-proxy => k8s.io/kube-proxy v0.24.17
	k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.24.17
	k8s.io/kubectl => k8s.io/kubectl v0.24.17
	k8s.io/kubelet => k8s.io/kubelet v0.24.17
	k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.24.17
	k8s.io/metrics => k8s.io/metrics v0.24.17
	k8s.io/mount-utils => k8s.io/mount-utils v0.24.17
	k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.24.17
	k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.24.17
)

相关文档