Skip to content

Commit

Permalink
feat: Headless Argo CD (aka GitOps Agent) (argoproj#6361)
Browse files Browse the repository at this point in the history
* feat: add --headless flag to Argo CD CLI command

Signed-off-by: Alexander Matyushentsev <[email protected]>

* docs: add headless installation manifests and documentation

Signed-off-by: Alexander Matyushentsev <[email protected]>

* Apply reviewer notes

Signed-off-by: Alexander Matyushentsev <[email protected]>

* Remove port forwarding logs

Signed-off-by: Alexander Matyushentsev <[email protected]>
  • Loading branch information
Alexander Matyushentsev authored Jul 21, 2021
1 parent d7a8a87 commit 561452a
Show file tree
Hide file tree
Showing 143 changed files with 3,036 additions and 308 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
.idea/
.DS_Store
vendor/
dist/
dist/*
ui/dist/app/*
!ui/dist/app/gitkeep
site/
*.iml
# delve debug binaries
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ RUN go mod download

# Perform the build
COPY . .
COPY --from=argocd-ui ./src/dist/app ./src/dist/app
RUN make argocd-all

ARG BUILD_ALL_CLIS=true
Expand All @@ -126,7 +127,6 @@ RUN if [ "$BUILD_ALL_CLIS" = "true" ] ; then \
####################################################################################################
FROM argocd-base
COPY --from=argocd-build /go/src/github.com/argoproj/argo-cd/dist/argocd* /usr/local/bin/
COPY --from=argocd-ui ./src/dist/app /shared/app

USER root
RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-util
Expand Down
16 changes: 1 addition & 15 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ IMAGE_PREFIX=${IMAGE_NAMESPACE}/
endif

.PHONY: all
all: cli image argocd-util
all: cli image

# We have some legacy requirements for being checked out within $GOPATH.
# The ensure-gopath target can be used as dependency to ensure we are running
Expand Down Expand Up @@ -212,10 +212,6 @@ cli: test-tools-image
cli-local: clean-debug
CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${CLI_NAME} ./cmd

.PHONY: cli-argocd
cli-argocd:
go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${CLI_NAME} ./cmd

.PHONY: release-cli
release-cli: clean-debug image
docker create --name tmp-argocd-linux $(IMAGE_PREFIX)argocd:$(IMAGE_TAG)
Expand All @@ -224,16 +220,6 @@ release-cli: clean-debug image
docker cp tmp-argocd-linux:/usr/local/bin/argocd-windows-amd64.exe ${DIST_DIR}/argocd-windows-amd64.exe
docker rm tmp-argocd-linux

.PHONY: argocd-util
argocd-util: clean-debug
# Build argocd-util as a statically linked binary, so it could run within the alpine-based dex container (argoproj/argo-cd#844)
CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${UTIL_CLI_NAME} ./cmd

# .PHONY: dev-tools-image
# dev-tools-image:
# docker build -t $(DEV_TOOLS_PREFIX)$(DEV_TOOLS_IMAGE) . -f hack/Dockerfile.dev-tools
# docker tag $(DEV_TOOLS_PREFIX)$(DEV_TOOLS_IMAGE) $(DEV_TOOLS_PREFIX)$(DEV_TOOLS_IMAGE):$(DEV_TOOLS_VERSION)

.PHONY: test-tools-image
test-tools-image:
docker build --build-arg UID=$(shell id -u) -t $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE) -f test/container/Dockerfile .
Expand Down
2 changes: 1 addition & 1 deletion Procfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
controller: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-application-controller go run ./cmd/main.go --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081}"
api-server: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-server go run ./cmd/main.go --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} --staticassets ui/dist/app"
api-server: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-server go run ./cmd/main.go --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} "
dex: sh -c "ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/v2/cmd gendexcfg -o `pwd`/dist/dex.yaml && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:v2.27.0 serve /dex.yaml"
redis: bash -c "if [ $ARGOCD_REDIS_LOCAL == 'true' ]; then redis-server --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; else docker run --rm --name argocd-redis -i -p ${ARGOCD_E2E_REDIS_PORT:-6379}:${ARGOCD_E2E_REDIS_PORT:-6379} redis:6.2.4-alpine --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; fi"
repo-server: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_GNUPGHOME=${ARGOCD_GNUPGHOME:-/tmp/argocd-local/gpg/keys} ARGOCD_GPG_DATA_PATH=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-repo-server go run ./cmd/main.go --loglevel debug --port ${ARGOCD_E2E_REPOSERVER_PORT:-8081} --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379}"
Expand Down
4 changes: 1 addition & 3 deletions cmd/argocd-server/commands/argocd_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ func NewCommand() *cobra.Command {
glogLevel int
clientConfig clientcmd.ClientConfig
repoServerTimeoutSeconds int
staticAssetsDir string
baseHRef string
rootPath string
repoServerAddress string
Expand Down Expand Up @@ -128,7 +127,6 @@ func NewCommand() *cobra.Command {
ListenPort: listenPort,
MetricsPort: metricsPort,
Namespace: namespace,
StaticAssetsDir: staticAssetsDir,
BaseHRef: baseHRef,
RootPath: rootPath,
KubeClientset: kubeclientset,
Expand Down Expand Up @@ -159,7 +157,7 @@ func NewCommand() *cobra.Command {

clientConfig = cli.AddKubectlFlagsToCmd(command)
command.Flags().BoolVar(&insecure, "insecure", env.ParseBoolFromEnv("ARGOCD_SERVER_INSECURE", false), "Run server without TLS")
command.Flags().StringVar(&staticAssetsDir, "staticassets", "", "Static assets directory path")
_ = command.Flags().MarkDeprecated("staticassets", "The --staticassets flag is not longer supported. Static assets are embedded into binary.")
command.Flags().StringVar(&baseHRef, "basehref", env.StringFromEnv("ARGOCD_SERVER_BASEHREF", "/"), "Value for base href in index.html. Used if Argo CD is running behind reverse proxy under subpath different from /")
command.Flags().StringVar(&rootPath, "rootpath", env.StringFromEnv("ARGOCD_SERVER_ROOTPATH", ""), "Used if Argo CD is running behind reverse proxy under subpath different from /")
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", env.StringFromEnv("ARGOCD_SERVER_LOGFORMAT", "text"), "Set the logging format. One of: text|json")
Expand Down
2 changes: 1 addition & 1 deletion cmd/argocd-util/commands/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ func NewReconcileCommand() *cobra.Command {
if repoServerAddress == "" {
printLine("Repo server is not provided, trying to port-forward to argocd-repo-server pod.")
overrides := clientcmd.ConfigOverrides{}
repoServerPort, err := kubeutil.PortForward("app.kubernetes.io/name=argocd-repo-server", 8081, namespace, &overrides)
repoServerPort, err := kubeutil.PortForward(8081, namespace, &overrides, "app.kubernetes.io/name=argocd-repo-server")
errors.CheckError(err)
repoServerAddress = fmt.Sprintf("localhost:%d", repoServerPort)
}
Expand Down
3 changes: 2 additions & 1 deletion cmd/argocd-util/commands/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ func loadClusters(kubeClient *kubernetes.Clientset, appClient *versioned.Clients
var cache *appstatecache.Cache
if portForwardRedis {
overrides := clientcmd.ConfigOverrides{}
port, err := kubeutil.PortForward("app.kubernetes.io/name=argocd-redis-ha-haproxy", 6379, namespace, &overrides)
port, err := kubeutil.PortForward(6379, namespace, &overrides,
"app.kubernetes.io/name=argocd-redis-ha-haproxy", "app.kubernetes.io/name=argocd-redis")
if err != nil {
return nil, err
}
Expand Down
42 changes: 42 additions & 0 deletions cmd/argocd/commands/admin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package commands

import (
"context"
"fmt"

"github.com/spf13/cobra"

"github.com/argoproj/argo-cd/v2/cmd/argocd/commands/headless"
"github.com/argoproj/argo-cd/v2/common"
"github.com/argoproj/argo-cd/v2/pkg/apiclient"
)

func NewAdminCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "admin",
Short: "Contains a set of commands useful for Argo CD administrators and requires direct Kubernetes access",
Run: func(c *cobra.Command, args []string) {
c.HelpFunc()(c, args)
},
}
cmd.AddCommand(NewDashboardCommand())
return cmd
}

func NewDashboardCommand() *cobra.Command {
var (
port int
)
cmd := &cobra.Command{
Use: "dashboard",
Short: "Starts Argo CD Web UI locally",
Run: func(cmd *cobra.Command, args []string) {
println(fmt.Sprintf("Argo CD UI is available at http://localhost:%d", port))
<-context.Background().Done()
},
}
clientOpts := &apiclient.ClientOptions{Headless: true}
headless.InitCommand(cmd, clientOpts, &port)
cmd.Flags().IntVar(&port, "port", common.DefaultPortAPIServer, "Listen on given port")
return cmd
}
97 changes: 97 additions & 0 deletions cmd/argocd/commands/headless/forward.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package headless

import (
"context"
"fmt"
"sync"
"time"

"github.com/go-redis/redis/v8"
"k8s.io/client-go/tools/clientcmd"

"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
repoapiclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient"
"github.com/argoproj/argo-cd/v2/util/cache"
"github.com/argoproj/argo-cd/v2/util/io"
kubeutil "github.com/argoproj/argo-cd/v2/util/kube"
)

type forwardCacheClient struct {
namespace string
init sync.Once
client cache.CacheClient
err error
}

func (c *forwardCacheClient) doLazy(action func(client cache.CacheClient) error) error {
c.init.Do(func() {
overrides := clientcmd.ConfigOverrides{}
redisPort, err := kubeutil.PortForward(6379, c.namespace, &overrides,
"app.kubernetes.io/name=argocd-redis-ha-haproxy", "app.kubernetes.io/name=argocd-redis")
if err != nil {
c.err = err
return
}

redisClient := redis.NewClient(&redis.Options{Addr: fmt.Sprintf("localhost:%d", redisPort)})
c.client = cache.NewRedisCache(redisClient, time.Hour)
})
if c.err != nil {
return c.err
}
return action(c.client)
}

func (c *forwardCacheClient) Set(item *cache.Item) error {
return c.doLazy(func(client cache.CacheClient) error {
return client.Set(item)
})
}

func (c *forwardCacheClient) Get(key string, obj interface{}) error {
return c.doLazy(func(client cache.CacheClient) error {
return client.Get(key, obj)
})
}

func (c *forwardCacheClient) Delete(key string) error {
return c.doLazy(func(client cache.CacheClient) error {
return client.Delete(key)
})
}

func (c *forwardCacheClient) OnUpdated(ctx context.Context, key string, callback func() error) error {
return c.doLazy(func(client cache.CacheClient) error {
return client.OnUpdated(ctx, key, callback)
})
}

func (c *forwardCacheClient) NotifyUpdated(key string) error {
return c.doLazy(func(client cache.CacheClient) error {
return client.NotifyUpdated(key)
})
}

type forwardRepoClientset struct {
namespace string
init sync.Once
repoClientset repoapiclient.Clientset
err error
}

func (c *forwardRepoClientset) NewRepoServerClient() (io.Closer, repoapiclient.RepoServerServiceClient, error) {
c.init.Do(func() {
overrides := clientcmd.ConfigOverrides{}
repoServerPort, err := kubeutil.PortForward(8081, c.namespace, &overrides, "app.kubernetes.io/name=argocd-repo-server")
if err != nil {
c.err = err
return
}
c.repoClientset = apiclient.NewRepoServerClientset(fmt.Sprintf("localhost:%d", repoServerPort), 60, apiclient.TLSConfiguration{
DisableTLS: false, StrictValidation: false})
})
if c.err != nil {
return nil, nil, c.err
}
return c.repoClientset.NewRepoServerClient()
}
Loading

0 comments on commit 561452a

Please sign in to comment.