mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-03-23 16:58:47 +01:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13558b7ce8 | ||
|
|
3b2b3dacf5 | ||
|
|
1b2f89995c | ||
|
|
0479fcdf82 | ||
|
|
a04465466d | ||
|
|
670921df90 | ||
|
|
18f7e17d7a | ||
|
|
a2aede0441 |
@@ -40,9 +40,9 @@ RUN cd ${GOPATH}/src/dummy && \
|
||||
rmdir vendor
|
||||
|
||||
# Perform the build
|
||||
ARG MAKE_TARGET
|
||||
WORKDIR /root/go/src/github.com/argoproj/argo-cd
|
||||
COPY . .
|
||||
ARG MAKE_TARGET="cli server controller repo-server argocd-util"
|
||||
RUN make ${MAKE_TARGET}
|
||||
|
||||
|
||||
@@ -72,13 +72,12 @@ FROM debian:9.3
|
||||
RUN apt-get update && apt-get install -y git && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
ARG BINARY
|
||||
COPY --from=builder /root/go/src/github.com/argoproj/argo-cd/dist/${BINARY} /${BINARY}
|
||||
|
||||
COPY --from=cli-tooling /ks /usr/local/bin/ks
|
||||
COPY --from=cli-tooling /kubectl /usr/local/bin/kubectl
|
||||
|
||||
# workaround ksonnet issue https://github.com/ksonnet/ksonnet/issues/298
|
||||
ENV USER=root
|
||||
|
||||
ENV BINARY=$BINARY
|
||||
CMD /$BINARY
|
||||
COPY --from=builder /root/go/src/github.com/argoproj/argo-cd/dist/* /
|
||||
ARG BINARY
|
||||
CMD /${BINARY}
|
||||
|
||||
35
Gopkg.lock
generated
35
Gopkg.lock
generated
@@ -32,6 +32,18 @@
|
||||
revision = "2ee87856327ba09384cabd113bc6b5d174e9ec0f"
|
||||
version = "v3.5.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/coreos/dex"
|
||||
packages = ["api"]
|
||||
revision = "218d671a96865df2a4cf7f310efb99b8bfc5a5e2"
|
||||
version = "v2.10.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "v2"
|
||||
name = "github.com/coreos/go-oidc"
|
||||
packages = ["."]
|
||||
revision = "1180514eaf4d9f38d0d19eef639a1d695e066e72"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/daaku/go.zipexe"
|
||||
@@ -328,6 +340,15 @@
|
||||
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/pquerna/cachecontrol"
|
||||
packages = [
|
||||
".",
|
||||
"cacheobject"
|
||||
]
|
||||
revision = "525d0eb5f91d30e3b1548de401b7ef9ea6898520"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/sergi/go-diff"
|
||||
packages = ["diffmatchpatch"]
|
||||
@@ -402,6 +423,8 @@
|
||||
packages = [
|
||||
"bcrypt",
|
||||
"blowfish",
|
||||
"ed25519",
|
||||
"ed25519/internal/edwards25519",
|
||||
"ssh/terminal"
|
||||
]
|
||||
revision = "432090b8f568c018896cd8a0fb0345872bbac6ce"
|
||||
@@ -542,6 +565,16 @@
|
||||
revision = "3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4"
|
||||
version = "v0.9.0"
|
||||
|
||||
[[projects]]
|
||||
name = "gopkg.in/square/go-jose.v2"
|
||||
packages = [
|
||||
".",
|
||||
"cipher",
|
||||
"json"
|
||||
]
|
||||
revision = "76dd09796242edb5b897103a75df2645c028c960"
|
||||
version = "v2.1.6"
|
||||
|
||||
[[projects]]
|
||||
name = "gopkg.in/yaml.v2"
|
||||
packages = ["."]
|
||||
@@ -765,6 +798,6 @@
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "8e7142f84554c6f1665ef18e0fb906f82de8cd802b0211c4a46ec1ad228b8b7e"
|
||||
inputs-digest = "562f3dc64b35c3db83ab1e05f0aba9bbe52d5dfda905394b88ca22b8746a2cc5"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
||||
14
Makefile
14
Makefile
@@ -41,7 +41,7 @@ IMAGE_PREFIX=${IMAGE_NAMESPACE}/
|
||||
endif
|
||||
|
||||
.PHONY: all
|
||||
all: cli server-image controller-image repo-server-image
|
||||
all: cli server-image controller-image repo-server-image argocd-util
|
||||
|
||||
.PHONY: protogen
|
||||
protogen:
|
||||
@@ -74,13 +74,17 @@ cli-darwin:
|
||||
docker cp tmp-argocd-darwin:/root/go/src/github.com/argoproj/argo-cd/dist/argocd-darwin-amd64 dist/
|
||||
docker rm tmp-argocd-darwin
|
||||
|
||||
.PHONY: argocd-util
|
||||
argocd-util:
|
||||
CGO_ENABLED=0 go build -v -i -ldflags '${LDFLAGS} -extldflags "-static"' -o ${DIST_DIR}/argocd-util ./cmd/argocd-util
|
||||
|
||||
.PHONY: server
|
||||
server:
|
||||
CGO_ENABLED=0 go build -v -i -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-server ./cmd/argocd-server
|
||||
|
||||
.PHONY: server-image
|
||||
server-image:
|
||||
docker build --build-arg BINARY=argocd-server --build-arg MAKE_TARGET=server -t $(IMAGE_PREFIX)argocd-server:$(IMAGE_TAG) -f Dockerfile-argocd .
|
||||
docker build --build-arg BINARY=argocd-server -t $(IMAGE_PREFIX)argocd-server:$(IMAGE_TAG) -f Dockerfile-argocd .
|
||||
@if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)argocd-server:$(IMAGE_TAG) ; fi
|
||||
|
||||
.PHONY: repo-server
|
||||
@@ -89,7 +93,7 @@ repo-server:
|
||||
|
||||
.PHONY: repo-server-image
|
||||
repo-server-image:
|
||||
docker build --build-arg BINARY=argocd-repo-server --build-arg MAKE_TARGET=repo-server -t $(IMAGE_PREFIX)argocd-repo-server:$(IMAGE_TAG) -f Dockerfile-argocd .
|
||||
docker build --build-arg BINARY=argocd-repo-server -t $(IMAGE_PREFIX)argocd-repo-server:$(IMAGE_TAG) -f Dockerfile-argocd .
|
||||
@if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)argocd-repo-server:$(IMAGE_TAG) ; fi
|
||||
|
||||
.PHONY: controller
|
||||
@@ -98,12 +102,12 @@ controller:
|
||||
|
||||
.PHONY: controller-image
|
||||
controller-image:
|
||||
docker build --build-arg BINARY=argocd-application-controller --build-arg MAKE_TARGET=controller -t $(IMAGE_PREFIX)argocd-application-controller:$(IMAGE_TAG) -f Dockerfile-argocd .
|
||||
docker build --build-arg BINARY=argocd-application-controller -t $(IMAGE_PREFIX)argocd-application-controller:$(IMAGE_TAG) -f Dockerfile-argocd .
|
||||
@if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)argocd-application-controller:$(IMAGE_TAG) ; fi
|
||||
|
||||
.PHONY: cli-image
|
||||
cli-image:
|
||||
docker build --build-arg BINARY=argocd --build-arg MAKE_TARGET=cli -t $(IMAGE_PREFIX)argocd-cli:$(IMAGE_TAG) -f Dockerfile-argocd .
|
||||
docker build --build-arg BINARY=argocd -t $(IMAGE_PREFIX)argocd-cli:$(IMAGE_TAG) -f Dockerfile-argocd .
|
||||
@if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)argocd-cli:$(IMAGE_TAG) ; fi
|
||||
|
||||
.PHONY: builder-image
|
||||
|
||||
8
OWNERS
Normal file
8
OWNERS
Normal file
@@ -0,0 +1,8 @@
|
||||
owners:
|
||||
- alexmt
|
||||
- jessesuen
|
||||
|
||||
approvers:
|
||||
- alexmt
|
||||
- jessesuen
|
||||
- merenbach
|
||||
3
Procfile
3
Procfile
@@ -1,3 +1,4 @@
|
||||
controller: go run ./cmd/argocd-application-controller/main.go --app-resync 10
|
||||
api-server: go run ./cmd/argocd-server/main.go --insecure
|
||||
api-server: go run ./cmd/argocd-server/main.go --insecure --disable-auth
|
||||
repo-server: go run ./cmd/argocd-repo-server/main.go
|
||||
dex: sh -c "go run ./cmd/argocd-util/main.go gendexcfg -o `pwd`/dist/dex.yaml && docker run --rm -p 5556:5556 -p 5557:5557 -v `pwd`/dist/dex.yaml:/dex.yaml quay.io/coreos/dex:v2.10.0 serve /dex.yaml"
|
||||
127
cmd/argocd-util/main.go
Normal file
127
cmd/argocd-util/main.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/dex"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
// load the gcp plugin (required to authenticate against GKE clusters).
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
||||
// load the oidc plugin (required to authenticate with OpenID Connect).
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
|
||||
)
|
||||
|
||||
const (
|
||||
// CLIName is the name of the CLI
|
||||
cliName = "argocd-util"
|
||||
)
|
||||
|
||||
// NewCommand returns a new instance of an argocd command
|
||||
func NewCommand() *cobra.Command {
|
||||
var (
|
||||
logLevel string
|
||||
)
|
||||
|
||||
var command = &cobra.Command{
|
||||
Use: cliName,
|
||||
Short: "argocd-util has internal tools used by ArgoCD",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
c.HelpFunc()(c, args)
|
||||
},
|
||||
}
|
||||
|
||||
command.AddCommand(cli.NewVersionCmd(cliName))
|
||||
command.AddCommand(NewRunDexCommand())
|
||||
command.AddCommand(NewGenDexConfigCommand())
|
||||
|
||||
command.Flags().StringVar(&logLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
|
||||
return command
|
||||
}
|
||||
|
||||
func NewRunDexCommand() *cobra.Command {
|
||||
var (
|
||||
clientConfig clientcmd.ClientConfig
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: "rundex",
|
||||
Short: "Runs dex generating a config using settings from the ArgoCD configmap and secret",
|
||||
RunE: func(c *cobra.Command, args []string) error {
|
||||
dexPath, err := exec.LookPath("dex")
|
||||
errors.CheckError(err)
|
||||
dexCfgBytes, err := genDexConfig(clientConfig)
|
||||
errors.CheckError(err)
|
||||
if len(dexCfgBytes) == 0 {
|
||||
log.Infof("dex is not configured")
|
||||
// need to sleep forever since we run as a sidecar and kubernetes does not permit
|
||||
// containers in a deployment to have restartPolicy anything other than Always.
|
||||
// TODO: we should watch for a change in the dex.config key in the config-map
|
||||
// to restart dex when there is a change (e.g. clientID and clientSecretKey changed)
|
||||
select {}
|
||||
}
|
||||
err = ioutil.WriteFile("/tmp/dex.yaml", dexCfgBytes, 0644)
|
||||
errors.CheckError(err)
|
||||
log.Info(string(dexCfgBytes))
|
||||
return syscall.Exec(dexPath, []string{"dex", "serve", "/tmp/dex.yaml"}, []string{})
|
||||
},
|
||||
}
|
||||
|
||||
clientConfig = cli.AddKubectlFlagsToCmd(&command)
|
||||
return &command
|
||||
}
|
||||
|
||||
func NewGenDexConfigCommand() *cobra.Command {
|
||||
var (
|
||||
clientConfig clientcmd.ClientConfig
|
||||
out string
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: "gendexcfg",
|
||||
Short: "Generates a dex config from ArgoCD settings",
|
||||
RunE: func(c *cobra.Command, args []string) error {
|
||||
dexCfgBytes, err := genDexConfig(clientConfig)
|
||||
errors.CheckError(err)
|
||||
if len(dexCfgBytes) == 0 {
|
||||
log.Infof("dex is not configured")
|
||||
return nil
|
||||
}
|
||||
if out == "" {
|
||||
fmt.Printf(string(dexCfgBytes))
|
||||
} else {
|
||||
err = ioutil.WriteFile(out, dexCfgBytes, 0644)
|
||||
errors.CheckError(err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
clientConfig = cli.AddKubectlFlagsToCmd(&command)
|
||||
command.Flags().StringVarP(&out, "out", "o", "", "Output to the specified file instead of stdout")
|
||||
return &command
|
||||
}
|
||||
|
||||
func genDexConfig(clientConfig clientcmd.ClientConfig) ([]byte, error) {
|
||||
config, err := clientConfig.ClientConfig()
|
||||
errors.CheckError(err)
|
||||
namespace, _, err := clientConfig.Namespace()
|
||||
errors.CheckError(err)
|
||||
|
||||
kubeClient := kubernetes.NewForConfigOrDie(config)
|
||||
return dex.GenerateDexConfigYAML(kubeClient, namespace)
|
||||
}
|
||||
|
||||
func main() {
|
||||
if err := NewCommand().Execute(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -217,7 +217,10 @@ func NewApplicationSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com
|
||||
os.Exit(1)
|
||||
}
|
||||
setParameterOverrides(app, appOpts.parameters)
|
||||
_, err = appIf.Update(context.Background(), app)
|
||||
_, err = appIf.UpdateSpec(context.Background(), &application.ApplicationSpecRequest{
|
||||
AppName: app.Name,
|
||||
Spec: &app.Spec,
|
||||
})
|
||||
errors.CheckError(err)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -14,6 +14,9 @@ const (
|
||||
|
||||
// SecretTypeCluster indicates a secret type of cluster
|
||||
SecretTypeCluster = "cluster"
|
||||
|
||||
// AuthCookieName is the HTTP cookie name where we store our auth token
|
||||
AuthCookieName = "argocd.argoproj.io/auth-token"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/selection"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
@@ -222,26 +223,26 @@ func (ctrl *ApplicationController) processNextItem() bool {
|
||||
if isForceRefreshed || app.NeedRefreshAppStatus(ctrl.statusRefreshTimeout) {
|
||||
log.Infof("Refreshing application '%s' status (force refreshed: %v)", app.Name, isForceRefreshed)
|
||||
|
||||
status, err := ctrl.tryRefreshAppStatus(app.DeepCopy())
|
||||
comparisonResult, parameters, err := ctrl.tryRefreshAppStatus(app.DeepCopy())
|
||||
if err != nil {
|
||||
status = app.Status.DeepCopy()
|
||||
status.ComparisonResult = appv1.ComparisonResult{
|
||||
comparisonResult = &appv1.ComparisonResult{
|
||||
Status: appv1.ComparisonStatusError,
|
||||
Error: fmt.Sprintf("Failed to get application status for application '%s': %v", app.Name, err),
|
||||
ComparedTo: app.Spec.Source,
|
||||
ComparedAt: metav1.Time{Time: time.Now().UTC()},
|
||||
}
|
||||
parameters = nil
|
||||
}
|
||||
ctrl.updateAppStatus(app.Name, app.Namespace, status)
|
||||
ctrl.updateAppStatus(app.Name, app.Namespace, comparisonResult, parameters)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (ctrl *ApplicationController) tryRefreshAppStatus(app *appv1.Application) (*appv1.ApplicationStatus, error) {
|
||||
func (ctrl *ApplicationController) tryRefreshAppStatus(app *appv1.Application) (*appv1.ComparisonResult, *[]appv1.ComponentParameter, error) {
|
||||
conn, client, err := ctrl.repoClientset.NewRepositoryClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
defer util.Close(conn)
|
||||
repo, err := ctrl.apiRepoService.Get(context.Background(), &apireposerver.RepoQuery{Repo: app.Spec.Source.RepoURL})
|
||||
@@ -271,14 +272,14 @@ func (ctrl *ApplicationController) tryRefreshAppStatus(app *appv1.Application) (
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("Failed to load application manifest %v", err)
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
targetObjs := make([]*unstructured.Unstructured, len(manifestInfo.Manifests))
|
||||
for i, manifestStr := range manifestInfo.Manifests {
|
||||
var obj unstructured.Unstructured
|
||||
if err := json.Unmarshal([]byte(manifestStr), &obj); err != nil {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
targetObjs[i] = &obj
|
||||
@@ -287,11 +288,10 @@ func (ctrl *ApplicationController) tryRefreshAppStatus(app *appv1.Application) (
|
||||
server, namespace := argoutil.ResolveServerNamespace(app.Spec.Destination, manifestInfo)
|
||||
comparisonResult, err := ctrl.appComparator.CompareAppState(server, namespace, targetObjs, app)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
log.Infof("App %s comparison result: prev: %s. current: %s", app.Name, app.Status.ComparisonResult.Status, comparisonResult.Status)
|
||||
newStatus := app.Status
|
||||
newStatus.ComparisonResult = *comparisonResult
|
||||
|
||||
paramsReq := repository.EnvParamsRequest{
|
||||
Repo: repo,
|
||||
Revision: revision,
|
||||
@@ -300,13 +300,13 @@ func (ctrl *ApplicationController) tryRefreshAppStatus(app *appv1.Application) (
|
||||
}
|
||||
params, err := client.GetEnvParams(context.Background(), ¶msReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
newStatus.Parameters = make([]appv1.ComponentParameter, len(params.Params))
|
||||
parameters := make([]appv1.ComponentParameter, len(params.Params))
|
||||
for i := range params.Params {
|
||||
newStatus.Parameters[i] = *params.Params[i]
|
||||
parameters[i] = *params.Params[i]
|
||||
}
|
||||
return &newStatus, nil
|
||||
return comparisonResult, ¶meters, nil
|
||||
}
|
||||
|
||||
func (ctrl *ApplicationController) runWorker() {
|
||||
@@ -314,23 +314,22 @@ func (ctrl *ApplicationController) runWorker() {
|
||||
}
|
||||
}
|
||||
|
||||
func (ctrl *ApplicationController) updateAppStatus(appName string, namespace string, status *appv1.ApplicationStatus) {
|
||||
appKey := fmt.Sprintf("%s/%s", namespace, appName)
|
||||
obj, exists, err := ctrl.appInformer.GetIndexer().GetByKey(appKey)
|
||||
func (ctrl *ApplicationController) updateAppStatus(appName string, namespace string, comparisonResult *appv1.ComparisonResult, parameters *[]appv1.ComponentParameter) {
|
||||
statusPatch := make(map[string]interface{})
|
||||
statusPatch["comparisonResult"] = comparisonResult
|
||||
statusPatch["parameters"] = parameters
|
||||
patch, err := json.Marshal(map[string]interface{}{
|
||||
"status": statusPatch,
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
appClient := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(namespace)
|
||||
_, err = appClient.Patch(appName, types.MergePatchType, patch)
|
||||
}
|
||||
if err != nil {
|
||||
log.Warnf("Failed to get application '%s' from informer index: %+v", appKey, err)
|
||||
log.Warnf("Error updating application: %v", err)
|
||||
} else {
|
||||
if exists {
|
||||
app := obj.(*appv1.Application).DeepCopy()
|
||||
app.Status = *status
|
||||
appClient := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(namespace)
|
||||
_, err := appClient.Update(app)
|
||||
if err != nil {
|
||||
log.Warnf("Error updating application: %v", err)
|
||||
} else {
|
||||
log.Info("Application update successful")
|
||||
}
|
||||
}
|
||||
log.Info("Application update successful")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,11 +7,11 @@ import (
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
"github.com/argoproj/argo-cd/util/config"
|
||||
"github.com/argoproj/argo-cd/util/diff"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
"github.com/argoproj/argo-cd/util/password"
|
||||
"github.com/argoproj/argo-cd/util/session"
|
||||
"github.com/argoproj/argo-cd/util/settings"
|
||||
tlsutil "github.com/argoproj/argo-cd/util/tls"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/gobuffalo/packr"
|
||||
@@ -131,8 +131,8 @@ func (i *Installer) InstallApplicationCRD() {
|
||||
func (i *Installer) InstallSettings() {
|
||||
kubeclientset, err := kubernetes.NewForConfig(i.config)
|
||||
errors.CheckError(err)
|
||||
configManager := config.NewConfigManager(kubeclientset, i.Namespace)
|
||||
_, err = configManager.GetSettings()
|
||||
settingsMgr := settings.NewSettingsManager(kubeclientset, i.Namespace)
|
||||
_, err = settingsMgr.GetSettings()
|
||||
if err == nil {
|
||||
log.Infof("Settings already exists. Skipping creation")
|
||||
return
|
||||
@@ -141,7 +141,7 @@ func (i *Installer) InstallSettings() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// configmap/secret not yet created
|
||||
var newSettings config.ArgoCDSettings
|
||||
var newSettings settings.ArgoCDSettings
|
||||
|
||||
// set JWT signature
|
||||
signature, err := session.MakeSignature(32)
|
||||
@@ -173,7 +173,7 @@ func (i *Installer) InstallSettings() {
|
||||
errors.CheckError(err)
|
||||
newSettings.Certificate = cert
|
||||
|
||||
err = configManager.SaveSettings(&newSettings)
|
||||
err = settingsMgr.SaveSettings(&newSettings)
|
||||
errors.CheckError(err)
|
||||
}
|
||||
|
||||
@@ -222,8 +222,10 @@ func (i *Installer) InstallArgoCDServer() {
|
||||
i.unmarshalManifest("04c_argocd-server-rolebinding.yaml", &argoCDServerControllerRoleBinding)
|
||||
i.unmarshalManifest("04d_argocd-server-deployment.yaml", &argoCDServerControllerDeployment)
|
||||
i.unmarshalManifest("04e_argocd-server-service.yaml", &argoCDServerService)
|
||||
argoCDServerControllerDeployment.Spec.Template.Spec.InitContainers[0].Image = i.UIImage
|
||||
argoCDServerControllerDeployment.Spec.Template.Spec.InitContainers[0].Image = i.ServerImage
|
||||
argoCDServerControllerDeployment.Spec.Template.Spec.InitContainers[0].ImagePullPolicy = apiv1.PullPolicy(i.ImagePullPolicy)
|
||||
argoCDServerControllerDeployment.Spec.Template.Spec.InitContainers[1].Image = i.UIImage
|
||||
argoCDServerControllerDeployment.Spec.Template.Spec.InitContainers[1].ImagePullPolicy = apiv1.PullPolicy(i.ImagePullPolicy)
|
||||
argoCDServerControllerDeployment.Spec.Template.Spec.Containers[0].Image = i.ServerImage
|
||||
argoCDServerControllerDeployment.Spec.Template.Spec.Containers[0].ImagePullPolicy = apiv1.PullPolicy(i.ImagePullPolicy)
|
||||
i.MustInstallResource(kube.MustToUnstructured(&argoCDServerServiceAccount))
|
||||
|
||||
@@ -1,7 +1,57 @@
|
||||
# NOTE: the values here are just a example and are not the values used during an install.
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: argocd-cm
|
||||
namespace: argocd
|
||||
# TODO: future argocd tuning keys go here (e.g. resync period)
|
||||
data: {}
|
||||
data:
|
||||
# url is the externally facing base URL of ArgoCD.
|
||||
# This field is required when configuring SSO, which ArgoCD uses as part the redirectURI for the
|
||||
# dex connectors. When configuring the application in the SSO provider (e.g. github, okta), the
|
||||
# authorization callback URL will be url + /api/dex/callback. For example, if ArgoCD's url is
|
||||
# https://example.com, then the auth callback to set in the SSO provider should be:
|
||||
# https://example.com/api/dex/callback
|
||||
url: http://localhost:8080
|
||||
|
||||
# dex.config holds the contents of the configuration yaml for the dex OIDC/Oauth2 provider sidecar.
|
||||
# Only a subset of a full dex config is required, namely the connectors list. ArgoCD will generate
|
||||
# the complete dex config based on the configured URL, and the known callback endpoint which the
|
||||
# ArgoCD API server exposes (i.e. /api/dex/callback).
|
||||
dex.config: |
|
||||
# connectors is a list of dex connector configurations. For details on available connectors and
|
||||
# how to configure them, see: https://github.com/coreos/dex/tree/master/Documentation/connectors
|
||||
# NOTE:
|
||||
# * Any values which start with '$' will look to a key in argocd-secret of the same name, to
|
||||
# obtain the actual value.
|
||||
# * ArgoCD will automatically set the 'redirectURI' field in any OAuth2 connectors, to match the
|
||||
# external callback URL (e.g. https://example.com/api/dex/callback)
|
||||
connectors:
|
||||
# GitHub example
|
||||
- type: github
|
||||
id: github
|
||||
name: GitHub
|
||||
config:
|
||||
clientID: $github.clientID
|
||||
clientSecret: $github.clientSecret
|
||||
orgs:
|
||||
- name: your-github-org
|
||||
|
||||
# GitHub enterprise example
|
||||
- type: github
|
||||
id: acme-github
|
||||
name: Acme GitHub
|
||||
config:
|
||||
hostName: github.acme.com
|
||||
clientID: $acme.clientID
|
||||
clientSecret: $acme.clientSecret
|
||||
orgs:
|
||||
- name: your-github-org
|
||||
|
||||
# OIDC example (e.g. Okta)
|
||||
- type: oidc
|
||||
id: okta
|
||||
name: Okta
|
||||
config:
|
||||
issuer: https://dev-123456.oktapreview.com
|
||||
clientID: $okta.clientID
|
||||
clientSecret: $okta.clientSecret
|
||||
|
||||
@@ -6,8 +6,19 @@ metadata:
|
||||
name: argocd-secret
|
||||
namespace: argocd
|
||||
type: Opaque
|
||||
data:
|
||||
# bcrypt hash of 'password'
|
||||
admin.password: JDJhJDEwJGVYYkZmOEt3NUMzTDJVbE9FRDNqUU9QMC5reVNBamVLUXY0N3NqaFFpWlZwTkkyU2dMTzd1
|
||||
stringData:
|
||||
# bcrypt hash of the string "password"
|
||||
admin.password: $2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W
|
||||
# random server signature key for session validation
|
||||
server.secretkey: aEDvv73vv70F77+9CRBSNu+/vTYQ77+9EUFh77+9LzFyJ++/vXfLsO+/vWRbeu+/ve+/vQ==
|
||||
|
||||
# the following of user defined keys which are referenced in the example argocd-cm configmap
|
||||
# as pat of SSO configuration.
|
||||
github.clientID: aabbccddeeff00112233
|
||||
github.clientSecret: nv1vx8w4gw5byrflujfkxww6ykh85yq818aorvwy
|
||||
|
||||
acme.clientID: abcdefghijklmnopqrst
|
||||
acme.clientSecret: 5pp7dyre3d5nyk0ree1tr0gd68k18xn94x8lfae9
|
||||
|
||||
okta.clientID: aaaabbbbccccddddeee
|
||||
okta.clientSecret: x41ztv6ufyf07oyoopc6f62p222c00mox2ciquvt
|
||||
|
||||
@@ -14,16 +14,28 @@ spec:
|
||||
spec:
|
||||
serviceAccountName: argocd-server
|
||||
initContainers:
|
||||
- command: [cp, -r, /app, /shared]
|
||||
- name: copyutil
|
||||
image: argoproj/argocd-server:latest
|
||||
command: [cp, /argocd-util, /shared]
|
||||
volumeMounts:
|
||||
- mountPath: /shared
|
||||
name: static-files
|
||||
- name: ui
|
||||
image: argoproj/argocd-ui:latest
|
||||
name: argocd-server-ui
|
||||
command: [cp, -r, /app, /shared]
|
||||
volumeMounts:
|
||||
- mountPath: /shared
|
||||
name: static-files
|
||||
containers:
|
||||
- command: [/argocd-server, --staticassets, /shared/app, --repo-server, 'argocd-repo-server:8081']
|
||||
- name: argocd-server
|
||||
image: argoproj/argocd-server:latest
|
||||
name: argocd-server
|
||||
command: [/argocd-server, --staticassets, /shared/app, --repo-server, 'argocd-repo-server:8081']
|
||||
volumeMounts:
|
||||
- mountPath: /shared
|
||||
name: static-files
|
||||
- name: dex
|
||||
image: quay.io/coreos/dex:v2.10.0
|
||||
command: [/shared/argocd-util, rundex]
|
||||
volumeMounts:
|
||||
- mountPath: /shared
|
||||
name: static-files
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
MetaDataTokenKey = "token"
|
||||
// EnvArgoCDServer is the environment variable to look for an ArgoCD server address
|
||||
EnvArgoCDServer = "ARGOCD_SERVER"
|
||||
// EnvArgoCDAuthToken is the environment variable to look for an ArgoCD auth token
|
||||
@@ -138,7 +139,8 @@ func NewClientOrDie(opts *ClientOptions) ServerClient {
|
||||
return client
|
||||
}
|
||||
|
||||
// JwtCredentials holds a token for authentication.
|
||||
// JwtCredentials implements the gRPC credentials.Credentials interface which we is used to do
|
||||
// grpc.WithPerRPCCredentials(), for authentication
|
||||
type jwtCredentials struct {
|
||||
Token string
|
||||
}
|
||||
@@ -149,7 +151,8 @@ func (c jwtCredentials) RequireTransportSecurity() bool {
|
||||
|
||||
func (c jwtCredentials) GetRequestMetadata(context.Context, ...string) (map[string]string, error) {
|
||||
return map[string]string{
|
||||
"tokens": c.Token,
|
||||
MetaDataTokenKey: c.Token,
|
||||
"tokens": c.Token, // legacy key. delete eventually
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ func (s *Server) List(ctx context.Context, q *ApplicationQuery) (*appv1.Applicat
|
||||
|
||||
// Create creates an application
|
||||
func (s *Server) Create(ctx context.Context, a *appv1.Application) (*appv1.Application, error) {
|
||||
err := s.validateApp(ctx, a)
|
||||
err := s.validateApp(ctx, &a.Spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -89,13 +89,29 @@ func (s *Server) Get(ctx context.Context, q *ApplicationQuery) (*appv1.Applicati
|
||||
|
||||
// Update updates an application
|
||||
func (s *Server) Update(ctx context.Context, a *appv1.Application) (*appv1.Application, error) {
|
||||
err := s.validateApp(ctx, a)
|
||||
err := s.validateApp(ctx, &a.Spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Update(a)
|
||||
}
|
||||
|
||||
// UpdateSpec updates an application spec
|
||||
func (s *Server) UpdateSpec(ctx context.Context, q *ApplicationSpecRequest) (*appv1.ApplicationSpec, error) {
|
||||
err := s.validateApp(ctx, q.Spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
patch, err := json.Marshal(map[string]appv1.ApplicationSpec{
|
||||
"spec": *q.Spec,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Patch(q.AppName, types.MergePatchType, patch)
|
||||
return q.Spec, err
|
||||
}
|
||||
|
||||
// Delete removes an application and all associated resources
|
||||
func (s *Server) Delete(ctx context.Context, q *DeleteApplicationRequest) (*ApplicationResponse, error) {
|
||||
var err error
|
||||
@@ -164,20 +180,20 @@ func (s *Server) Watch(q *ApplicationQuery, ws ApplicationService_WatchServer) e
|
||||
// * the git path contains a valid app.yaml
|
||||
// * the specified environment exists
|
||||
// * the referenced cluster has been added to ArgoCD
|
||||
func (s *Server) validateApp(ctx context.Context, a *appv1.Application) error {
|
||||
func (s *Server) validateApp(ctx context.Context, spec *appv1.ApplicationSpec) error {
|
||||
// Test the repo
|
||||
conn, repoClient, err := s.repoClientset.NewRepositoryClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer util.Close(conn)
|
||||
repoRes, err := s.repoService.Get(ctx, &apirepository.RepoQuery{Repo: a.Spec.Source.RepoURL})
|
||||
repoRes, err := s.repoService.Get(ctx, &apirepository.RepoQuery{Repo: spec.Source.RepoURL})
|
||||
if err != nil {
|
||||
if errStatus, ok := status.FromError(err); ok && errStatus.Code() == codes.NotFound {
|
||||
// The repo has not been added to ArgoCD so we do not have credentials to access it.
|
||||
// We support the mode where apps can be created from public repositories. Test the
|
||||
// repo to make sure it is publically accessible
|
||||
err = git.TestRepo(a.Spec.Source.RepoURL, "", "", "")
|
||||
// repo to make sure it is publicly accessible
|
||||
err = git.TestRepo(spec.Source.RepoURL, "", "", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -189,10 +205,10 @@ func (s *Server) validateApp(ctx context.Context, a *appv1.Application) error {
|
||||
// Verify app.yaml is functional
|
||||
req := repository.KsonnetAppRequest{
|
||||
Repo: &appv1.Repository{
|
||||
Repo: a.Spec.Source.RepoURL,
|
||||
Repo: spec.Source.RepoURL,
|
||||
},
|
||||
Revision: a.Spec.Source.TargetRevision,
|
||||
Path: a.Spec.Source.Path,
|
||||
Revision: spec.Source.TargetRevision,
|
||||
Path: spec.Source.Path,
|
||||
}
|
||||
if repoRes != nil {
|
||||
req.Repo.Username = repoRes.Username
|
||||
@@ -205,15 +221,15 @@ func (s *Server) validateApp(ctx context.Context, a *appv1.Application) error {
|
||||
}
|
||||
|
||||
// Verify the specified environment is defined in it
|
||||
envSpec, ok := ksAppRes.Environments[a.Spec.Source.Environment]
|
||||
envSpec, ok := ksAppRes.Environments[spec.Source.Environment]
|
||||
if !ok {
|
||||
return status.Errorf(codes.InvalidArgument, "environment '%s' does not exist in app", a.Spec.Source.Environment)
|
||||
return status.Errorf(codes.InvalidArgument, "environment '%s' does not exist in app", spec.Source.Environment)
|
||||
}
|
||||
// Ensure the k8s cluster the app is referencing, is configured in ArgoCD
|
||||
// NOTE: need to check if it was overridden in the destination spec
|
||||
clusterURL := envSpec.Destination.Server
|
||||
if a.Spec.Destination != nil && a.Spec.Destination.Server != "" {
|
||||
clusterURL = a.Spec.Destination.Server
|
||||
if spec.Destination != nil && spec.Destination.Server != "" {
|
||||
clusterURL = spec.Destination.Server
|
||||
}
|
||||
_, err = s.clusterService.Get(ctx, &cluster.ClusterQuery{Server: clusterURL})
|
||||
if err != nil {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
ApplicationResponse
|
||||
DeleteApplicationRequest
|
||||
ApplicationSyncRequest
|
||||
ApplicationSpecRequest
|
||||
ApplicationSyncResult
|
||||
ApplicationRollbackRequest
|
||||
ResourceDetails
|
||||
@@ -160,6 +161,33 @@ func (m *ApplicationSyncRequest) GetPrune() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// ApplicationSpecRequest is a request to update application spec
|
||||
type ApplicationSpecRequest struct {
|
||||
AppName string `protobuf:"bytes,1,opt,name=appName,proto3" json:"appName,omitempty"`
|
||||
Spec *github_com_argoproj_argo_cd_pkg_apis_application_v1alpha1.ApplicationSpec `protobuf:"bytes,2,opt,name=spec" json:"spec,omitempty"`
|
||||
}
|
||||
|
||||
func (m *ApplicationSpecRequest) Reset() { *m = ApplicationSpecRequest{} }
|
||||
func (m *ApplicationSpecRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*ApplicationSpecRequest) ProtoMessage() {}
|
||||
func (*ApplicationSpecRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptorApplication, []int{4}
|
||||
}
|
||||
|
||||
func (m *ApplicationSpecRequest) GetAppName() string {
|
||||
if m != nil {
|
||||
return m.AppName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *ApplicationSpecRequest) GetSpec() *github_com_argoproj_argo_cd_pkg_apis_application_v1alpha1.ApplicationSpec {
|
||||
if m != nil {
|
||||
return m.Spec
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ApplicationSyncResult is a result of a sync requeswt
|
||||
type ApplicationSyncResult struct {
|
||||
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
|
||||
@@ -169,7 +197,7 @@ type ApplicationSyncResult struct {
|
||||
func (m *ApplicationSyncResult) Reset() { *m = ApplicationSyncResult{} }
|
||||
func (m *ApplicationSyncResult) String() string { return proto.CompactTextString(m) }
|
||||
func (*ApplicationSyncResult) ProtoMessage() {}
|
||||
func (*ApplicationSyncResult) Descriptor() ([]byte, []int) { return fileDescriptorApplication, []int{4} }
|
||||
func (*ApplicationSyncResult) Descriptor() ([]byte, []int) { return fileDescriptorApplication, []int{5} }
|
||||
|
||||
func (m *ApplicationSyncResult) GetMessage() string {
|
||||
if m != nil {
|
||||
@@ -196,7 +224,7 @@ func (m *ApplicationRollbackRequest) Reset() { *m = ApplicationRollbackR
|
||||
func (m *ApplicationRollbackRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*ApplicationRollbackRequest) ProtoMessage() {}
|
||||
func (*ApplicationRollbackRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptorApplication, []int{5}
|
||||
return fileDescriptorApplication, []int{6}
|
||||
}
|
||||
|
||||
func (m *ApplicationRollbackRequest) GetName() string {
|
||||
@@ -237,7 +265,7 @@ type ResourceDetails struct {
|
||||
func (m *ResourceDetails) Reset() { *m = ResourceDetails{} }
|
||||
func (m *ResourceDetails) String() string { return proto.CompactTextString(m) }
|
||||
func (*ResourceDetails) ProtoMessage() {}
|
||||
func (*ResourceDetails) Descriptor() ([]byte, []int) { return fileDescriptorApplication, []int{6} }
|
||||
func (*ResourceDetails) Descriptor() ([]byte, []int) { return fileDescriptorApplication, []int{7} }
|
||||
|
||||
func (m *ResourceDetails) GetName() string {
|
||||
if m != nil {
|
||||
@@ -275,7 +303,7 @@ type DeletePodQuery struct {
|
||||
func (m *DeletePodQuery) Reset() { *m = DeletePodQuery{} }
|
||||
func (m *DeletePodQuery) String() string { return proto.CompactTextString(m) }
|
||||
func (*DeletePodQuery) ProtoMessage() {}
|
||||
func (*DeletePodQuery) Descriptor() ([]byte, []int) { return fileDescriptorApplication, []int{7} }
|
||||
func (*DeletePodQuery) Descriptor() ([]byte, []int) { return fileDescriptorApplication, []int{8} }
|
||||
|
||||
func (m *DeletePodQuery) GetApplicationName() string {
|
||||
if m != nil {
|
||||
@@ -304,7 +332,7 @@ type PodLogsQuery struct {
|
||||
func (m *PodLogsQuery) Reset() { *m = PodLogsQuery{} }
|
||||
func (m *PodLogsQuery) String() string { return proto.CompactTextString(m) }
|
||||
func (*PodLogsQuery) ProtoMessage() {}
|
||||
func (*PodLogsQuery) Descriptor() ([]byte, []int) { return fileDescriptorApplication, []int{8} }
|
||||
func (*PodLogsQuery) Descriptor() ([]byte, []int) { return fileDescriptorApplication, []int{9} }
|
||||
|
||||
func (m *PodLogsQuery) GetApplicationName() string {
|
||||
if m != nil {
|
||||
@@ -363,7 +391,7 @@ type LogEntry struct {
|
||||
func (m *LogEntry) Reset() { *m = LogEntry{} }
|
||||
func (m *LogEntry) String() string { return proto.CompactTextString(m) }
|
||||
func (*LogEntry) ProtoMessage() {}
|
||||
func (*LogEntry) Descriptor() ([]byte, []int) { return fileDescriptorApplication, []int{9} }
|
||||
func (*LogEntry) Descriptor() ([]byte, []int) { return fileDescriptorApplication, []int{10} }
|
||||
|
||||
func (m *LogEntry) GetContent() string {
|
||||
if m != nil {
|
||||
@@ -384,6 +412,7 @@ func init() {
|
||||
proto.RegisterType((*ApplicationResponse)(nil), "application.ApplicationResponse")
|
||||
proto.RegisterType((*DeleteApplicationRequest)(nil), "application.DeleteApplicationRequest")
|
||||
proto.RegisterType((*ApplicationSyncRequest)(nil), "application.ApplicationSyncRequest")
|
||||
proto.RegisterType((*ApplicationSpecRequest)(nil), "application.ApplicationSpecRequest")
|
||||
proto.RegisterType((*ApplicationSyncResult)(nil), "application.ApplicationSyncResult")
|
||||
proto.RegisterType((*ApplicationRollbackRequest)(nil), "application.ApplicationRollbackRequest")
|
||||
proto.RegisterType((*ResourceDetails)(nil), "application.ResourceDetails")
|
||||
@@ -413,6 +442,8 @@ type ApplicationServiceClient interface {
|
||||
Get(ctx context.Context, in *ApplicationQuery, opts ...grpc.CallOption) (*github_com_argoproj_argo_cd_pkg_apis_application_v1alpha1.Application, error)
|
||||
// Update updates an application
|
||||
Update(ctx context.Context, in *github_com_argoproj_argo_cd_pkg_apis_application_v1alpha1.Application, opts ...grpc.CallOption) (*github_com_argoproj_argo_cd_pkg_apis_application_v1alpha1.Application, error)
|
||||
// Update updates an application spec
|
||||
UpdateSpec(ctx context.Context, in *ApplicationSpecRequest, opts ...grpc.CallOption) (*github_com_argoproj_argo_cd_pkg_apis_application_v1alpha1.ApplicationSpec, error)
|
||||
// Delete deletes an application
|
||||
Delete(ctx context.Context, in *DeleteApplicationRequest, opts ...grpc.CallOption) (*ApplicationResponse, error)
|
||||
// Sync syncs an application to its target state
|
||||
@@ -501,6 +532,15 @@ func (c *applicationServiceClient) Update(ctx context.Context, in *github_com_ar
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *applicationServiceClient) UpdateSpec(ctx context.Context, in *ApplicationSpecRequest, opts ...grpc.CallOption) (*github_com_argoproj_argo_cd_pkg_apis_application_v1alpha1.ApplicationSpec, error) {
|
||||
out := new(github_com_argoproj_argo_cd_pkg_apis_application_v1alpha1.ApplicationSpec)
|
||||
err := grpc.Invoke(ctx, "/application.ApplicationService/UpdateSpec", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *applicationServiceClient) Delete(ctx context.Context, in *DeleteApplicationRequest, opts ...grpc.CallOption) (*ApplicationResponse, error) {
|
||||
out := new(ApplicationResponse)
|
||||
err := grpc.Invoke(ctx, "/application.ApplicationService/Delete", in, out, c.cc, opts...)
|
||||
@@ -582,6 +622,8 @@ type ApplicationServiceServer interface {
|
||||
Get(context.Context, *ApplicationQuery) (*github_com_argoproj_argo_cd_pkg_apis_application_v1alpha1.Application, error)
|
||||
// Update updates an application
|
||||
Update(context.Context, *github_com_argoproj_argo_cd_pkg_apis_application_v1alpha1.Application) (*github_com_argoproj_argo_cd_pkg_apis_application_v1alpha1.Application, error)
|
||||
// Update updates an application spec
|
||||
UpdateSpec(context.Context, *ApplicationSpecRequest) (*github_com_argoproj_argo_cd_pkg_apis_application_v1alpha1.ApplicationSpec, error)
|
||||
// Delete deletes an application
|
||||
Delete(context.Context, *DeleteApplicationRequest) (*ApplicationResponse, error)
|
||||
// Sync syncs an application to its target state
|
||||
@@ -691,6 +733,24 @@ func _ApplicationService_Update_Handler(srv interface{}, ctx context.Context, de
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ApplicationService_UpdateSpec_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ApplicationSpecRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ApplicationServiceServer).UpdateSpec(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/application.ApplicationService/UpdateSpec",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ApplicationServiceServer).UpdateSpec(ctx, req.(*ApplicationSpecRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ApplicationService_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(DeleteApplicationRequest)
|
||||
if err := dec(in); err != nil {
|
||||
@@ -804,6 +864,10 @@ var _ApplicationService_serviceDesc = grpc.ServiceDesc{
|
||||
MethodName: "Update",
|
||||
Handler: _ApplicationService_Update_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "UpdateSpec",
|
||||
Handler: _ApplicationService_UpdateSpec_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Delete",
|
||||
Handler: _ApplicationService_Delete_Handler,
|
||||
@@ -974,6 +1038,40 @@ func (m *ApplicationSyncRequest) MarshalTo(dAtA []byte) (int, error) {
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (m *ApplicationSpecRequest) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalTo(dAtA)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *ApplicationSpecRequest) MarshalTo(dAtA []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.AppName) > 0 {
|
||||
dAtA[i] = 0xa
|
||||
i++
|
||||
i = encodeVarintApplication(dAtA, i, uint64(len(m.AppName)))
|
||||
i += copy(dAtA[i:], m.AppName)
|
||||
}
|
||||
if m.Spec != nil {
|
||||
dAtA[i] = 0x12
|
||||
i++
|
||||
i = encodeVarintApplication(dAtA, i, uint64(m.Spec.Size()))
|
||||
n1, err := m.Spec.MarshalTo(dAtA[i:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i += n1
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (m *ApplicationSyncResult) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
@@ -1173,11 +1271,11 @@ func (m *PodLogsQuery) MarshalTo(dAtA []byte) (int, error) {
|
||||
dAtA[i] = 0x2a
|
||||
i++
|
||||
i = encodeVarintApplication(dAtA, i, uint64(m.SinceTime.Size()))
|
||||
n1, err := m.SinceTime.MarshalTo(dAtA[i:])
|
||||
n2, err := m.SinceTime.MarshalTo(dAtA[i:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i += n1
|
||||
i += n2
|
||||
}
|
||||
if m.TailLines != 0 {
|
||||
dAtA[i] = 0x30
|
||||
@@ -1222,11 +1320,11 @@ func (m *LogEntry) MarshalTo(dAtA []byte) (int, error) {
|
||||
dAtA[i] = 0x12
|
||||
i++
|
||||
i = encodeVarintApplication(dAtA, i, uint64(m.TimeStamp.Size()))
|
||||
n2, err := m.TimeStamp.MarshalTo(dAtA[i:])
|
||||
n3, err := m.TimeStamp.MarshalTo(dAtA[i:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i += n2
|
||||
i += n3
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
@@ -1297,6 +1395,20 @@ func (m *ApplicationSyncRequest) Size() (n int) {
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *ApplicationSpecRequest) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
l = len(m.AppName)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovApplication(uint64(l))
|
||||
}
|
||||
if m.Spec != nil {
|
||||
l = m.Spec.Size()
|
||||
n += 1 + l + sovApplication(uint64(l))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *ApplicationSyncResult) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
@@ -1860,6 +1972,118 @@ func (m *ApplicationSyncRequest) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *ApplicationSpecRequest) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowApplication
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: ApplicationSpecRequest: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: ApplicationSpecRequest: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field AppName", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowApplication
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthApplication
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.AppName = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Spec", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowApplication
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthApplication
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if m.Spec == nil {
|
||||
m.Spec = &github_com_argoproj_argo_cd_pkg_apis_application_v1alpha1.ApplicationSpec{}
|
||||
}
|
||||
if err := m.Spec.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipApplication(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthApplication
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *ApplicationSyncResult) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
@@ -2830,67 +3054,71 @@ var (
|
||||
func init() { proto.RegisterFile("server/application/application.proto", fileDescriptorApplication) }
|
||||
|
||||
var fileDescriptorApplication = []byte{
|
||||
// 990 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x56, 0x41, 0x6f, 0xdc, 0x44,
|
||||
0x14, 0x96, 0x77, 0x37, 0x9b, 0xdd, 0x49, 0x45, 0xd1, 0x90, 0x44, 0xae, 0x9b, 0xa6, 0xab, 0x69,
|
||||
0x0a, 0x4b, 0x10, 0x76, 0x13, 0x40, 0xa0, 0xa8, 0x1c, 0x08, 0x29, 0x50, 0x14, 0xa1, 0xe0, 0x14,
|
||||
0x21, 0x71, 0x41, 0x13, 0x7b, 0xea, 0x98, 0xb5, 0x67, 0xdc, 0x99, 0x59, 0xa3, 0xa5, 0xe4, 0x00,
|
||||
0x27, 0x38, 0x21, 0x04, 0x77, 0x7e, 0x07, 0xff, 0x00, 0x6e, 0x48, 0xdc, 0x11, 0x8a, 0xf8, 0x0b,
|
||||
0xdc, 0xd1, 0x8c, 0xed, 0xb5, 0xbd, 0xd9, 0xdd, 0x36, 0x68, 0x0f, 0x3d, 0x79, 0xde, 0x9b, 0x37,
|
||||
0xef, 0xfb, 0xde, 0x7b, 0x33, 0xef, 0x19, 0x6c, 0x09, 0xc2, 0x53, 0xc2, 0x1d, 0x9c, 0x24, 0x51,
|
||||
0xe8, 0x61, 0x19, 0x32, 0x5a, 0x5d, 0xdb, 0x09, 0x67, 0x92, 0xc1, 0x95, 0x8a, 0xca, 0x5a, 0x0d,
|
||||
0x58, 0xc0, 0xb4, 0xde, 0x51, 0xab, 0xcc, 0xc4, 0xda, 0x08, 0x18, 0x0b, 0x22, 0xe2, 0xe0, 0x24,
|
||||
0x74, 0x30, 0xa5, 0x4c, 0x6a, 0x63, 0x91, 0xef, 0xa2, 0xc1, 0x5b, 0xc2, 0x0e, 0x99, 0xde, 0xf5,
|
||||
0x18, 0x27, 0x4e, 0xba, 0xe3, 0x04, 0x84, 0x12, 0x8e, 0x25, 0xf1, 0x73, 0x9b, 0xd7, 0x4b, 0x9b,
|
||||
0x18, 0x7b, 0xa7, 0x21, 0x25, 0x7c, 0xe4, 0x24, 0x83, 0x40, 0x29, 0x84, 0x13, 0x13, 0x89, 0xa7,
|
||||
0x9d, 0xba, 0x1f, 0x84, 0xf2, 0x74, 0x78, 0x62, 0x7b, 0x2c, 0x76, 0x30, 0xd7, 0xc4, 0xbe, 0xd0,
|
||||
0x8b, 0x57, 0x3d, 0xbf, 0x3c, 0x5d, 0x0d, 0x2f, 0xdd, 0xc1, 0x51, 0x72, 0x8a, 0x2f, 0xb8, 0x42,
|
||||
0x2f, 0x82, 0xe7, 0xdf, 0x29, 0xed, 0x3e, 0x1e, 0x12, 0x3e, 0x82, 0x10, 0xb4, 0x28, 0x8e, 0x89,
|
||||
0x69, 0xf4, 0x8c, 0x7e, 0xd7, 0xd5, 0x6b, 0xb4, 0x06, 0x5e, 0xa8, 0xd8, 0xb9, 0x44, 0x24, 0x8c,
|
||||
0x0a, 0x82, 0xbe, 0x02, 0xe6, 0x01, 0x89, 0x88, 0x24, 0xb5, 0xcd, 0x47, 0x43, 0x22, 0xe4, 0x34,
|
||||
0x37, 0x70, 0x03, 0x74, 0xd5, 0x57, 0x24, 0xd8, 0x23, 0x66, 0x43, 0x6f, 0x94, 0x0a, 0xb8, 0x0e,
|
||||
0xda, 0x59, 0x69, 0xcc, 0xa6, 0xde, 0xca, 0x25, 0xb8, 0x0a, 0x96, 0x1e, 0x32, 0xee, 0x11, 0xb3,
|
||||
0xd5, 0x33, 0xfa, 0x1d, 0x37, 0x13, 0x50, 0x0a, 0xd6, 0x2b, 0xa8, 0xc7, 0x23, 0xea, 0xcd, 0x43,
|
||||
0xb6, 0x40, 0x87, 0x93, 0x34, 0x14, 0x21, 0xa3, 0x39, 0xf0, 0x58, 0x56, 0xb8, 0x3e, 0x1f, 0xb9,
|
||||
0x43, 0xaa, 0x71, 0x3b, 0x6e, 0x2e, 0x29, 0xdc, 0x84, 0x0f, 0xe9, 0x18, 0x57, 0x0b, 0x28, 0x06,
|
||||
0x6b, 0x17, 0x70, 0xc5, 0x30, 0x92, 0xd0, 0x04, 0xcb, 0x31, 0x11, 0x02, 0x07, 0x05, 0x72, 0x21,
|
||||
0xc2, 0x3d, 0xd0, 0xe5, 0x44, 0xb0, 0x21, 0xf7, 0x88, 0x30, 0x1b, 0xbd, 0x66, 0x7f, 0x65, 0x77,
|
||||
0xc3, 0xae, 0x5e, 0x39, 0x37, 0xdf, 0x3d, 0x20, 0x12, 0x87, 0x91, 0x70, 0x4b, 0x73, 0x94, 0x02,
|
||||
0xab, 0x9a, 0x5c, 0x16, 0x45, 0x27, 0xd8, 0x1b, 0xcc, 0x0b, 0x75, 0x1d, 0x34, 0x42, 0x5f, 0x07,
|
||||
0xd9, 0xdc, 0x6f, 0x9f, 0xff, 0x75, 0xb3, 0x71, 0xff, 0xc0, 0x6d, 0x84, 0xfe, 0x25, 0xc3, 0x7c,
|
||||
0x04, 0xae, 0x4e, 0xb0, 0x9a, 0x0a, 0x06, 0x41, 0x6b, 0x10, 0x52, 0x3f, 0xcf, 0xa9, 0x5e, 0xd7,
|
||||
0xab, 0xdc, 0x9c, 0xac, 0x72, 0x25, 0x4d, 0xad, 0x5a, 0x9a, 0xd0, 0x03, 0xf0, 0x5c, 0x76, 0x9b,
|
||||
0x8e, 0x98, 0x9f, 0x5d, 0xc5, 0x3e, 0xb8, 0x5a, 0x49, 0xd3, 0x47, 0x25, 0xf8, 0xa4, 0x5a, 0x79,
|
||||
0x4d, 0x98, 0xaf, 0x2d, 0x32, 0x2a, 0x85, 0x88, 0x7e, 0x6e, 0x80, 0x2b, 0x47, 0xcc, 0x3f, 0x64,
|
||||
0x81, 0x58, 0x98, 0x53, 0x15, 0xa2, 0xc7, 0xa8, 0xc4, 0xea, 0xc5, 0x16, 0x21, 0x8e, 0x15, 0x10,
|
||||
0x81, 0x2b, 0x22, 0xa4, 0x1e, 0x39, 0x26, 0x1e, 0xa3, 0xbe, 0xd0, 0x71, 0x36, 0xdd, 0x9a, 0x0e,
|
||||
0x7e, 0x00, 0xba, 0x5a, 0x7e, 0x10, 0xc6, 0xc4, 0x5c, 0xea, 0x19, 0xfd, 0x95, 0xdd, 0x6d, 0x3b,
|
||||
0x6b, 0x07, 0x76, 0xb5, 0x1d, 0xd8, 0xc9, 0x20, 0x50, 0x0a, 0x61, 0xab, 0x76, 0x60, 0xa7, 0x3b,
|
||||
0xb6, 0x3a, 0xe1, 0x96, 0x87, 0x15, 0x17, 0x55, 0x9f, 0xc3, 0x90, 0x12, 0x61, 0xb6, 0x35, 0x54,
|
||||
0xa9, 0x50, 0x55, 0x7f, 0xc8, 0xa2, 0x88, 0x7d, 0x69, 0x2e, 0x67, 0x55, 0xcf, 0x24, 0x44, 0x41,
|
||||
0xe7, 0x90, 0x05, 0xf7, 0xa8, 0xe4, 0x23, 0x15, 0xa7, 0x22, 0x4f, 0xa8, 0x2c, 0x6e, 0x6e, 0x2e,
|
||||
0x2a, 0x96, 0x32, 0x8c, 0xc9, 0xb1, 0xc4, 0x71, 0xa2, 0x73, 0x70, 0x49, 0x96, 0xe3, 0xc3, 0xbb,
|
||||
0xff, 0xae, 0x00, 0x58, 0x7d, 0x37, 0x84, 0xa7, 0xa1, 0x47, 0xe0, 0x0f, 0x06, 0x68, 0x1d, 0x86,
|
||||
0x42, 0xc2, 0x1b, 0xb5, 0x07, 0x31, 0xd9, 0x94, 0xac, 0x0f, 0xed, 0xb2, 0xe9, 0xd9, 0x45, 0xd3,
|
||||
0xd3, 0x8b, 0xcf, 0x3d, 0xbf, 0x44, 0xaf, 0xfa, 0x28, 0x9a, 0x5e, 0xd5, 0x99, 0x82, 0x42, 0x1b,
|
||||
0xdf, 0xfe, 0xf9, 0xcf, 0x4f, 0x8d, 0x75, 0xb8, 0xaa, 0x7b, 0x73, 0xba, 0x53, 0x6d, 0x95, 0x02,
|
||||
0xfe, 0x62, 0x80, 0xa5, 0x4f, 0xb1, 0xf4, 0x4e, 0x9f, 0x44, 0xe9, 0x68, 0x31, 0x94, 0x34, 0xd6,
|
||||
0xbd, 0x94, 0x50, 0x89, 0x6e, 0x69, 0x62, 0x37, 0xe0, 0xf5, 0x82, 0x98, 0x90, 0x9c, 0xe0, 0xb8,
|
||||
0xc6, 0xef, 0x8e, 0x01, 0x7f, 0x35, 0x40, 0xfb, 0x5d, 0x4e, 0xb0, 0x24, 0xf0, 0xbd, 0xc5, 0x70,
|
||||
0xb0, 0x16, 0xe4, 0x07, 0xdd, 0xd4, 0x11, 0x5c, 0x43, 0x53, 0x53, 0xbb, 0x67, 0x6c, 0xc3, 0x1f,
|
||||
0x0d, 0xd0, 0x7c, 0x9f, 0x3c, 0xb1, 0xdc, 0x8b, 0xe2, 0x73, 0x21, 0xa3, 0x55, 0x3e, 0xce, 0x63,
|
||||
0xd5, 0x95, 0xce, 0xe0, 0xef, 0x06, 0x68, 0x7f, 0x92, 0xf8, 0xcf, 0x62, 0x3e, 0x1d, 0xcd, 0xff,
|
||||
0x65, 0x6b, 0x6b, 0x3a, 0x7f, 0xf5, 0xd8, 0x7c, 0x2c, 0xb1, 0xad, 0x03, 0x51, 0xf9, 0x4d, 0x41,
|
||||
0x3b, 0xeb, 0xa1, 0xf0, 0x76, 0xcd, 0xfb, 0xac, 0x31, 0x6d, 0xf5, 0x66, 0x15, 0x62, 0x3c, 0xe4,
|
||||
0xf3, 0x1c, 0x6e, 0xcf, 0xcd, 0xe1, 0xd7, 0xa0, 0xa5, 0x46, 0x21, 0xbc, 0x35, 0xcb, 0x5d, 0x65,
|
||||
0x40, 0x5b, 0x68, 0xbe, 0x91, 0x9a, 0xa6, 0xe8, 0x15, 0x8d, 0x7a, 0x1b, 0xf5, 0xe6, 0xa0, 0x3a,
|
||||
0x62, 0x44, 0x3d, 0x15, 0xf5, 0x77, 0x06, 0xe8, 0x14, 0xa3, 0x11, 0xbe, 0x34, 0x33, 0xa2, 0xfa,
|
||||
0xf0, 0x7c, 0x2a, 0x1a, 0x79, 0x01, 0xd0, 0xd6, 0x3c, 0x1a, 0x3c, 0x77, 0xac, 0xa8, 0x7c, 0x6f,
|
||||
0x80, 0xee, 0x78, 0x8a, 0xc1, 0xeb, 0x53, 0x8a, 0x50, 0x4c, 0xb7, 0xa7, 0x48, 0xfd, 0xdb, 0x1a,
|
||||
0xfd, 0xcd, 0xed, 0x37, 0xa6, 0xa3, 0x4f, 0xcc, 0xab, 0x33, 0x27, 0x61, 0xbe, 0x70, 0x1e, 0xe7,
|
||||
0x43, 0xea, 0x0c, 0x7e, 0x63, 0x80, 0xe5, 0x7c, 0xf4, 0xc1, 0x6b, 0x35, 0xb0, 0xea, 0x40, 0xb4,
|
||||
0xd6, 0x6a, 0x5b, 0xc5, 0x54, 0x40, 0xfb, 0x1a, 0xfc, 0x2e, 0xdc, 0xfb, 0x5f, 0xe0, 0x4e, 0xc4,
|
||||
0x02, 0x71, 0xc7, 0xd8, 0xbf, 0xfb, 0xdb, 0xf9, 0xa6, 0xf1, 0xc7, 0xf9, 0xa6, 0xf1, 0xf7, 0xf9,
|
||||
0xa6, 0xf1, 0x99, 0x3d, 0xef, 0xd7, 0xf5, 0xe2, 0x7f, 0xf9, 0x49, 0x5b, 0xff, 0xa6, 0xbe, 0xf6,
|
||||
0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x69, 0xad, 0xd8, 0xee, 0xb4, 0x0b, 0x00, 0x00,
|
||||
// 1054 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x57, 0x4f, 0x6f, 0xdc, 0x44,
|
||||
0x14, 0x97, 0x77, 0x37, 0x9b, 0x64, 0x52, 0x28, 0x1a, 0x92, 0xc8, 0x75, 0xd3, 0x34, 0x9a, 0xa4,
|
||||
0x10, 0x82, 0xb0, 0x9b, 0x00, 0x02, 0x45, 0xe5, 0x40, 0x48, 0x81, 0xa2, 0x08, 0x05, 0xa7, 0x08,
|
||||
0x89, 0x03, 0x68, 0x62, 0xbf, 0x3a, 0x26, 0xf6, 0x8c, 0xeb, 0x99, 0x35, 0x5a, 0x4a, 0x0e, 0x70,
|
||||
0x82, 0x13, 0xe2, 0xcf, 0x9d, 0x13, 0x47, 0x3e, 0x00, 0xdf, 0x00, 0x6e, 0x48, 0xdc, 0x11, 0x8a,
|
||||
0xf8, 0x20, 0x68, 0xc6, 0xf6, 0xda, 0xde, 0xec, 0x6e, 0x5b, 0xb4, 0x87, 0x9e, 0x76, 0xde, 0xcc,
|
||||
0xf3, 0xfc, 0x7e, 0xef, 0xbd, 0x99, 0xf7, 0x9b, 0x45, 0x1b, 0x02, 0xd2, 0x0c, 0x52, 0x87, 0x26,
|
||||
0x49, 0x14, 0x7a, 0x54, 0x86, 0x9c, 0xd5, 0xc7, 0x76, 0x92, 0x72, 0xc9, 0xf1, 0x42, 0x6d, 0xca,
|
||||
0x5a, 0x0c, 0x78, 0xc0, 0xf5, 0xbc, 0xa3, 0x46, 0xb9, 0x8b, 0xb5, 0x12, 0x70, 0x1e, 0x44, 0xe0,
|
||||
0xd0, 0x24, 0x74, 0x28, 0x63, 0x5c, 0x6a, 0x67, 0x51, 0xac, 0x92, 0xd3, 0xd7, 0x85, 0x1d, 0x72,
|
||||
0xbd, 0xea, 0xf1, 0x14, 0x9c, 0x6c, 0xdb, 0x09, 0x80, 0x41, 0x4a, 0x25, 0xf8, 0x85, 0xcf, 0x2b,
|
||||
0x95, 0x4f, 0x4c, 0xbd, 0x93, 0x90, 0x41, 0xda, 0x77, 0x92, 0xd3, 0x40, 0x4d, 0x08, 0x27, 0x06,
|
||||
0x49, 0x47, 0x7d, 0x75, 0x27, 0x08, 0xe5, 0x49, 0xef, 0xd8, 0xf6, 0x78, 0xec, 0xd0, 0x54, 0x13,
|
||||
0xfb, 0x4c, 0x0f, 0x5e, 0xf2, 0xfc, 0xea, 0xeb, 0x7a, 0x78, 0xd9, 0x36, 0x8d, 0x92, 0x13, 0x7a,
|
||||
0x61, 0x2b, 0xf2, 0x1c, 0x7a, 0xe6, 0xcd, 0xca, 0xef, 0x83, 0x1e, 0xa4, 0x7d, 0x8c, 0x51, 0x87,
|
||||
0xd1, 0x18, 0x4c, 0x63, 0xcd, 0xd8, 0x9c, 0x77, 0xf5, 0x98, 0x2c, 0xa1, 0x67, 0x6b, 0x7e, 0x2e,
|
||||
0x88, 0x84, 0x33, 0x01, 0xe4, 0x0b, 0x64, 0xee, 0x43, 0x04, 0x12, 0x1a, 0x8b, 0xf7, 0x7b, 0x20,
|
||||
0xe4, 0xa8, 0x6d, 0xf0, 0x0a, 0x9a, 0x57, 0xbf, 0x22, 0xa1, 0x1e, 0x98, 0x2d, 0xbd, 0x50, 0x4d,
|
||||
0xe0, 0x65, 0xd4, 0xcd, 0x4b, 0x63, 0xb6, 0xf5, 0x52, 0x61, 0xe1, 0x45, 0x34, 0x73, 0x8f, 0xa7,
|
||||
0x1e, 0x98, 0x9d, 0x35, 0x63, 0x73, 0xce, 0xcd, 0x0d, 0x92, 0xa1, 0xe5, 0x1a, 0xea, 0x51, 0x9f,
|
||||
0x79, 0x93, 0x90, 0x2d, 0x34, 0x97, 0x42, 0x16, 0x8a, 0x90, 0xb3, 0x02, 0x78, 0x60, 0x2b, 0x5c,
|
||||
0x3f, 0xed, 0xbb, 0x3d, 0xa6, 0x71, 0xe7, 0xdc, 0xc2, 0x52, 0xb8, 0x49, 0xda, 0x63, 0x03, 0x5c,
|
||||
0x6d, 0x90, 0x1f, 0x8c, 0x26, 0x70, 0x02, 0x03, 0x60, 0x13, 0xcd, 0xd2, 0x24, 0x79, 0xbf, 0xc2,
|
||||
0x2e, 0x4d, 0xfc, 0x09, 0xea, 0x88, 0x04, 0x3c, 0x0d, 0xbd, 0xb0, 0xf3, 0x9e, 0x5d, 0x55, 0xd0,
|
||||
0x2e, 0x2b, 0xa8, 0x07, 0x9f, 0x7a, 0xbe, 0x9d, 0x9c, 0x06, 0xb6, 0xaa, 0xa0, 0x5d, 0x3f, 0x94,
|
||||
0x65, 0x05, 0xed, 0x61, 0x68, 0xbd, 0x2f, 0x89, 0xd1, 0xd2, 0x85, 0x64, 0x88, 0x5e, 0xa4, 0x29,
|
||||
0xc5, 0x20, 0x04, 0x0d, 0x06, 0x94, 0x0a, 0x13, 0xef, 0xa2, 0xf9, 0x14, 0x04, 0xef, 0xa5, 0x1e,
|
||||
0x08, 0xb3, 0xb5, 0xd6, 0xde, 0x5c, 0xd8, 0x59, 0x69, 0x40, 0xba, 0xc5, 0xea, 0x3e, 0x48, 0x1a,
|
||||
0x46, 0xc2, 0xad, 0xdc, 0x49, 0x86, 0xac, 0x7a, 0xc5, 0x79, 0x14, 0x1d, 0x53, 0xef, 0x74, 0x52,
|
||||
0xfe, 0x97, 0x51, 0x2b, 0xf4, 0x75, 0xf8, 0xed, 0xbd, 0xee, 0xf9, 0xdf, 0xd7, 0x5b, 0x77, 0xf6,
|
||||
0xdd, 0x56, 0xe8, 0x3f, 0x66, 0xee, 0xef, 0xa3, 0xcb, 0x43, 0xac, 0x46, 0x82, 0x61, 0xd4, 0x39,
|
||||
0x0d, 0x99, 0x5f, 0x14, 0x5a, 0x8f, 0x9b, 0x47, 0xaf, 0x3d, 0x7c, 0xf4, 0x6a, 0x69, 0xea, 0x34,
|
||||
0xd2, 0x44, 0xee, 0xa2, 0xa7, 0xf3, 0x23, 0x7e, 0xc8, 0xfd, 0xfc, 0x7e, 0x6c, 0xa2, 0xcb, 0xb5,
|
||||
0x34, 0xd5, 0xaa, 0x3d, 0x3c, 0xad, 0x76, 0x4d, 0xb8, 0xaf, 0x3d, 0x72, 0x2a, 0xa5, 0x49, 0x7e,
|
||||
0x6a, 0xa1, 0x4b, 0x87, 0xdc, 0x3f, 0xe0, 0x81, 0x98, 0xda, 0xa6, 0x2a, 0x44, 0x8f, 0x33, 0x49,
|
||||
0x55, 0x1b, 0x29, 0x43, 0x1c, 0x4c, 0x60, 0x82, 0x2e, 0x89, 0x90, 0x79, 0x70, 0x04, 0x1e, 0x67,
|
||||
0xbe, 0xd0, 0x71, 0xb6, 0xdd, 0xc6, 0x1c, 0x7e, 0x17, 0xcd, 0x6b, 0xfb, 0x6e, 0x18, 0x83, 0x39,
|
||||
0xa3, 0xcf, 0xea, 0x96, 0x9d, 0xf7, 0x28, 0xbb, 0xde, 0xa3, 0xaa, 0x33, 0xaa, 0x7a, 0x94, 0x9d,
|
||||
0x6d, 0xdb, 0xea, 0x0b, 0xb7, 0xfa, 0x58, 0x71, 0x51, 0xf5, 0x39, 0x08, 0x19, 0x08, 0xb3, 0xab,
|
||||
0xa1, 0xaa, 0x09, 0x55, 0xf5, 0x7b, 0x3c, 0x8a, 0xf8, 0xe7, 0xe6, 0x6c, 0x5e, 0xf5, 0xdc, 0x22,
|
||||
0x0c, 0xcd, 0x1d, 0xf0, 0xe0, 0x36, 0x93, 0x69, 0x5f, 0xc5, 0xa9, 0xc8, 0x03, 0x93, 0xe5, 0xc9,
|
||||
0x2d, 0x4c, 0xc5, 0x52, 0x86, 0x31, 0x1c, 0x49, 0x1a, 0x27, 0xc5, 0x8d, 0x7a, 0x2c, 0x96, 0x83,
|
||||
0x8f, 0x77, 0x7e, 0x79, 0x0a, 0xe1, 0xfa, 0xbd, 0x81, 0x34, 0x0b, 0x3d, 0xc0, 0xdf, 0x19, 0xa8,
|
||||
0x73, 0x10, 0x0a, 0x89, 0xaf, 0x35, 0x2e, 0xc4, 0x70, 0xa7, 0xb4, 0xa6, 0x74, 0x8f, 0x15, 0x14,
|
||||
0x59, 0xf9, 0xfa, 0xaf, 0x7f, 0x7f, 0x6c, 0x2d, 0xe3, 0x45, 0x2d, 0x18, 0xd9, 0x76, 0xbd, 0x7f,
|
||||
0x0b, 0xfc, 0xb3, 0x81, 0x66, 0x3e, 0xa2, 0xd2, 0x3b, 0x79, 0x18, 0xa5, 0xc3, 0xe9, 0x50, 0xd2,
|
||||
0x58, 0xb7, 0x33, 0x60, 0x92, 0xac, 0x6b, 0x62, 0xd7, 0xf0, 0xd5, 0x92, 0x98, 0x90, 0x29, 0xd0,
|
||||
0xb8, 0xc1, 0xef, 0xa6, 0x81, 0x7f, 0x33, 0x50, 0xf7, 0xad, 0x14, 0xa8, 0x04, 0xfc, 0xf6, 0x74,
|
||||
0x38, 0x58, 0x53, 0xda, 0x87, 0x5c, 0xd7, 0x11, 0x5c, 0x21, 0x23, 0x53, 0xbb, 0x6b, 0x6c, 0xe1,
|
||||
0xef, 0x0d, 0xd4, 0x7e, 0x07, 0x1e, 0x5a, 0xee, 0x69, 0xf1, 0xb9, 0x90, 0xd1, 0x3a, 0x1f, 0xe7,
|
||||
0x81, 0xea, 0x4a, 0x67, 0xf8, 0x0f, 0x03, 0x75, 0x3f, 0x4c, 0xfc, 0x27, 0x31, 0x9f, 0x8e, 0xe6,
|
||||
0xff, 0x82, 0xb5, 0x31, 0x9a, 0xbf, 0xba, 0x6c, 0x3e, 0x95, 0xd4, 0xd6, 0x81, 0xa8, 0xfc, 0xfe,
|
||||
0x6a, 0x20, 0x94, 0xc7, 0xa2, 0x24, 0x0b, 0xaf, 0x8f, 0x4b, 0x73, 0x4d, 0x4b, 0xad, 0x29, 0x6a,
|
||||
0x24, 0xb1, 0x35, 0xe1, 0x4d, 0x6b, 0x7d, 0x34, 0xe1, 0x42, 0xa4, 0xcf, 0x1c, 0x25, 0xa5, 0x8a,
|
||||
0x6f, 0x86, 0xba, 0x79, 0xcf, 0xc7, 0x37, 0x1a, 0x00, 0xe3, 0xde, 0x3a, 0xd6, 0xda, 0xb8, 0x88,
|
||||
0x06, 0x2f, 0xa5, 0xa2, 0xe6, 0x5b, 0x13, 0x6b, 0xfe, 0x25, 0xea, 0x28, 0xe9, 0x9e, 0x90, 0xa0,
|
||||
0xea, 0x95, 0x63, 0x91, 0xc9, 0x4e, 0x4a, 0xfd, 0xc9, 0x8b, 0x1a, 0xf5, 0x06, 0x59, 0x9b, 0x80,
|
||||
0xea, 0x88, 0x3e, 0xd3, 0x51, 0x7f, 0x63, 0xa0, 0xb9, 0x52, 0xca, 0xf1, 0xf3, 0x63, 0x23, 0x6a,
|
||||
0x8a, 0xfd, 0x23, 0xd1, 0x28, 0x0e, 0x0c, 0xd9, 0x98, 0x44, 0x23, 0x2d, 0x36, 0x56, 0x54, 0xbe,
|
||||
0x35, 0xd0, 0xfc, 0x40, 0x75, 0xf1, 0xd5, 0x11, 0x45, 0x28, 0xd5, 0xf8, 0x11, 0x52, 0xff, 0x86,
|
||||
0x46, 0x7f, 0x6d, 0xeb, 0xd5, 0xb1, 0xd5, 0xaf, 0xeb, 0xeb, 0x99, 0x93, 0x70, 0x5f, 0x38, 0x0f,
|
||||
0x0a, 0x51, 0x3d, 0xc3, 0x5f, 0x19, 0x68, 0xb6, 0x90, 0x6a, 0x7c, 0xa5, 0x01, 0x56, 0x17, 0x70,
|
||||
0x6b, 0xa9, 0xb1, 0x54, 0xaa, 0x18, 0xd9, 0xd3, 0xe0, 0xb7, 0xf0, 0xee, 0xff, 0x02, 0x77, 0x22,
|
||||
0x1e, 0x88, 0x9b, 0xc6, 0xde, 0xad, 0xdf, 0xcf, 0x57, 0x8d, 0x3f, 0xcf, 0x57, 0x8d, 0x7f, 0xce,
|
||||
0x57, 0x8d, 0x8f, 0xed, 0x49, 0xef, 0xff, 0x8b, 0x7f, 0x6e, 0x8e, 0xbb, 0xfa, 0xad, 0xff, 0xf2,
|
||||
0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x61, 0x8e, 0xe6, 0xda, 0xf9, 0x0c, 0x00, 0x00,
|
||||
}
|
||||
|
||||
@@ -142,6 +142,37 @@ func request_ApplicationService_Update_0(ctx context.Context, marshaler runtime.
|
||||
|
||||
}
|
||||
|
||||
func request_ApplicationService_UpdateSpec_0(ctx context.Context, marshaler runtime.Marshaler, client ApplicationServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq ApplicationSpecRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
var (
|
||||
val string
|
||||
ok bool
|
||||
err error
|
||||
_ = err
|
||||
)
|
||||
|
||||
val, ok = pathParams["appName"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "appName")
|
||||
}
|
||||
|
||||
protoReq.AppName, err = runtime.String(val)
|
||||
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "appName", err)
|
||||
}
|
||||
|
||||
msg, err := client.UpdateSpec(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
var (
|
||||
filter_ApplicationService_Delete_0 = &utilities.DoubleArray{Encoding: map[string]int{"name": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}}
|
||||
)
|
||||
@@ -514,6 +545,35 @@ func RegisterApplicationServiceHandlerClient(ctx context.Context, mux *runtime.S
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("PUT", pattern_ApplicationService_UpdateSpec_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
if cn, ok := w.(http.CloseNotifier); ok {
|
||||
go func(done <-chan struct{}, closed <-chan bool) {
|
||||
select {
|
||||
case <-done:
|
||||
case <-closed:
|
||||
cancel()
|
||||
}
|
||||
}(ctx.Done(), cn.CloseNotify())
|
||||
}
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_ApplicationService_UpdateSpec_0(rctx, inboundMarshaler, client, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ApplicationService_UpdateSpec_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("DELETE", pattern_ApplicationService_Delete_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
@@ -673,6 +733,8 @@ var (
|
||||
|
||||
pattern_ApplicationService_Update_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"api", "v1", "applications", "metadata.name"}, ""))
|
||||
|
||||
pattern_ApplicationService_UpdateSpec_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v1", "applications", "appName", "spec"}, ""))
|
||||
|
||||
pattern_ApplicationService_Delete_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"api", "v1", "applications", "name"}, ""))
|
||||
|
||||
pattern_ApplicationService_Sync_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v1", "applications", "name", "sync"}, ""))
|
||||
@@ -695,6 +757,8 @@ var (
|
||||
|
||||
forward_ApplicationService_Update_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_ApplicationService_UpdateSpec_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_ApplicationService_Delete_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_ApplicationService_Sync_0 = runtime.ForwardResponseMessage
|
||||
|
||||
@@ -35,6 +35,12 @@ message ApplicationSyncRequest {
|
||||
bool prune = 4;
|
||||
}
|
||||
|
||||
// ApplicationSpecRequest is a request to update application spec
|
||||
message ApplicationSpecRequest {
|
||||
string appName = 1;
|
||||
github.com.argoproj.argo_cd.pkg.apis.application.v1alpha1.ApplicationSpec spec = 2;
|
||||
}
|
||||
|
||||
// ApplicationSyncResult is a result of a sync requeswt
|
||||
message ApplicationSyncResult {
|
||||
string message = 1;
|
||||
@@ -109,6 +115,14 @@ service ApplicationService {
|
||||
};
|
||||
}
|
||||
|
||||
// Update updates an application spec
|
||||
rpc UpdateSpec(ApplicationSpecRequest) returns (github.com.argoproj.argo_cd.pkg.apis.application.v1alpha1.ApplicationSpec) {
|
||||
option (google.api.http) = {
|
||||
put: "/api/v1/applications/{appName}/spec"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// Delete deletes an application
|
||||
rpc Delete(DeleteApplicationRequest) returns (ApplicationResponse) {
|
||||
option (google.api.http).delete = "/api/v1/applications/{name}";
|
||||
|
||||
@@ -12,8 +12,8 @@ import (
|
||||
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
apierr "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -63,7 +63,7 @@ func (s *Server) List(ctx context.Context, q *ClusterQuery) (*appv1.ClusterList,
|
||||
Items: make([]appv1.Cluster, len(clusterSecrets.Items)),
|
||||
}
|
||||
for i, clusterSecret := range clusterSecrets.Items {
|
||||
clusterList.Items[i] = *secretToCluster(&clusterSecret)
|
||||
clusterList.Items[i] = *secretToCluster(&clusterSecret, false)
|
||||
}
|
||||
return &clusterList, nil
|
||||
}
|
||||
@@ -87,11 +87,11 @@ func (s *Server) Create(ctx context.Context, c *appv1.Cluster) (*appv1.Cluster,
|
||||
clusterSecret, err = s.kubeclientset.CoreV1().Secrets(s.ns).Create(clusterSecret)
|
||||
if err != nil {
|
||||
if apierr.IsAlreadyExists(err) {
|
||||
return nil, grpc.Errorf(codes.AlreadyExists, "cluster '%s' already exists", c.Server)
|
||||
return nil, status.Errorf(codes.AlreadyExists, "cluster '%s' already exists", c.Server)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return secretToCluster(clusterSecret), nil
|
||||
return secretToCluster(clusterSecret, false), nil
|
||||
}
|
||||
|
||||
// ClusterEvent contains information about cluster event
|
||||
@@ -120,7 +120,7 @@ func (s *Server) WatchClusters(ctx context.Context, callback func(*ClusterEvent)
|
||||
}()
|
||||
for next := range w.ResultChan() {
|
||||
secret := next.Object.(*apiv1.Secret)
|
||||
cluster := secretToCluster(secret)
|
||||
cluster := secretToCluster(secret, false)
|
||||
callback(&ClusterEvent{
|
||||
Type: next.Type,
|
||||
Cluster: cluster,
|
||||
@@ -134,7 +134,7 @@ func (s *Server) getClusterSecret(server string) (*apiv1.Secret, error) {
|
||||
clusterSecret, err := s.kubeclientset.CoreV1().Secrets(s.ns).Get(secName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
if apierr.IsNotFound(err) {
|
||||
return nil, grpc.Errorf(codes.NotFound, "cluster '%s' not found", server)
|
||||
return nil, status.Errorf(codes.NotFound, "cluster '%s' not found", server)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
@@ -147,7 +147,7 @@ func (s *Server) Get(ctx context.Context, q *ClusterQuery) (*appv1.Cluster, erro
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return secretToCluster(clusterSecret), nil
|
||||
return secretToCluster(clusterSecret, false), nil
|
||||
}
|
||||
|
||||
// Update updates a cluster
|
||||
@@ -165,7 +165,7 @@ func (s *Server) Update(ctx context.Context, c *appv1.Cluster) (*appv1.Cluster,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return secretToCluster(clusterSecret), nil
|
||||
return secretToCluster(clusterSecret, false), nil
|
||||
}
|
||||
|
||||
// UpdateREST updates a cluster (special handler intended to be used only by the gRPC gateway)
|
||||
@@ -210,8 +210,8 @@ func clusterToStringData(c *appv1.Cluster) map[string]string {
|
||||
return stringData
|
||||
}
|
||||
|
||||
// secretToRepo converts a secret into a repository object
|
||||
func secretToCluster(s *apiv1.Secret) *appv1.Cluster {
|
||||
// secretToCluster converts a secret into a repository object, optionally redacting sensitive information
|
||||
func secretToCluster(s *apiv1.Secret, redact bool) *appv1.Cluster {
|
||||
var config appv1.ClusterConfig
|
||||
err := json.Unmarshal(s.Data["config"], &config)
|
||||
if err != nil {
|
||||
@@ -220,7 +220,9 @@ func secretToCluster(s *apiv1.Secret) *appv1.Cluster {
|
||||
cluster := appv1.Cluster{
|
||||
Server: string(s.Data["server"]),
|
||||
Name: string(s.Data["name"]),
|
||||
Config: config,
|
||||
}
|
||||
if !redact {
|
||||
cluster.Config = config
|
||||
}
|
||||
return &cluster
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
|
||||
"github.com/argoproj/argo-cd/util/git"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
apierr "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -54,7 +54,7 @@ func (s *Server) List(ctx context.Context, q *RepoQuery) (*appsv1.RepositoryList
|
||||
Items: make([]appsv1.Repository, len(repoSecrets.Items)),
|
||||
}
|
||||
for i, repoSec := range repoSecrets.Items {
|
||||
repoList.Items[i] = *secretToRepo(&repoSec)
|
||||
repoList.Items[i] = *secretToRepo(&repoSec, true)
|
||||
}
|
||||
return &repoList, nil
|
||||
}
|
||||
@@ -81,11 +81,11 @@ func (s *Server) Create(ctx context.Context, r *appsv1.Repository) (*appsv1.Repo
|
||||
repoSecret, err = s.kubeclientset.CoreV1().Secrets(s.ns).Create(repoSecret)
|
||||
if err != nil {
|
||||
if apierr.IsAlreadyExists(err) {
|
||||
return nil, grpc.Errorf(codes.AlreadyExists, "repository '%s' already exists", r.Repo)
|
||||
return nil, status.Errorf(codes.AlreadyExists, "repository '%s' already exists", r.Repo)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return secretToRepo(repoSecret), nil
|
||||
return secretToRepo(repoSecret, false), nil
|
||||
}
|
||||
|
||||
func (s *Server) getRepoSecret(repo string) (*apiv1.Secret, error) {
|
||||
@@ -93,7 +93,7 @@ func (s *Server) getRepoSecret(repo string) (*apiv1.Secret, error) {
|
||||
repoSecret, err := s.kubeclientset.CoreV1().Secrets(s.ns).Get(secName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
if apierr.IsNotFound(err) {
|
||||
return nil, grpc.Errorf(codes.NotFound, "repo '%s' not found", repo)
|
||||
return nil, status.Errorf(codes.NotFound, "repo '%s' not found", repo)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
@@ -106,7 +106,7 @@ func (s *Server) Get(ctx context.Context, q *RepoQuery) (*appsv1.Repository, err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return secretToRepo(repoSecret), nil
|
||||
return secretToRepo(repoSecret, false), nil
|
||||
}
|
||||
|
||||
// Update updates a repository
|
||||
@@ -124,7 +124,7 @@ func (s *Server) Update(ctx context.Context, r *appsv1.Repository) (*appsv1.Repo
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return secretToRepo(repoSecret), nil
|
||||
return secretToRepo(repoSecret, false), nil
|
||||
}
|
||||
|
||||
// UpdateREST updates a repository (from a REST request)
|
||||
@@ -159,12 +159,15 @@ func repoToStringData(r *appsv1.Repository) map[string]string {
|
||||
}
|
||||
}
|
||||
|
||||
// secretToRepo converts a secret into a repository object
|
||||
func secretToRepo(s *apiv1.Secret) *appsv1.Repository {
|
||||
return &appsv1.Repository{
|
||||
Repo: string(s.Data["repository"]),
|
||||
Username: string(s.Data["username"]),
|
||||
Password: string(s.Data["password"]),
|
||||
SSHPrivateKey: string(s.Data["sshPrivateKey"]),
|
||||
// secretToRepo converts a secret into a repository object, optionally redacting sensitive information
|
||||
func secretToRepo(s *apiv1.Secret, redact bool) *appsv1.Repository {
|
||||
repo := appsv1.Repository{
|
||||
Repo: string(s.Data["repository"]),
|
||||
Username: string(s.Data["username"]),
|
||||
}
|
||||
if !redact {
|
||||
repo.Password = string(s.Data["password"])
|
||||
repo.SSHPrivateKey = string(s.Data["sshPrivateKey"])
|
||||
}
|
||||
return &repo
|
||||
}
|
||||
|
||||
199
server/server.go
199
server/server.go
@@ -8,20 +8,25 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
argocd "github.com/argoproj/argo-cd"
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
"github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
|
||||
"github.com/argoproj/argo-cd/reposerver"
|
||||
"github.com/argoproj/argo-cd/server/application"
|
||||
"github.com/argoproj/argo-cd/server/cluster"
|
||||
"github.com/argoproj/argo-cd/server/repository"
|
||||
"github.com/argoproj/argo-cd/server/session"
|
||||
"github.com/argoproj/argo-cd/server/settings"
|
||||
"github.com/argoproj/argo-cd/server/version"
|
||||
"github.com/argoproj/argo-cd/util/config"
|
||||
dexutil "github.com/argoproj/argo-cd/util/dex"
|
||||
grpc_util "github.com/argoproj/argo-cd/util/grpc"
|
||||
jsonutil "github.com/argoproj/argo-cd/util/json"
|
||||
util_session "github.com/argoproj/argo-cd/util/session"
|
||||
settings_util "github.com/argoproj/argo-cd/util/settings"
|
||||
tlsutil "github.com/argoproj/argo-cd/util/tls"
|
||||
golang_proto "github.com/golang/protobuf/proto"
|
||||
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
@@ -36,24 +41,29 @@ import (
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/reflection"
|
||||
"google.golang.org/grpc/status"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
)
|
||||
|
||||
const (
|
||||
port = 8080
|
||||
authCookieName = "argocd.argoproj.io/auth-token"
|
||||
port = 8080
|
||||
)
|
||||
|
||||
var (
|
||||
endpoint = fmt.Sprintf("localhost:%d", port)
|
||||
// ErrNoSession indicates no auth token was supplied as part of a request
|
||||
ErrNoSession = status.Errorf(codes.Unauthenticated, "no session information")
|
||||
)
|
||||
|
||||
// ArgoCDServer is the API server for ArgoCD
|
||||
type ArgoCDServer struct {
|
||||
ArgoCDServerOpts
|
||||
|
||||
settings config.ArgoCDSettings
|
||||
log *log.Entry
|
||||
ssoClientApp *dexutil.ClientApp
|
||||
settings settings_util.ArgoCDSettings
|
||||
log *log.Entry
|
||||
sessionMgr *util_session.SessionManager
|
||||
settingsMgr *settings_util.SettingsManager
|
||||
}
|
||||
|
||||
type ArgoCDServerOpts struct {
|
||||
@@ -68,15 +78,18 @@ type ArgoCDServerOpts struct {
|
||||
|
||||
// NewServer returns a new instance of the ArgoCD API server
|
||||
func NewServer(opts ArgoCDServerOpts) *ArgoCDServer {
|
||||
configManager := config.NewConfigManager(opts.KubeClientset, opts.Namespace)
|
||||
settings, err := configManager.GetSettings()
|
||||
settingsMgr := settings_util.NewSettingsManager(opts.KubeClientset, opts.Namespace)
|
||||
settings, err := settingsMgr.GetSettings()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
sessionMgr := util_session.MakeSessionManager(settings.ServerSignature)
|
||||
return &ArgoCDServer{
|
||||
ArgoCDServerOpts: opts,
|
||||
log: log.NewEntry(log.New()),
|
||||
settings: *settings,
|
||||
sessionMgr: &sessionMgr,
|
||||
settingsMgr: settingsMgr,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,24 +135,57 @@ func (a *ArgoCDServer) Run() {
|
||||
}
|
||||
tlsl = tls.NewListener(tlsl, &tlsConfig)
|
||||
|
||||
// Now, we build another mux recursively to match HTTPS and GoRPC.
|
||||
// Now, we build another mux recursively to match HTTPS and gRPC.
|
||||
tlsm = cmux.New(tlsl)
|
||||
httpsL = tlsm.Match(cmux.HTTP1Fast())
|
||||
grpcL = tlsm.Match(cmux.Any())
|
||||
}
|
||||
|
||||
// Start the muxed listeners for our servers
|
||||
log.Infof("argocd %s serving on port %d (tls: %v, namespace: %s)", argocd.GetVersion(), port, a.useTLS(), a.Namespace)
|
||||
log.Infof("argocd %s serving on port %d (url: %s, tls: %v, namespace: %s, sso: %v)",
|
||||
argocd.GetVersion(), port, a.settings.URL, a.useTLS(), a.Namespace, a.settings.IsSSOConfigured())
|
||||
go func() { errors.CheckError(grpcS.Serve(grpcL)) }()
|
||||
go func() { errors.CheckError(httpS.Serve(httpL)) }()
|
||||
if a.useTLS() {
|
||||
go func() { errors.CheckError(httpsS.Serve(httpsL)) }()
|
||||
go func() { errors.CheckError(tlsm.Serve()) }()
|
||||
}
|
||||
go a.initializeOIDCClientApp()
|
||||
err = tcpm.Serve()
|
||||
errors.CheckError(err)
|
||||
}
|
||||
|
||||
// initializeOIDCClientApp initializes the OIDC Client application, querying the well known oidc
|
||||
// configuration path. Because ArgoCD is a OIDC client to itself, we have a chicken-and-egg problem
|
||||
// of (1) serving dex over HTTP, and (2) querying the OIDC provider (ourselves) to initialize the
|
||||
// app (HTTP GET http://example-argocd.com/api/dex/.well-known/openid-configuration)
|
||||
// This method is expected to be invoked right after we start listening over HTTP
|
||||
func (a *ArgoCDServer) initializeOIDCClientApp() {
|
||||
if !a.settings.IsSSOConfigured() {
|
||||
return
|
||||
}
|
||||
// wait for dex to become ready
|
||||
dexClient, err := dexutil.NewDexClient()
|
||||
errors.CheckError(err)
|
||||
dexClient.WaitUntilReady()
|
||||
var backoff = wait.Backoff{
|
||||
Steps: 5,
|
||||
Duration: 1 * time.Second,
|
||||
Factor: 1.0,
|
||||
Jitter: 0.1,
|
||||
}
|
||||
var realErr error
|
||||
_ = wait.ExponentialBackoff(backoff, func() (bool, error) {
|
||||
realErr = a.ssoClientApp.Initialize()
|
||||
if realErr != nil {
|
||||
a.log.Warnf("failed to initialize client app: %v", realErr)
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
errors.CheckError(realErr)
|
||||
}
|
||||
|
||||
func (a *ArgoCDServer) useTLS() bool {
|
||||
if a.Insecure || a.settings.Certificate == nil {
|
||||
return false
|
||||
@@ -167,35 +213,29 @@ func (a *ArgoCDServer) newGRPCServer() *grpc.Server {
|
||||
grpcS := grpc.NewServer(sOpts...)
|
||||
clusterService := cluster.NewServer(a.Namespace, a.KubeClientset, a.AppClientset)
|
||||
repoService := repository.NewServer(a.Namespace, a.KubeClientset, a.AppClientset)
|
||||
sessionService := session.NewServer(a.Namespace, a.KubeClientset, a.AppClientset, a.settings)
|
||||
sessionService := session.NewServer(a.settings)
|
||||
applicationService := application.NewServer(a.Namespace, a.KubeClientset, a.AppClientset, a.RepoClientset, repoService, clusterService)
|
||||
settingsService := settings.NewServer(a.settingsMgr)
|
||||
version.RegisterVersionServiceServer(grpcS, &version.Server{})
|
||||
cluster.RegisterClusterServiceServer(grpcS, clusterService)
|
||||
application.RegisterApplicationServiceServer(grpcS, applicationService)
|
||||
repository.RegisterRepositoryServiceServer(grpcS, repoService)
|
||||
session.RegisterSessionServiceServer(grpcS, sessionService)
|
||||
settings.RegisterSettingsServiceServer(grpcS, settingsService)
|
||||
|
||||
// Register reflection service on gRPC server.
|
||||
reflection.Register(grpcS)
|
||||
return grpcS
|
||||
}
|
||||
|
||||
// MakeCookieMetadata generates a string representing a Web cookie. Yum!
|
||||
func (a *ArgoCDServer) makeCookieMetadata(key, value string, flags ...string) string {
|
||||
components := []string{
|
||||
fmt.Sprintf("%s=%s", key, value),
|
||||
}
|
||||
if a.ArgoCDServerOpts.Insecure == false {
|
||||
components = append(components, "Secure")
|
||||
}
|
||||
components = append(components, flags...)
|
||||
return strings.Join(components, "; ")
|
||||
}
|
||||
|
||||
// TranslateGrpcCookieHeader conditionally sets a cookie on the response.
|
||||
func (a *ArgoCDServer) translateGrpcCookieHeader(ctx context.Context, w http.ResponseWriter, resp golang_proto.Message) error {
|
||||
if sessionResp, ok := resp.(*session.SessionResponse); ok {
|
||||
cookie := a.makeCookieMetadata(authCookieName, sessionResp.Token, "path=/")
|
||||
flags := []string{"path=/"}
|
||||
if !a.Insecure {
|
||||
flags = append(flags, "Secure")
|
||||
}
|
||||
cookie := util_session.MakeCookieMetadata(common.AuthCookieName, sessionResp.Token, flags...)
|
||||
w.Header().Set("Set-Cookie", cookie)
|
||||
}
|
||||
return nil
|
||||
@@ -215,16 +255,9 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context) *http.Server {
|
||||
// grpc-gateway is just translating HTTP/HTTPS requests as gRPC requests over localhost,
|
||||
// so we need to supply the same certificates to establish the connections that a normal,
|
||||
// external gRPC client would need.
|
||||
certPool := x509.NewCertPool()
|
||||
pemCertBytes, _ := tlsutil.EncodeX509KeyPair(*a.settings.Certificate)
|
||||
ok := certPool.AppendCertsFromPEM(pemCertBytes)
|
||||
if !ok {
|
||||
panic("bad certs")
|
||||
}
|
||||
dCreds := credentials.NewTLS(&tls.Config{
|
||||
RootCAs: certPool,
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
tlsConfig := a.selfTLSConfig()
|
||||
tlsConfig.InsecureSkipVerify = true
|
||||
dCreds := credentials.NewTLS(tlsConfig)
|
||||
dOpts = append(dOpts, grpc.WithTransportCredentials(dCreds))
|
||||
} else {
|
||||
dOpts = append(dOpts, grpc.WithInsecure())
|
||||
@@ -245,6 +278,9 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context) *http.Server {
|
||||
mustRegisterGWHandler(application.RegisterApplicationServiceHandlerFromEndpoint, ctx, gwmux, endpoint, dOpts)
|
||||
mustRegisterGWHandler(repository.RegisterRepositoryServiceHandlerFromEndpoint, ctx, gwmux, endpoint, dOpts)
|
||||
mustRegisterGWHandler(session.RegisterSessionServiceHandlerFromEndpoint, ctx, gwmux, endpoint, dOpts)
|
||||
mustRegisterGWHandler(settings.RegisterSettingsServiceHandlerFromEndpoint, ctx, gwmux, endpoint, dOpts)
|
||||
|
||||
a.registerDexHandlers(mux)
|
||||
|
||||
if a.StaticAssetsDir != "" {
|
||||
mux.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
|
||||
@@ -268,6 +304,35 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context) *http.Server {
|
||||
return &httpS
|
||||
}
|
||||
|
||||
// selfTLSConfig returns a tls.Config with the configured certificates
|
||||
func (a *ArgoCDServer) selfTLSConfig() *tls.Config {
|
||||
certPool := x509.NewCertPool()
|
||||
pemCertBytes, _ := tlsutil.EncodeX509KeyPair(*a.settings.Certificate)
|
||||
ok := certPool.AppendCertsFromPEM(pemCertBytes)
|
||||
if !ok {
|
||||
panic("bad certs")
|
||||
}
|
||||
return &tls.Config{
|
||||
RootCAs: certPool,
|
||||
}
|
||||
}
|
||||
|
||||
// registerDexHandlers will register dex HTTP handlers, creating the the OAuth client app
|
||||
func (a *ArgoCDServer) registerDexHandlers(mux *http.ServeMux) {
|
||||
if !a.settings.IsSSOConfigured() {
|
||||
return
|
||||
}
|
||||
// Run dex OpenID Connect Identity Provider behind a reverse proxy (served at /api/dex)
|
||||
var err error
|
||||
mux.HandleFunc(dexutil.DexAPIEndpoint+"/", dexutil.NewDexHTTPReverseProxy())
|
||||
tlsConfig := a.selfTLSConfig()
|
||||
tlsConfig.InsecureSkipVerify = true
|
||||
a.ssoClientApp, err = dexutil.NewClientApp(a.settings.URL, a.settings.ServerSignature, tlsConfig)
|
||||
errors.CheckError(err)
|
||||
mux.HandleFunc(dexutil.LoginEndpoint, a.ssoClientApp.HandleLogin)
|
||||
mux.HandleFunc(dexutil.CallbackEndpoint, a.ssoClientApp.HandleCallback)
|
||||
}
|
||||
|
||||
// newRedirectServer returns an HTTP server which does a 307 redirect to the HTTPS server
|
||||
func newRedirectServer() *http.Server {
|
||||
return &http.Server{
|
||||
@@ -292,41 +357,45 @@ func mustRegisterGWHandler(register registerFunc, ctx context.Context, mux *runt
|
||||
}
|
||||
}
|
||||
|
||||
// parseTokens tests a slice of strings and returns `true` only if any of them are valid.
|
||||
func (a *ArgoCDServer) parseTokens(tokens []string) bool {
|
||||
mgr := util_session.MakeSessionManager(a.settings.ServerSignature)
|
||||
for _, token := range tokens {
|
||||
_, err := mgr.Parse(token)
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Authenticate checks for the presence of a token when accessing server-side resources.
|
||||
// Authenticate checks for the presence of a valid token when accessing server-side resources.
|
||||
func (a *ArgoCDServer) authenticate(ctx context.Context) (context.Context, error) {
|
||||
if a.DisableAuth {
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
if md, ok := metadata.FromIncomingContext(ctx); ok {
|
||||
tokens := md["tokens"]
|
||||
|
||||
// Extract only the value portion of cookie-stored tokens
|
||||
for _, cookieToken := range md["grpcgateway-cookie"] {
|
||||
tokenPair := strings.SplitN(cookieToken, "=", 2)
|
||||
if len(tokenPair) == 2 {
|
||||
tokens = append(tokens, tokenPair[1])
|
||||
}
|
||||
}
|
||||
|
||||
// Check both gRPC-provided tokens and Web-provided (cookie-based) ones
|
||||
if a.parseTokens(tokens) {
|
||||
return ctx, nil
|
||||
}
|
||||
return ctx, status.Errorf(codes.Unauthenticated, "user is not allowed access")
|
||||
md, ok := metadata.FromIncomingContext(ctx)
|
||||
if !ok {
|
||||
return ctx, ErrNoSession
|
||||
}
|
||||
|
||||
return ctx, status.Errorf(codes.Unauthenticated, "empty metadata")
|
||||
token := getToken(md)
|
||||
if token == "" {
|
||||
return ctx, ErrNoSession
|
||||
}
|
||||
_, err := a.sessionMgr.Parse(token)
|
||||
if err != nil {
|
||||
return ctx, fmt.Errorf("failed to validate auth token: %v", err)
|
||||
}
|
||||
// TODO: when we care about user groups, we will want to put the claims into the context
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
// getToken extracts the token from gRPC metadata or cookie headers
|
||||
func getToken(md metadata.MD) string {
|
||||
// check the "token" metadata
|
||||
tokens, ok := md[apiclient.MetaDataTokenKey]
|
||||
if ok && len(tokens) > 0 {
|
||||
return tokens[0]
|
||||
}
|
||||
// check the legacy key (v0.3.2 and below). 'tokens' was renamed to 'token'
|
||||
tokens, ok = md["tokens"]
|
||||
if ok && len(tokens) > 0 {
|
||||
return tokens[0]
|
||||
}
|
||||
// check the HTTP cookie
|
||||
for _, cookieToken := range md["grpcgateway-cookie"] {
|
||||
tokenPair := strings.SplitN(cookieToken, "=", 2)
|
||||
if len(tokenPair) == 2 && tokenPair[0] == common.AuthCookieName {
|
||||
return tokenPair[1]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -3,29 +3,21 @@ package session
|
||||
import (
|
||||
"context"
|
||||
|
||||
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
|
||||
"github.com/argoproj/argo-cd/util/config"
|
||||
"github.com/argoproj/argo-cd/util/password"
|
||||
"github.com/argoproj/argo-cd/util/session"
|
||||
"github.com/argoproj/argo-cd/util/settings"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
)
|
||||
|
||||
// Server provides a Session service
|
||||
type Server struct {
|
||||
ns string
|
||||
kubeclientset kubernetes.Interface
|
||||
appclientset appclientset.Interface
|
||||
serversettings config.ArgoCDSettings
|
||||
serversettings settings.ArgoCDSettings
|
||||
}
|
||||
|
||||
// NewServer returns a new instance of the Session service
|
||||
func NewServer(namespace string, kubeclientset kubernetes.Interface, appclientset appclientset.Interface, serversettings config.ArgoCDSettings) *Server {
|
||||
func NewServer(serversettings settings.ArgoCDSettings) *Server {
|
||||
return &Server{
|
||||
ns: namespace,
|
||||
appclientset: appclientset,
|
||||
kubeclientset: kubeclientset,
|
||||
serversettings: serversettings,
|
||||
}
|
||||
}
|
||||
|
||||
36
server/settings/settings.go
Normal file
36
server/settings/settings.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package settings
|
||||
|
||||
import (
|
||||
"github.com/argoproj/argo-cd/util/settings"
|
||||
"github.com/ghodss/yaml"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// Server provides a Settings service
|
||||
type Server struct {
|
||||
mgr *settings.SettingsManager
|
||||
}
|
||||
|
||||
// NewServer returns a new instance of the Repository service
|
||||
func NewServer(mgr *settings.SettingsManager) *Server {
|
||||
return &Server{
|
||||
mgr: mgr,
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns ArgoCD settings
|
||||
func (s *Server) Get(ctx context.Context, q *SettingsQuery) (*Settings, error) {
|
||||
argoCDSettings, err := s.mgr.GetSettings()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
set := Settings{
|
||||
URL: argoCDSettings.URL,
|
||||
}
|
||||
var cfg DexConfig
|
||||
err = yaml.Unmarshal([]byte(argoCDSettings.DexConfig), &cfg)
|
||||
if err == nil {
|
||||
set.DexConfig = &cfg
|
||||
}
|
||||
return &set, nil
|
||||
}
|
||||
859
server/settings/settings.pb.go
Normal file
859
server/settings/settings.pb.go
Normal file
@@ -0,0 +1,859 @@
|
||||
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||
// source: server/settings/settings.proto
|
||||
|
||||
/*
|
||||
Package settings is a generated protocol buffer package.
|
||||
|
||||
Settings Service
|
||||
|
||||
Settings Service API retrives ArgoCD settings
|
||||
|
||||
It is generated from these files:
|
||||
server/settings/settings.proto
|
||||
|
||||
It has these top-level messages:
|
||||
SettingsQuery
|
||||
Settings
|
||||
DexConfig
|
||||
Connector
|
||||
*/
|
||||
package settings
|
||||
|
||||
import proto "github.com/gogo/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
import _ "github.com/gogo/protobuf/gogoproto"
|
||||
import _ "google.golang.org/genproto/googleapis/api/annotations"
|
||||
|
||||
import context "golang.org/x/net/context"
|
||||
import grpc "google.golang.org/grpc"
|
||||
|
||||
import io "io"
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
// SettingsQuery is a query for ArgoCD settings
|
||||
type SettingsQuery struct {
|
||||
}
|
||||
|
||||
func (m *SettingsQuery) Reset() { *m = SettingsQuery{} }
|
||||
func (m *SettingsQuery) String() string { return proto.CompactTextString(m) }
|
||||
func (*SettingsQuery) ProtoMessage() {}
|
||||
func (*SettingsQuery) Descriptor() ([]byte, []int) { return fileDescriptorSettings, []int{0} }
|
||||
|
||||
type Settings struct {
|
||||
URL string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"`
|
||||
DexConfig *DexConfig `protobuf:"bytes,2,opt,name=dexConfig" json:"dexConfig,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Settings) Reset() { *m = Settings{} }
|
||||
func (m *Settings) String() string { return proto.CompactTextString(m) }
|
||||
func (*Settings) ProtoMessage() {}
|
||||
func (*Settings) Descriptor() ([]byte, []int) { return fileDescriptorSettings, []int{1} }
|
||||
|
||||
func (m *Settings) GetURL() string {
|
||||
if m != nil {
|
||||
return m.URL
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Settings) GetDexConfig() *DexConfig {
|
||||
if m != nil {
|
||||
return m.DexConfig
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type DexConfig struct {
|
||||
Connectors []*Connector `protobuf:"bytes,1,rep,name=connectors" json:"connectors,omitempty"`
|
||||
}
|
||||
|
||||
func (m *DexConfig) Reset() { *m = DexConfig{} }
|
||||
func (m *DexConfig) String() string { return proto.CompactTextString(m) }
|
||||
func (*DexConfig) ProtoMessage() {}
|
||||
func (*DexConfig) Descriptor() ([]byte, []int) { return fileDescriptorSettings, []int{2} }
|
||||
|
||||
func (m *DexConfig) GetConnectors() []*Connector {
|
||||
if m != nil {
|
||||
return m.Connectors
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Connector struct {
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Connector) Reset() { *m = Connector{} }
|
||||
func (m *Connector) String() string { return proto.CompactTextString(m) }
|
||||
func (*Connector) ProtoMessage() {}
|
||||
func (*Connector) Descriptor() ([]byte, []int) { return fileDescriptorSettings, []int{3} }
|
||||
|
||||
func (m *Connector) GetName() string {
|
||||
if m != nil {
|
||||
return m.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Connector) GetType() string {
|
||||
if m != nil {
|
||||
return m.Type
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*SettingsQuery)(nil), "cluster.SettingsQuery")
|
||||
proto.RegisterType((*Settings)(nil), "cluster.Settings")
|
||||
proto.RegisterType((*DexConfig)(nil), "cluster.DexConfig")
|
||||
proto.RegisterType((*Connector)(nil), "cluster.Connector")
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// Client API for SettingsService service
|
||||
|
||||
type SettingsServiceClient interface {
|
||||
// Get returns ArgoCD settings
|
||||
Get(ctx context.Context, in *SettingsQuery, opts ...grpc.CallOption) (*Settings, error)
|
||||
}
|
||||
|
||||
type settingsServiceClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewSettingsServiceClient(cc *grpc.ClientConn) SettingsServiceClient {
|
||||
return &settingsServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *settingsServiceClient) Get(ctx context.Context, in *SettingsQuery, opts ...grpc.CallOption) (*Settings, error) {
|
||||
out := new(Settings)
|
||||
err := grpc.Invoke(ctx, "/cluster.SettingsService/Get", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for SettingsService service
|
||||
|
||||
type SettingsServiceServer interface {
|
||||
// Get returns ArgoCD settings
|
||||
Get(context.Context, *SettingsQuery) (*Settings, error)
|
||||
}
|
||||
|
||||
func RegisterSettingsServiceServer(s *grpc.Server, srv SettingsServiceServer) {
|
||||
s.RegisterService(&_SettingsService_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _SettingsService_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SettingsQuery)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SettingsServiceServer).Get(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/cluster.SettingsService/Get",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SettingsServiceServer).Get(ctx, req.(*SettingsQuery))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _SettingsService_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "cluster.SettingsService",
|
||||
HandlerType: (*SettingsServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Get",
|
||||
Handler: _SettingsService_Get_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "server/settings/settings.proto",
|
||||
}
|
||||
|
||||
func (m *SettingsQuery) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalTo(dAtA)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *SettingsQuery) MarshalTo(dAtA []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (m *Settings) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalTo(dAtA)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *Settings) MarshalTo(dAtA []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.URL) > 0 {
|
||||
dAtA[i] = 0xa
|
||||
i++
|
||||
i = encodeVarintSettings(dAtA, i, uint64(len(m.URL)))
|
||||
i += copy(dAtA[i:], m.URL)
|
||||
}
|
||||
if m.DexConfig != nil {
|
||||
dAtA[i] = 0x12
|
||||
i++
|
||||
i = encodeVarintSettings(dAtA, i, uint64(m.DexConfig.Size()))
|
||||
n1, err := m.DexConfig.MarshalTo(dAtA[i:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i += n1
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (m *DexConfig) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalTo(dAtA)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *DexConfig) MarshalTo(dAtA []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.Connectors) > 0 {
|
||||
for _, msg := range m.Connectors {
|
||||
dAtA[i] = 0xa
|
||||
i++
|
||||
i = encodeVarintSettings(dAtA, i, uint64(msg.Size()))
|
||||
n, err := msg.MarshalTo(dAtA[i:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i += n
|
||||
}
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (m *Connector) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalTo(dAtA)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *Connector) MarshalTo(dAtA []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.Name) > 0 {
|
||||
dAtA[i] = 0xa
|
||||
i++
|
||||
i = encodeVarintSettings(dAtA, i, uint64(len(m.Name)))
|
||||
i += copy(dAtA[i:], m.Name)
|
||||
}
|
||||
if len(m.Type) > 0 {
|
||||
dAtA[i] = 0x12
|
||||
i++
|
||||
i = encodeVarintSettings(dAtA, i, uint64(len(m.Type)))
|
||||
i += copy(dAtA[i:], m.Type)
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func encodeVarintSettings(dAtA []byte, offset int, v uint64) int {
|
||||
for v >= 1<<7 {
|
||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||
v >>= 7
|
||||
offset++
|
||||
}
|
||||
dAtA[offset] = uint8(v)
|
||||
return offset + 1
|
||||
}
|
||||
func (m *SettingsQuery) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *Settings) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
l = len(m.URL)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovSettings(uint64(l))
|
||||
}
|
||||
if m.DexConfig != nil {
|
||||
l = m.DexConfig.Size()
|
||||
n += 1 + l + sovSettings(uint64(l))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *DexConfig) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.Connectors) > 0 {
|
||||
for _, e := range m.Connectors {
|
||||
l = e.Size()
|
||||
n += 1 + l + sovSettings(uint64(l))
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *Connector) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
l = len(m.Name)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovSettings(uint64(l))
|
||||
}
|
||||
l = len(m.Type)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovSettings(uint64(l))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func sovSettings(x uint64) (n int) {
|
||||
for {
|
||||
n++
|
||||
x >>= 7
|
||||
if x == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
func sozSettings(x uint64) (n int) {
|
||||
return sovSettings(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||
}
|
||||
func (m *SettingsQuery) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowSettings
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: SettingsQuery: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: SettingsQuery: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipSettings(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthSettings
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *Settings) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowSettings
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: Settings: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: Settings: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field URL", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowSettings
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthSettings
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.URL = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field DexConfig", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowSettings
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthSettings
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if m.DexConfig == nil {
|
||||
m.DexConfig = &DexConfig{}
|
||||
}
|
||||
if err := m.DexConfig.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipSettings(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthSettings
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *DexConfig) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowSettings
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: DexConfig: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: DexConfig: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Connectors", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowSettings
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthSettings
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Connectors = append(m.Connectors, &Connector{})
|
||||
if err := m.Connectors[len(m.Connectors)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipSettings(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthSettings
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *Connector) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowSettings
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: Connector: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: Connector: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowSettings
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthSettings
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Name = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowSettings
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthSettings
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Type = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipSettings(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthSettings
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func skipSettings(dAtA []byte) (n int, err error) {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowSettings
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
wireType := int(wire & 0x7)
|
||||
switch wireType {
|
||||
case 0:
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowSettings
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx++
|
||||
if dAtA[iNdEx-1] < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return iNdEx, nil
|
||||
case 1:
|
||||
iNdEx += 8
|
||||
return iNdEx, nil
|
||||
case 2:
|
||||
var length int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowSettings
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
length |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
iNdEx += length
|
||||
if length < 0 {
|
||||
return 0, ErrInvalidLengthSettings
|
||||
}
|
||||
return iNdEx, nil
|
||||
case 3:
|
||||
for {
|
||||
var innerWire uint64
|
||||
var start int = iNdEx
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowSettings
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
innerWire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
innerWireType := int(innerWire & 0x7)
|
||||
if innerWireType == 4 {
|
||||
break
|
||||
}
|
||||
next, err := skipSettings(dAtA[start:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
iNdEx = start + next
|
||||
}
|
||||
return iNdEx, nil
|
||||
case 4:
|
||||
return iNdEx, nil
|
||||
case 5:
|
||||
iNdEx += 4
|
||||
return iNdEx, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||
}
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidLengthSettings = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowSettings = fmt.Errorf("proto: integer overflow")
|
||||
)
|
||||
|
||||
func init() { proto.RegisterFile("server/settings/settings.proto", fileDescriptorSettings) }
|
||||
|
||||
var fileDescriptorSettings = []byte{
|
||||
// 322 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x91, 0x41, 0x4b, 0xc3, 0x40,
|
||||
0x10, 0x85, 0xd9, 0x46, 0xac, 0x19, 0x91, 0xea, 0x22, 0x12, 0x8b, 0xc4, 0x92, 0x53, 0x41, 0x4c,
|
||||
0xb4, 0x3d, 0x79, 0x12, 0x5a, 0x41, 0x10, 0x2f, 0xa6, 0x88, 0x20, 0x78, 0x48, 0xd3, 0x71, 0x8d,
|
||||
0xb4, 0x3b, 0x65, 0xb3, 0x29, 0xf6, 0xea, 0x5f, 0xf0, 0x4f, 0x79, 0x14, 0xbc, 0x8b, 0x04, 0x7f,
|
||||
0x88, 0x74, 0xdb, 0x44, 0xab, 0xb7, 0xc7, 0xf7, 0x66, 0x92, 0xb7, 0xf3, 0xc0, 0x4d, 0x51, 0x4d,
|
||||
0x50, 0x05, 0x29, 0x6a, 0x9d, 0x48, 0x91, 0x96, 0xc2, 0x1f, 0x2b, 0xd2, 0xc4, 0xab, 0xf1, 0x30,
|
||||
0x4b, 0x35, 0xaa, 0xfa, 0xb6, 0x20, 0x41, 0x86, 0x05, 0x33, 0x35, 0xb7, 0xeb, 0x7b, 0x82, 0x48,
|
||||
0x0c, 0x31, 0x88, 0xc6, 0x49, 0x10, 0x49, 0x49, 0x3a, 0xd2, 0x09, 0xc9, 0xc5, 0xb2, 0x57, 0x83,
|
||||
0x8d, 0xde, 0xe2, 0x73, 0x57, 0x19, 0xaa, 0xa9, 0x77, 0x03, 0x6b, 0x05, 0xe0, 0xbb, 0x60, 0x65,
|
||||
0x6a, 0xe8, 0xb0, 0x06, 0x6b, 0xda, 0x9d, 0x6a, 0xfe, 0xb1, 0x6f, 0x5d, 0x87, 0x97, 0xe1, 0x8c,
|
||||
0xf1, 0x23, 0xb0, 0x07, 0xf8, 0xd4, 0x25, 0x79, 0x9f, 0x08, 0xa7, 0xd2, 0x60, 0xcd, 0xf5, 0x16,
|
||||
0xf7, 0x17, 0x41, 0xfc, 0xb3, 0xc2, 0x09, 0x7f, 0x86, 0xbc, 0x53, 0xb0, 0x4b, 0xce, 0x5b, 0x00,
|
||||
0x31, 0x49, 0x89, 0xb1, 0x26, 0x95, 0x3a, 0xac, 0x61, 0x2d, 0xed, 0x77, 0x0b, 0x2b, 0xfc, 0x35,
|
||||
0xe5, 0xb5, 0xc1, 0x2e, 0x0d, 0xce, 0x61, 0x45, 0x46, 0x23, 0x9c, 0x67, 0x0b, 0x8d, 0x9e, 0x31,
|
||||
0x3d, 0x1d, 0xa3, 0x89, 0x63, 0x87, 0x46, 0xb7, 0xee, 0xa0, 0x56, 0x3c, 0xa7, 0x87, 0x6a, 0x92,
|
||||
0xc4, 0xc8, 0x2f, 0xc0, 0x3a, 0x47, 0xcd, 0x77, 0xca, 0xdf, 0x2d, 0x1d, 0xa0, 0xbe, 0xf5, 0x8f,
|
||||
0x7b, 0xce, 0xf3, 0xfb, 0xd7, 0x4b, 0x85, 0xf3, 0x4d, 0x73, 0xc4, 0xc9, 0x71, 0xd9, 0x40, 0xe7,
|
||||
0xe4, 0x35, 0x77, 0xd9, 0x5b, 0xee, 0xb2, 0xcf, 0xdc, 0x65, 0xb7, 0x07, 0x22, 0xd1, 0x0f, 0x59,
|
||||
0xdf, 0x8f, 0x69, 0x14, 0x44, 0xca, 0x74, 0xf1, 0x68, 0xc4, 0x61, 0x3c, 0x08, 0xfe, 0xb4, 0xd8,
|
||||
0x5f, 0x35, 0x05, 0xb4, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0xef, 0x0e, 0xd5, 0xb9, 0xdf, 0x01,
|
||||
0x00, 0x00,
|
||||
}
|
||||
116
server/settings/settings.pb.gw.go
Normal file
116
server/settings/settings.pb.gw.go
Normal file
@@ -0,0 +1,116 @@
|
||||
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
|
||||
// source: server/settings/settings.proto
|
||||
|
||||
/*
|
||||
Package settings is a reverse proxy.
|
||||
|
||||
It translates gRPC into RESTful JSON APIs.
|
||||
*/
|
||||
package settings
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/grpc-ecosystem/grpc-gateway/runtime"
|
||||
"github.com/grpc-ecosystem/grpc-gateway/utilities"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
var _ codes.Code
|
||||
var _ io.Reader
|
||||
var _ status.Status
|
||||
var _ = runtime.String
|
||||
var _ = utilities.NewDoubleArray
|
||||
|
||||
func request_SettingsService_Get_0(ctx context.Context, marshaler runtime.Marshaler, client SettingsServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq SettingsQuery
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
msg, err := client.Get(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
// RegisterSettingsServiceHandlerFromEndpoint is same as RegisterSettingsServiceHandler but
|
||||
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
|
||||
func RegisterSettingsServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
|
||||
conn, err := grpc.Dial(endpoint, opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if cerr := conn.Close(); cerr != nil {
|
||||
grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
|
||||
}
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
if cerr := conn.Close(); cerr != nil {
|
||||
grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
|
||||
}
|
||||
}()
|
||||
}()
|
||||
|
||||
return RegisterSettingsServiceHandler(ctx, mux, conn)
|
||||
}
|
||||
|
||||
// RegisterSettingsServiceHandler registers the http handlers for service SettingsService to "mux".
|
||||
// The handlers forward requests to the grpc endpoint over "conn".
|
||||
func RegisterSettingsServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
|
||||
return RegisterSettingsServiceHandlerClient(ctx, mux, NewSettingsServiceClient(conn))
|
||||
}
|
||||
|
||||
// RegisterSettingsServiceHandler registers the http handlers for service SettingsService to "mux".
|
||||
// The handlers forward requests to the grpc endpoint over the given implementation of "SettingsServiceClient".
|
||||
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "SettingsServiceClient"
|
||||
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
|
||||
// "SettingsServiceClient" to call the correct interceptors.
|
||||
func RegisterSettingsServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client SettingsServiceClient) error {
|
||||
|
||||
mux.Handle("GET", pattern_SettingsService_Get_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
if cn, ok := w.(http.CloseNotifier); ok {
|
||||
go func(done <-chan struct{}, closed <-chan bool) {
|
||||
select {
|
||||
case <-done:
|
||||
case <-closed:
|
||||
cancel()
|
||||
}
|
||||
}(ctx.Done(), cn.CloseNotify())
|
||||
}
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_SettingsService_Get_0(rctx, inboundMarshaler, client, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_SettingsService_Get_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
pattern_SettingsService_Get_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "settings"}, ""))
|
||||
)
|
||||
|
||||
var (
|
||||
forward_SettingsService_Get_0 = runtime.ForwardResponseMessage
|
||||
)
|
||||
38
server/settings/settings.proto
Normal file
38
server/settings/settings.proto
Normal file
@@ -0,0 +1,38 @@
|
||||
syntax = "proto3";
|
||||
option go_package = "github.com/argoproj/argo-cd/server/settings";
|
||||
|
||||
// Settings Service
|
||||
//
|
||||
// Settings Service API retrives ArgoCD settings
|
||||
package cluster;
|
||||
|
||||
import "gogoproto/gogo.proto";
|
||||
import "google/api/annotations.proto";
|
||||
|
||||
// SettingsQuery is a query for ArgoCD settings
|
||||
message SettingsQuery {
|
||||
}
|
||||
|
||||
message Settings {
|
||||
string url = 1 [(gogoproto.customname) = "URL"];;
|
||||
DexConfig dexConfig = 2;
|
||||
}
|
||||
|
||||
message DexConfig {
|
||||
repeated Connector connectors = 1;
|
||||
}
|
||||
|
||||
message Connector {
|
||||
string name = 1;
|
||||
string type = 2;
|
||||
}
|
||||
|
||||
// SettingsService
|
||||
service SettingsService {
|
||||
|
||||
// Get returns ArgoCD settings
|
||||
rpc Get(SettingsQuery) returns (Settings) {
|
||||
option (google.api.http).get = "/api/v1/settings";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
tlsutil "github.com/argoproj/argo-cd/util/tls"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
apierr "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
)
|
||||
|
||||
// ArgoCDSettings holds in-memory runtime configuration options.
|
||||
type ArgoCDSettings struct {
|
||||
// LocalUsers holds users local to (stored on) the server. This is to be distinguished from any potential alternative future login providers (LDAP, SAML, etc.) that might ever be added.
|
||||
LocalUsers map[string]string
|
||||
|
||||
// ServerSignature holds the key used to generate JWT tokens.
|
||||
ServerSignature []byte
|
||||
|
||||
// Certificate holds the certificate/private key for the ArgoCD API server.
|
||||
// If nil, will run insecure without TLS.
|
||||
Certificate *tls.Certificate
|
||||
}
|
||||
|
||||
const (
|
||||
// configManagerAdminPasswordKey designates the key for a root password inside a Kubernetes secret.
|
||||
configManagerAdminPasswordKey = "admin.password"
|
||||
|
||||
// configManagerServerSignatureKey designates the key for a server secret key inside a Kubernetes secret.
|
||||
configManagerServerSignatureKey = "server.secretkey"
|
||||
|
||||
// configManagerServerCertificate designates the key for the public cert used in TLS
|
||||
configManagerServerCertificate = "server.crt"
|
||||
|
||||
// configManagerServerPrivateKey designates the key for the private key used in TLS
|
||||
configManagerServerPrivateKey = "server.key"
|
||||
)
|
||||
|
||||
// ConfigManager holds config info for a new manager with which to access Kubernetes ConfigMaps.
|
||||
type ConfigManager struct {
|
||||
clientset kubernetes.Interface
|
||||
namespace string
|
||||
}
|
||||
|
||||
// GetSettings retrieves settings from the ConfigManager.
|
||||
func (mgr *ConfigManager) GetSettings() (*ArgoCDSettings, error) {
|
||||
// TODO: we currently do not store anything in configmaps, yet. We eventually will (e.g.
|
||||
// tuning parameters). Future settings/tunables should be stored here
|
||||
_, err := mgr.clientset.CoreV1().ConfigMaps(mgr.namespace).Get(common.ArgoCDConfigMapName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
argoCDSecret, err := mgr.clientset.CoreV1().Secrets(mgr.namespace).Get(common.ArgoCDSecretName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var settings ArgoCDSettings
|
||||
adminPasswordHash, ok := argoCDSecret.Data[configManagerAdminPasswordKey]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("admin user not found")
|
||||
}
|
||||
settings.LocalUsers = map[string]string{
|
||||
common.ArgoCDAdminUsername: string(adminPasswordHash),
|
||||
}
|
||||
secretKey, ok := argoCDSecret.Data[configManagerServerSignatureKey]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("server secret key not found")
|
||||
}
|
||||
settings.ServerSignature = secretKey
|
||||
|
||||
serverCert, certOk := argoCDSecret.Data[configManagerServerCertificate]
|
||||
serverKey, keyOk := argoCDSecret.Data[configManagerServerPrivateKey]
|
||||
if certOk && keyOk {
|
||||
cert, err := tls.X509KeyPair(serverCert, serverKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid x509 key pair %s/%s in secret: %s", configManagerServerCertificate, configManagerServerPrivateKey, err)
|
||||
}
|
||||
settings.Certificate = &cert
|
||||
}
|
||||
return &settings, nil
|
||||
}
|
||||
|
||||
// SaveSettings serializes ArgoCD settings and upserts it into K8s secret/configmap
|
||||
func (mgr *ConfigManager) SaveSettings(settings *ArgoCDSettings) error {
|
||||
configMapData := make(map[string]string)
|
||||
_, err := mgr.clientset.CoreV1().ConfigMaps(mgr.namespace).Get(common.ArgoCDConfigMapName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
if !apierr.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
newConfigMap := &apiv1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: common.ArgoCDConfigMapName,
|
||||
},
|
||||
Data: configMapData,
|
||||
}
|
||||
_, err = mgr.clientset.CoreV1().ConfigMaps(mgr.namespace).Create(newConfigMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// mgr.clientset.CoreV1().ConfigMaps(mgr.namespace).Update()
|
||||
}
|
||||
|
||||
secretStringData := map[string]string{
|
||||
configManagerServerSignatureKey: string(settings.ServerSignature),
|
||||
configManagerAdminPasswordKey: settings.LocalUsers[common.ArgoCDAdminUsername],
|
||||
}
|
||||
if settings.Certificate != nil {
|
||||
certBytes, keyBytes := tlsutil.EncodeX509KeyPair(*settings.Certificate)
|
||||
secretStringData[configManagerServerCertificate] = string(certBytes)
|
||||
secretStringData[configManagerServerPrivateKey] = string(keyBytes)
|
||||
}
|
||||
argoCDSecret, err := mgr.clientset.CoreV1().Secrets(mgr.namespace).Get(common.ArgoCDSecretName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
if !apierr.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
newSecret := &apiv1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: common.ArgoCDSecretName,
|
||||
},
|
||||
StringData: secretStringData,
|
||||
}
|
||||
_, err = mgr.clientset.CoreV1().Secrets(mgr.namespace).Create(newSecret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
argoCDSecret.Data = nil
|
||||
argoCDSecret.StringData = secretStringData
|
||||
_, err = mgr.clientset.CoreV1().Secrets(mgr.namespace).Update(argoCDSecret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewConfigManager generates a new ConfigManager pointer and returns it
|
||||
func NewConfigManager(clientset kubernetes.Interface, namespace string) *ConfigManager {
|
||||
return &ConfigManager{
|
||||
clientset: clientset,
|
||||
namespace: namespace,
|
||||
}
|
||||
}
|
||||
177
util/dex/config.go
Normal file
177
util/dex/config.go
Normal file
@@ -0,0 +1,177 @@
|
||||
package dex
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/util/settings"
|
||||
"github.com/ghodss/yaml"
|
||||
log "github.com/sirupsen/logrus"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
)
|
||||
|
||||
const (
|
||||
// DexClientAppName is name of the Oauth client app used when registering our app to dex
|
||||
DexClientAppName = "ArgoCD"
|
||||
// DexClientAppID is the Oauth client ID we will use when registering our app to dex
|
||||
DexClientAppID = "argo-cd"
|
||||
)
|
||||
|
||||
func GenerateDexConfigYAML(kubeClientset kubernetes.Interface, namespace string) ([]byte, error) {
|
||||
settingsMgr := settings.NewSettingsManager(kubeClientset, namespace)
|
||||
settings, err := settingsMgr.GetSettings()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !settings.IsSSOConfigured() {
|
||||
return nil, nil
|
||||
}
|
||||
var dexCfg map[string]interface{}
|
||||
err = yaml.Unmarshal([]byte(settings.DexConfig), &dexCfg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal dex.config from configmap: %v", err)
|
||||
}
|
||||
dexCfg["issuer"] = settings.URL + DexAPIEndpoint
|
||||
dexCfg["storage"] = map[string]interface{}{
|
||||
"type": "memory",
|
||||
}
|
||||
dexCfg["web"] = map[string]interface{}{
|
||||
"http": "0.0.0.0:5556",
|
||||
}
|
||||
dexCfg["grpc"] = map[string]interface{}{
|
||||
"addr": "0.0.0.0:5557",
|
||||
}
|
||||
dexCfg["oauth2"] = map[string]interface{}{
|
||||
"skipApprovalScreen": true,
|
||||
}
|
||||
dexCfg["staticClients"] = []map[string]interface{}{
|
||||
{
|
||||
"id": DexClientAppID,
|
||||
"name": DexClientAppName,
|
||||
"secret": formulateOAuthClientSecret(settings.ServerSignature),
|
||||
"redirectURIs": []string{
|
||||
settings.URL + CallbackEndpoint,
|
||||
},
|
||||
},
|
||||
}
|
||||
// dexCfg["enablePasswordDB"] = true
|
||||
// dexCfg["staticPasswords"] = []map[string]interface{}{
|
||||
// {
|
||||
// "userID": "00000000-0000-0000-0000-000000000001",
|
||||
// "username": "admin",
|
||||
// "email": "admin@internal",
|
||||
// "hash": settings.LocalUsers["admin"],
|
||||
// },
|
||||
// }
|
||||
connectors := dexCfg["connectors"].([]interface{})
|
||||
for i, connectorIf := range connectors {
|
||||
connector := connectorIf.(map[string]interface{})
|
||||
connectorType := connector["type"].(string)
|
||||
if !needsRedirectURI(connectorType) {
|
||||
continue
|
||||
}
|
||||
connectorCfg := connector["config"].(map[string]interface{})
|
||||
connectorCfg["redirectURI"] = settings.URL + "/api/dex/callback"
|
||||
connector["config"] = connectorCfg
|
||||
connectors[i] = connector
|
||||
}
|
||||
dexCfg["connectors"] = connectors
|
||||
|
||||
secretValues, err := getSecretValues(kubeClientset, namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dexCfg = replaceMapSecrets(dexCfg, secretValues)
|
||||
return yaml.Marshal(dexCfg)
|
||||
}
|
||||
|
||||
// formulateOAuthClientSecret calculates an arbitrary, but predictable OAuth2 client secret string
|
||||
// derived some seed input (typically the server secret). This is called by the dex startup wrapper
|
||||
// (argocd-util rundex), as well as the API server, such that they both independently come to the
|
||||
// same conclusion of what the OAuth2 shared client secret should be.
|
||||
func formulateOAuthClientSecret(in []byte) string {
|
||||
h := sha256.New()
|
||||
_, err := h.Write(in)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
sha := h.Sum(nil)
|
||||
return base64.URLEncoding.EncodeToString(sha)[:40]
|
||||
}
|
||||
|
||||
// getSecretValues is a convenience to get the ArgoCD secret data as a map[string]string
|
||||
func getSecretValues(kubeClientset kubernetes.Interface, namespace string) (map[string]string, error) {
|
||||
sec, err := kubeClientset.CoreV1().Secrets(namespace).Get(common.ArgoCDSecretName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
secretValues := make(map[string]string, len(sec.Data))
|
||||
for k, v := range sec.Data {
|
||||
secretValues[k] = string(v)
|
||||
}
|
||||
return secretValues, nil
|
||||
}
|
||||
|
||||
// replaceMapSecrets takes a json object and recursively looks for any secret key references in the
|
||||
// object and replaces the value with the secret value
|
||||
func replaceMapSecrets(obj map[string]interface{}, secretValues map[string]string) map[string]interface{} {
|
||||
newObj := make(map[string]interface{})
|
||||
for k, v := range obj {
|
||||
switch val := v.(type) {
|
||||
case map[string]interface{}:
|
||||
newObj[k] = replaceMapSecrets(val, secretValues)
|
||||
case []interface{}:
|
||||
newObj[k] = replaceListSecrets(val, secretValues)
|
||||
case string:
|
||||
newObj[k] = replaceStringSecret(val, secretValues)
|
||||
default:
|
||||
newObj[k] = val
|
||||
}
|
||||
}
|
||||
return newObj
|
||||
}
|
||||
|
||||
func replaceListSecrets(obj []interface{}, secretValues map[string]string) []interface{} {
|
||||
newObj := make([]interface{}, len(obj))
|
||||
for i, v := range obj {
|
||||
switch val := v.(type) {
|
||||
case map[string]interface{}:
|
||||
newObj[i] = replaceMapSecrets(val, secretValues)
|
||||
case []interface{}:
|
||||
newObj[i] = replaceListSecrets(val, secretValues)
|
||||
case string:
|
||||
newObj[i] = replaceStringSecret(val, secretValues)
|
||||
default:
|
||||
newObj[i] = val
|
||||
}
|
||||
}
|
||||
return newObj
|
||||
}
|
||||
|
||||
func replaceStringSecret(val string, secretValues map[string]string) string {
|
||||
if val == "" || !strings.HasPrefix(val, "$") {
|
||||
return val
|
||||
}
|
||||
secretKey := val[1:]
|
||||
secretVal, ok := secretValues[secretKey]
|
||||
if !ok {
|
||||
log.Warnf("config referenced '%s', but key does not exist in secret", val)
|
||||
return val
|
||||
}
|
||||
return secretVal
|
||||
}
|
||||
|
||||
// needsRedirectURI returns whether or not the given connector type needs a redirectURI
|
||||
// Update this list as necessary, as new connectors are added
|
||||
// https://github.com/coreos/dex/tree/master/Documentation/connectors
|
||||
func needsRedirectURI(connectorType string) bool {
|
||||
switch connectorType {
|
||||
case "oidc", "saml", "microsoft", "linkedin", "gitlab", "github":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
323
util/dex/dex.go
Normal file
323
util/dex/dex.go
Normal file
@@ -0,0 +1,323 @@
|
||||
package dex
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
"github.com/argoproj/argo-cd/util/session"
|
||||
"github.com/coreos/dex/api"
|
||||
oidc "github.com/coreos/go-oidc"
|
||||
jwt "github.com/dgrijalva/jwt-go"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/oauth2"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
const (
|
||||
// DexReverseProxyAddr is the address of the Dex OIDC server, which we run a reverse proxy against
|
||||
DexReverseProxyAddr = "http://localhost:5556"
|
||||
// DexgRPCAPIAddr is the address to the Dex gRPC API server for managing dex. This is assumed to run
|
||||
// locally (as a sidecar)
|
||||
DexgRPCAPIAddr = "localhost:5557"
|
||||
// DexAPIEndpoint is the endpoint where we serve the Dex API server
|
||||
DexAPIEndpoint = "/api/dex"
|
||||
// LoginEndpoint is ArgoCD's shorthand login endpoint which redirects to dex's OAuth 2.0 provider's consent page
|
||||
LoginEndpoint = "/auth/login"
|
||||
// CallbackEndpoint is ArgoCD's final callback endpoint we reach after OAuth 2.0 login flow has been completed
|
||||
CallbackEndpoint = "/auth/callback"
|
||||
// envVarSSODebug is an environment variable to enable additional OAuth debugging in the API server
|
||||
envVarSSODebug = "ARGOCD_SSO_DEBUG"
|
||||
)
|
||||
|
||||
type DexAPIClient struct {
|
||||
api.DexClient
|
||||
}
|
||||
|
||||
// NewDexHTTPReverseProxy returns a reverse proxy to the DEX server. Dex is assumed to be configured
|
||||
// with the external issuer URL muxed to the same path configured in server.go. In other words, if
|
||||
// ArgoCD API server wants to proxy requests at /api/dex, then the dex config yaml issuer URL should
|
||||
// also be /api/dex (e.g. issuer: https://argocd.example.com/api/dex)
|
||||
func NewDexHTTPReverseProxy() func(writer http.ResponseWriter, request *http.Request) {
|
||||
target, err := url.Parse(DexReverseProxyAddr)
|
||||
errors.CheckError(err)
|
||||
proxy := httputil.NewSingleHostReverseProxy(target)
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
proxy.ServeHTTP(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func NewDexClient() (*DexAPIClient, error) {
|
||||
conn, err := grpc.Dial(DexgRPCAPIAddr, grpc.WithInsecure())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to dial %s: %v", DexgRPCAPIAddr, err)
|
||||
}
|
||||
apiClient := DexAPIClient{
|
||||
api.NewDexClient(conn),
|
||||
}
|
||||
return &apiClient, nil
|
||||
}
|
||||
|
||||
// WaitUntilReady waits until the dex gRPC server is responding
|
||||
func (d *DexAPIClient) WaitUntilReady() {
|
||||
log.Info("Waiting for dex to become ready")
|
||||
ctx := context.Background()
|
||||
for {
|
||||
vers, err := d.GetVersion(ctx, &api.VersionReq{})
|
||||
if err == nil {
|
||||
log.Infof("Dex %s (API: %d) up and running", vers.Server, vers.Api)
|
||||
return
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: implement proper state management
|
||||
const exampleAppState = "I wish to wash my irish wristwatch"
|
||||
|
||||
type ClientApp struct {
|
||||
// OAuth2 client ID of this application (e.g. argo-cd)
|
||||
clientID string
|
||||
// OAuth2 client secret of this application
|
||||
clientSecret string
|
||||
// Callback URL for OAuth2 responses (e.g. https://argocd.example.com/auth/callback)
|
||||
redirectURI string
|
||||
// URL of the issuer (e.g. https://argocd.example.com/api/dex)
|
||||
issuerURL string
|
||||
|
||||
Path string
|
||||
|
||||
verifier *oidc.IDTokenVerifier
|
||||
provider *oidc.Provider
|
||||
|
||||
// Does the provider use "offline_access" scope to request a refresh token
|
||||
// or does it use "access_type=offline" (e.g. Google)?
|
||||
offlineAsScope bool
|
||||
|
||||
client *http.Client
|
||||
|
||||
// sessionMgr creates and validates sessions
|
||||
sessionMgr session.SessionManager
|
||||
|
||||
// secureCookie indicates if the cookie should be set with the Secure flag, meaning it should
|
||||
// only ever be sent over HTTPS. This value is inferred by the scheme of the redirectURI.
|
||||
secureCookie bool
|
||||
}
|
||||
|
||||
// NewClientApp will register the ArgoCD client app in Dex and return an object which has HTTP
|
||||
// handlers for handling the HTTP responses for login and callback
|
||||
func NewClientApp(clientBaseURL string, serverSecretKey []byte, tlsConfig *tls.Config) (*ClientApp, error) {
|
||||
redirectURI := clientBaseURL + CallbackEndpoint
|
||||
issuerURL := clientBaseURL + DexAPIEndpoint
|
||||
log.Infof("Creating client app (redirectURI: %s, issuerURL: %s)", redirectURI, issuerURL)
|
||||
a := ClientApp{
|
||||
clientID: DexClientAppID,
|
||||
clientSecret: formulateOAuthClientSecret(serverSecretKey),
|
||||
redirectURI: redirectURI,
|
||||
issuerURL: issuerURL,
|
||||
}
|
||||
u, err := url.Parse(redirectURI)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse redirect-uri: %v", err)
|
||||
}
|
||||
a.Path = u.Path
|
||||
a.secureCookie = bool(u.Scheme == "https")
|
||||
|
||||
a.client = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: tlsConfig,
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
},
|
||||
}
|
||||
if os.Getenv(envVarSSODebug) == "1" {
|
||||
a.client.Transport = debugTransport{a.client.Transport}
|
||||
}
|
||||
a.sessionMgr = session.MakeSessionManager(serverSecretKey)
|
||||
return &a, nil
|
||||
}
|
||||
|
||||
type debugTransport struct {
|
||||
t http.RoundTripper
|
||||
}
|
||||
|
||||
func (d debugTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
reqDump, err := httputil.DumpRequest(req, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Printf("%s", reqDump)
|
||||
|
||||
resp, err := d.t.RoundTrip(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
respDump, err := httputil.DumpResponse(resp, true)
|
||||
if err != nil {
|
||||
_ = resp.Body.Close()
|
||||
return nil, err
|
||||
}
|
||||
log.Printf("%s", respDump)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// Initialize initializes the client app. The OIDC provider must be running
|
||||
func (a *ClientApp) Initialize() error {
|
||||
log.Info("Initializing client app")
|
||||
ctx := oidc.ClientContext(context.Background(), a.client)
|
||||
provider, err := oidc.NewProvider(ctx, a.issuerURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to query provider %q: %v", a.issuerURL, err)
|
||||
}
|
||||
var s struct {
|
||||
// What scopes does a provider support?
|
||||
// See: https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
|
||||
ScopesSupported []string `json:"scopes_supported"`
|
||||
}
|
||||
if err := provider.Claims(&s); err != nil {
|
||||
return fmt.Errorf("Failed to parse provider scopes_supported: %v", err)
|
||||
}
|
||||
log.Infof("OpenID supported scopes: %v", s.ScopesSupported)
|
||||
|
||||
a.provider = provider
|
||||
a.verifier = provider.Verifier(&oidc.Config{ClientID: a.clientID})
|
||||
if len(s.ScopesSupported) == 0 {
|
||||
// scopes_supported is a "RECOMMENDED" discovery claim, not a required
|
||||
// one. If missing, assume that the provider follows the spec and has
|
||||
// an "offline_access" scope.
|
||||
a.offlineAsScope = true
|
||||
} else {
|
||||
// See if scopes_supported has the "offline_access" scope.
|
||||
a.offlineAsScope = func() bool {
|
||||
for _, scope := range s.ScopesSupported {
|
||||
if scope == oidc.ScopeOfflineAccess {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *ClientApp) oauth2Config(scopes []string) *oauth2.Config {
|
||||
return &oauth2.Config{
|
||||
ClientID: a.clientID,
|
||||
ClientSecret: a.clientSecret,
|
||||
Endpoint: a.provider.Endpoint(),
|
||||
Scopes: scopes,
|
||||
RedirectURL: a.redirectURI,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *ClientApp) HandleLogin(w http.ResponseWriter, r *http.Request) {
|
||||
var authCodeURL string
|
||||
scopes := []string{"openid", "profile", "email", "groups"}
|
||||
if r.FormValue("offline_access") != "yes" {
|
||||
authCodeURL = a.oauth2Config(scopes).AuthCodeURL(exampleAppState)
|
||||
} else if a.offlineAsScope {
|
||||
scopes = append(scopes, "offline_access")
|
||||
authCodeURL = a.oauth2Config(scopes).AuthCodeURL(exampleAppState)
|
||||
} else {
|
||||
authCodeURL = a.oauth2Config(scopes).AuthCodeURL(exampleAppState, oauth2.AccessTypeOffline)
|
||||
}
|
||||
http.Redirect(w, r, authCodeURL, http.StatusSeeOther)
|
||||
}
|
||||
|
||||
func (a *ClientApp) HandleCallback(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
err error
|
||||
token *oauth2.Token
|
||||
)
|
||||
|
||||
ctx := oidc.ClientContext(r.Context(), a.client)
|
||||
oauth2Config := a.oauth2Config(nil)
|
||||
switch r.Method {
|
||||
case "GET":
|
||||
// Authorization redirect callback from OAuth2 auth flow.
|
||||
if errMsg := r.FormValue("error"); errMsg != "" {
|
||||
http.Error(w, errMsg+": "+r.FormValue("error_description"), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
code := r.FormValue("code")
|
||||
if code == "" {
|
||||
http.Error(w, fmt.Sprintf("no code in request: %q", r.Form), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if state := r.FormValue("state"); state != exampleAppState {
|
||||
http.Error(w, fmt.Sprintf("expected state %q got %q", exampleAppState, state), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
token, err = oauth2Config.Exchange(ctx, code)
|
||||
case "POST":
|
||||
// Form request from frontend to refresh a token.
|
||||
refresh := r.FormValue("refresh_token")
|
||||
if refresh == "" {
|
||||
http.Error(w, fmt.Sprintf("no refresh_token in request: %q", r.Form), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
t := &oauth2.Token{
|
||||
RefreshToken: refresh,
|
||||
Expiry: time.Now().Add(-time.Hour),
|
||||
}
|
||||
token, err = oauth2Config.TokenSource(ctx, t).Token()
|
||||
default:
|
||||
http.Error(w, fmt.Sprintf("method not implemented: %s", r.Method), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("failed to get token: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
rawIDToken, ok := token.Extra("id_token").(string)
|
||||
if !ok {
|
||||
http.Error(w, "no id_token in token response", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
idToken, err := a.verifier.Verify(r.Context(), rawIDToken)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Failed to verify ID token: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
var claims jwt.MapClaims
|
||||
err = idToken.Claims(&claims)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Failed to unmarshal claims: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
clientToken, err := a.sessionMgr.SignClaims(claims)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Failed to sign identity provider claims: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
flags := []string{"path=/"}
|
||||
if a.secureCookie {
|
||||
flags = append(flags, "Secure")
|
||||
}
|
||||
cookie := session.MakeCookieMetadata(common.AuthCookieName, clientToken, flags...)
|
||||
w.Header().Set("Set-Cookie", cookie)
|
||||
if os.Getenv(envVarSSODebug) == "1" {
|
||||
claimsJSON, _ := json.MarshalIndent(claims, "", " ")
|
||||
renderToken(w, a.redirectURI, rawIDToken, token.RefreshToken, claimsJSON)
|
||||
} else {
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
}
|
||||
}
|
||||
69
util/dex/templates.go
Normal file
69
util/dex/templates.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package dex
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type tokenTmplData struct {
|
||||
IDToken string
|
||||
RefreshToken string
|
||||
RedirectURL string
|
||||
Claims string
|
||||
}
|
||||
|
||||
var tokenTmpl = template.Must(template.New("token.html").Parse(`<html>
|
||||
<head>
|
||||
<style>
|
||||
/* make pre wrap */
|
||||
pre {
|
||||
white-space: pre-wrap; /* css-3 */
|
||||
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
|
||||
white-space: -pre-wrap; /* Opera 4-6 */
|
||||
white-space: -o-pre-wrap; /* Opera 7 */
|
||||
word-wrap: break-word; /* Internet Explorer 5.5+ */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p> Token: <pre><code>{{ .IDToken }}</code></pre></p>
|
||||
<p> Claims: <pre><code>{{ .Claims }}</code></pre></p>
|
||||
{{ if .RefreshToken }}
|
||||
<p> Refresh Token: <pre><code>{{ .RefreshToken }}</code></pre></p>
|
||||
<form action="{{ .RedirectURL }}" method="post">
|
||||
<input type="hidden" name="refresh_token" value="{{ .RefreshToken }}">
|
||||
<input type="submit" value="Redeem refresh token">
|
||||
</form>
|
||||
{{ end }}
|
||||
</body>
|
||||
</html>
|
||||
`))
|
||||
|
||||
func renderToken(w http.ResponseWriter, redirectURL, idToken, refreshToken string, claims []byte) {
|
||||
renderTemplate(w, tokenTmpl, tokenTmplData{
|
||||
IDToken: idToken,
|
||||
RefreshToken: refreshToken,
|
||||
RedirectURL: redirectURL,
|
||||
Claims: string(claims),
|
||||
})
|
||||
}
|
||||
|
||||
func renderTemplate(w http.ResponseWriter, tmpl *template.Template, data interface{}) {
|
||||
err := tmpl.Execute(w, data)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch err := err.(type) {
|
||||
case *template.Error:
|
||||
// An ExecError guarantees that Execute has not written to the underlying reader.
|
||||
log.Printf("Error rendering template %s: %s", tmpl.Name(), err)
|
||||
|
||||
// TODO(ericchiang): replace with better internal server error.
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
default:
|
||||
// An error with the underlying write, such as the connection being
|
||||
// dropped. Ignore for now.
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,7 @@ func (m *NativeGitClient) Init(repo string, repoPath string) error {
|
||||
// SetCredentials sets a local credentials file to connect to a remote git repository
|
||||
func (m *NativeGitClient) SetCredentials(repo string, username string, password string, sshPrivateKey string, repoPath string) error {
|
||||
if password != "" {
|
||||
log.Infof("Setting password credentials")
|
||||
gitCredentialsFile := path.Join(repoPath, ".git", "credentials")
|
||||
repoURL, err := url.ParseRequestURI(repo)
|
||||
if err != nil {
|
||||
@@ -60,6 +61,7 @@ func (m *NativeGitClient) SetCredentials(repo string, username string, password
|
||||
}
|
||||
}
|
||||
if sshPrivateKey != "" {
|
||||
log.Infof("Setting SSH credentials")
|
||||
sshPrivateKeyFile := path.Join(repoPath, ".git", "ssh-private-key")
|
||||
err := ioutil.WriteFile(sshPrivateKeyFile, []byte(sshPrivateKey), 0600)
|
||||
if err != nil {
|
||||
|
||||
@@ -2,7 +2,9 @@ package session
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
util_password "github.com/argoproj/argo-cd/util/password"
|
||||
@@ -23,12 +25,6 @@ const (
|
||||
blankPasswordError = "Blank passwords are not allowed"
|
||||
)
|
||||
|
||||
// SessionManagerTokenClaims holds claim metadata for a token.
|
||||
type SessionManagerTokenClaims struct {
|
||||
//Foo string `json:"foo"`
|
||||
jwt.StandardClaims
|
||||
}
|
||||
|
||||
// MakeSessionManager creates a new session manager with the given secret key.
|
||||
func MakeSessionManager(secretKey []byte) SessionManager {
|
||||
return SessionManager{
|
||||
@@ -41,43 +37,39 @@ func (mgr SessionManager) Create(subject string) (string, error) {
|
||||
// Create a new token object, specifying signing method and the claims
|
||||
// you would like it to contain.
|
||||
now := time.Now().Unix()
|
||||
claims := SessionManagerTokenClaims{
|
||||
//"bar",
|
||||
jwt.StandardClaims{
|
||||
//ExpiresAt: time.Date(2015, 10, 10, 12, 0, 0, 0, time.UTC).Unix(),
|
||||
IssuedAt: now,
|
||||
Issuer: sessionManagerClaimsIssuer,
|
||||
NotBefore: now,
|
||||
Subject: subject,
|
||||
},
|
||||
claims := jwt.StandardClaims{
|
||||
//ExpiresAt: time.Date(2015, 10, 10, 12, 0, 0, 0, time.UTC).Unix(),
|
||||
IssuedAt: now,
|
||||
Issuer: sessionManagerClaimsIssuer,
|
||||
NotBefore: now,
|
||||
Subject: subject,
|
||||
}
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
return mgr.SignClaims(claims)
|
||||
}
|
||||
|
||||
// Unix and get the complete encoded token as a string using the secret
|
||||
func (mgr SessionManager) SignClaims(claims jwt.Claims) (string, error) {
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
return token.SignedString(mgr.serverSecretKey)
|
||||
}
|
||||
|
||||
// Parse tries to parse the provided string and returns the token claims.
|
||||
func (mgr SessionManager) Parse(tokenString string) (*SessionManagerTokenClaims, error) {
|
||||
func (mgr SessionManager) Parse(tokenString string) (jwt.Claims, error) {
|
||||
// Parse takes the token string and a function for looking up the key. The latter is especially
|
||||
// useful if you use multiple keys for your application. The standard is to use 'kid' in the
|
||||
// head of the token to identify which key to use, but the parsed token (head and claims) is provided
|
||||
// to the callback, providing flexibility.
|
||||
token, err := jwt.ParseWithClaims(tokenString, &SessionManagerTokenClaims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
var claims jwt.MapClaims
|
||||
token, err := jwt.ParseWithClaims(tokenString, &claims, func(token *jwt.Token) (interface{}, error) {
|
||||
// Don't forget to validate the alg is what you expect:
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
|
||||
}
|
||||
|
||||
return mgr.serverSecretKey, nil
|
||||
})
|
||||
|
||||
if token != nil {
|
||||
if claims, ok := token.Claims.(*SessionManagerTokenClaims); ok && token.Valid {
|
||||
return claims, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, err
|
||||
return token.Claims, nil
|
||||
}
|
||||
|
||||
// MakeSignature generates a cryptographically-secure pseudo-random token, based on a given number of random bytes, for signing purposes.
|
||||
@@ -87,6 +79,8 @@ func MakeSignature(size int) ([]byte, error) {
|
||||
if err != nil {
|
||||
b = nil
|
||||
}
|
||||
// base64 encode it so signing key can be typed into validation utilities
|
||||
b = []byte(base64.StdEncoding.EncodeToString(b))
|
||||
return b, err
|
||||
}
|
||||
|
||||
@@ -116,3 +110,12 @@ func (mgr SessionManager) LoginLocalUser(username, password string, users map[st
|
||||
|
||||
return "", fmt.Errorf(invalidLoginError)
|
||||
}
|
||||
|
||||
// MakeCookieMetadata generates a string representing a Web cookie. Yum!
|
||||
func MakeCookieMetadata(key, value string, flags ...string) string {
|
||||
components := []string{
|
||||
fmt.Sprintf("%s=%s", key, value),
|
||||
}
|
||||
components = append(components, flags...)
|
||||
return strings.Join(components, "; ")
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package session
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
jwt "github.com/dgrijalva/jwt-go"
|
||||
)
|
||||
|
||||
func TestSessionManager(t *testing.T) {
|
||||
@@ -21,7 +23,8 @@ func TestSessionManager(t *testing.T) {
|
||||
t.Errorf("Could not parse token: %v", err)
|
||||
}
|
||||
|
||||
subject := claims.Subject
|
||||
mapClaims := *(claims.(*jwt.MapClaims))
|
||||
subject := mapClaims["sub"].(string)
|
||||
if subject != "argo" {
|
||||
t.Errorf("Token claim subject \"%s\" does not match expected subject \"%s\".", subject, defaultSubject)
|
||||
}
|
||||
|
||||
182
util/settings/settings.go
Normal file
182
util/settings/settings.go
Normal file
@@ -0,0 +1,182 @@
|
||||
package settings
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
tlsutil "github.com/argoproj/argo-cd/util/tls"
|
||||
"github.com/ghodss/yaml"
|
||||
log "github.com/sirupsen/logrus"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
apierr "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
)
|
||||
|
||||
// ArgoCDSettings holds in-memory runtime configuration options.
|
||||
type ArgoCDSettings struct {
|
||||
// URL is the externally facing URL users will visit to reach ArgoCD.
|
||||
// The value here is used when configuring SSO. Omitting this value will disable SSO.
|
||||
URL string
|
||||
|
||||
// DexConfig is contains portions of a dex config yaml
|
||||
DexConfig string
|
||||
|
||||
// LocalUsers holds users local to (stored on) the server. This is to be distinguished from any potential alternative future login providers (LDAP, SAML, etc.) that might ever be added.
|
||||
LocalUsers map[string]string
|
||||
|
||||
// ServerSignature holds the key used to generate JWT tokens.
|
||||
ServerSignature []byte
|
||||
|
||||
// Certificate holds the certificate/private key for the ArgoCD API server.
|
||||
// If nil, will run insecure without TLS.
|
||||
Certificate *tls.Certificate
|
||||
}
|
||||
|
||||
const (
|
||||
// settingAdminPasswordKey designates the key for a root password inside a Kubernetes secret.
|
||||
settingAdminPasswordKey = "admin.password"
|
||||
// settingServerSignatureKey designates the key for a server secret key inside a Kubernetes secret.
|
||||
settingServerSignatureKey = "server.secretkey"
|
||||
// settingServerCertificate designates the key for the public cert used in TLS
|
||||
settingServerCertificate = "server.crt"
|
||||
// settingServerPrivateKey designates the key for the private key used in TLS
|
||||
settingServerPrivateKey = "server.key"
|
||||
// settingURLKey designates the key where ArgoCDs external URL is set
|
||||
settingURLKey = "url"
|
||||
// settingDexConfigKey designates the key for the dex config
|
||||
settingDexConfigKey = "dex.config"
|
||||
)
|
||||
|
||||
// SettingsManager holds config info for a new manager with which to access Kubernetes ConfigMaps.
|
||||
type SettingsManager struct {
|
||||
clientset kubernetes.Interface
|
||||
namespace string
|
||||
}
|
||||
|
||||
// GetSettings retrieves settings from the ConfigManager.
|
||||
func (mgr *SettingsManager) GetSettings() (*ArgoCDSettings, error) {
|
||||
argoCDCM, err := mgr.clientset.CoreV1().ConfigMaps(mgr.namespace).Get(common.ArgoCDConfigMapName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
argoCDSecret, err := mgr.clientset.CoreV1().Secrets(mgr.namespace).Get(common.ArgoCDSecretName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var settings ArgoCDSettings
|
||||
settings.DexConfig = argoCDCM.Data[settingDexConfigKey]
|
||||
settings.URL = argoCDCM.Data[settingURLKey]
|
||||
|
||||
adminPasswordHash, ok := argoCDSecret.Data[settingAdminPasswordKey]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("admin user not found")
|
||||
}
|
||||
settings.LocalUsers = map[string]string{
|
||||
common.ArgoCDAdminUsername: string(adminPasswordHash),
|
||||
}
|
||||
secretKey, ok := argoCDSecret.Data[settingServerSignatureKey]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("server secret key not found")
|
||||
}
|
||||
settings.ServerSignature = secretKey
|
||||
|
||||
serverCert, certOk := argoCDSecret.Data[settingServerCertificate]
|
||||
serverKey, keyOk := argoCDSecret.Data[settingServerPrivateKey]
|
||||
if certOk && keyOk {
|
||||
cert, err := tls.X509KeyPair(serverCert, serverKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid x509 key pair %s/%s in secret: %s", settingServerCertificate, settingServerPrivateKey, err)
|
||||
}
|
||||
settings.Certificate = &cert
|
||||
}
|
||||
return &settings, nil
|
||||
}
|
||||
|
||||
// SaveSettings serializes ArgoCD settings and upserts it into K8s secret/configmap
|
||||
func (mgr *SettingsManager) SaveSettings(settings *ArgoCDSettings) error {
|
||||
// Upsert the config data
|
||||
argoCDCM, err := mgr.clientset.CoreV1().ConfigMaps(mgr.namespace).Get(common.ArgoCDConfigMapName, metav1.GetOptions{})
|
||||
createCM := false
|
||||
if err != nil {
|
||||
if !apierr.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
argoCDCM = &apiv1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: common.ArgoCDConfigMapName,
|
||||
},
|
||||
Data: make(map[string]string),
|
||||
}
|
||||
createCM = true
|
||||
}
|
||||
argoCDCM.Data[settingURLKey] = settings.URL
|
||||
argoCDCM.Data[settingDexConfigKey] = settings.DexConfig
|
||||
if createCM {
|
||||
_, err = mgr.clientset.CoreV1().ConfigMaps(mgr.namespace).Create(argoCDCM)
|
||||
} else {
|
||||
_, err = mgr.clientset.CoreV1().ConfigMaps(mgr.namespace).Update(argoCDCM)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Upsert the secret data. Ensure we do not delete any extra keys which user may have added
|
||||
argoCDSecret, err := mgr.clientset.CoreV1().Secrets(mgr.namespace).Get(common.ArgoCDSecretName, metav1.GetOptions{})
|
||||
createSecret := false
|
||||
if err != nil {
|
||||
if !apierr.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
argoCDSecret = &apiv1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: common.ArgoCDSecretName,
|
||||
},
|
||||
Data: make(map[string][]byte),
|
||||
}
|
||||
createSecret = true
|
||||
}
|
||||
argoCDSecret.StringData = make(map[string]string)
|
||||
argoCDSecret.StringData[settingServerSignatureKey] = string(settings.ServerSignature)
|
||||
argoCDSecret.StringData[settingAdminPasswordKey] = settings.LocalUsers[common.ArgoCDAdminUsername]
|
||||
if settings.Certificate != nil {
|
||||
certBytes, keyBytes := tlsutil.EncodeX509KeyPair(*settings.Certificate)
|
||||
argoCDSecret.StringData[settingServerCertificate] = string(certBytes)
|
||||
argoCDSecret.StringData[settingServerPrivateKey] = string(keyBytes)
|
||||
} else {
|
||||
delete(argoCDSecret.Data, settingServerCertificate)
|
||||
delete(argoCDSecret.Data, settingServerPrivateKey)
|
||||
}
|
||||
if createSecret {
|
||||
_, err = mgr.clientset.CoreV1().Secrets(mgr.namespace).Create(argoCDSecret)
|
||||
} else {
|
||||
_, err = mgr.clientset.CoreV1().Secrets(mgr.namespace).Update(argoCDSecret)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewSettingsManager generates a new SettingsManager pointer and returns it
|
||||
func NewSettingsManager(clientset kubernetes.Interface, namespace string) *SettingsManager {
|
||||
return &SettingsManager{
|
||||
clientset: clientset,
|
||||
namespace: namespace,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *ArgoCDSettings) IsSSOConfigured() bool {
|
||||
if a.URL == "" {
|
||||
return false
|
||||
}
|
||||
var dexCfg map[string]interface{}
|
||||
err := yaml.Unmarshal([]byte(a.DexConfig), &dexCfg)
|
||||
if err != nil {
|
||||
log.Warn("invalid dex yaml config")
|
||||
return false
|
||||
}
|
||||
return len(dexCfg) > 0
|
||||
}
|
||||
155
workflows/bluegreen.yaml
Normal file
155
workflows/bluegreen.yaml
Normal file
@@ -0,0 +1,155 @@
|
||||
# This example demonstrates a "blue-green" deployment
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Workflow
|
||||
metadata:
|
||||
generateName: k8s-bluegreen-
|
||||
spec:
|
||||
entrypoint: k8s-bluegreen
|
||||
arguments:
|
||||
parameters:
|
||||
- name: deployment-name
|
||||
- name: service-name
|
||||
- name: new-deployment-manifest
|
||||
templates:
|
||||
- name: k8s-bluegreen
|
||||
steps:
|
||||
# 1. Create a parallel Kubernetes deployment with tweaks to name and app name
|
||||
- - name: create-blue-deployment
|
||||
template: clone-deployment
|
||||
arguments:
|
||||
parameters:
|
||||
- name: green-deployment-name
|
||||
value: '{{workflow.parameters.deployment-name}}'
|
||||
- name: suffix
|
||||
value: blue
|
||||
|
||||
# 2. Wait for parallel deployment to become ready
|
||||
- - name: wait-for-blue-deployment
|
||||
template: wait-deployment-ready
|
||||
arguments:
|
||||
parameters:
|
||||
- name: deployment-name
|
||||
value: '{{steps.create-blue-deployment.outputs.parameters.blue-deployment-name}}'
|
||||
|
||||
# 3. Patch the named service to point to the parallel deployment app
|
||||
- - name: switch-service-to-blue-deployment
|
||||
template: patch-service
|
||||
arguments:
|
||||
parameters:
|
||||
- name: service-name
|
||||
value: '{{workflow.parameters.service-name}}'
|
||||
- name: app-name
|
||||
value: '{{steps.create-blue-deployment.outputs.parameters.blue-deployment-app-name}}'
|
||||
|
||||
# 4. Update the original deployment (receiving no traffic) with a new version
|
||||
- - name: create-green-deployment
|
||||
template: patch-deployment
|
||||
arguments:
|
||||
parameters:
|
||||
- name: deployment-manifest-data
|
||||
value: '{{workflow.parameters.new-deployment-manifest}}'
|
||||
|
||||
# 5. Wait for the original deployment, now updated, to become ready
|
||||
- - name: wait-for-green-deployment
|
||||
template: wait-deployment-ready
|
||||
arguments:
|
||||
parameters:
|
||||
- name: deployment-name
|
||||
value: '{{steps.create-green-deployment.outputs.parameters.green-deployment-name}}'
|
||||
|
||||
# 6. Patch the named service to point to the original, now updated app
|
||||
- - name: switch-service-to-green-deployment
|
||||
template: patch-service
|
||||
arguments:
|
||||
parameters:
|
||||
- name: service-name
|
||||
value: '{{workflow.parameters.service-name}}'
|
||||
- name: app-name
|
||||
value: '{{steps.create-green-deployment.outputs.parameters.green-deployment-app-name}}'
|
||||
|
||||
# 7. Remove the cloned deployment (no longer receiving traffic)
|
||||
- - name: delete-cloned-deployment
|
||||
template: delete-deployment
|
||||
arguments:
|
||||
parameters:
|
||||
- name: deployment-name
|
||||
value: '{{steps.create-blue-deployment.outputs.parameters.blue-deployment-name}}'
|
||||
|
||||
# end of steps
|
||||
|
||||
|
||||
|
||||
|
||||
- name: clone-deployment
|
||||
inputs:
|
||||
parameters:
|
||||
- name: green-deployment-name
|
||||
- name: suffix
|
||||
container:
|
||||
image: argoproj/argoexec:latest
|
||||
command: [sh, -c]
|
||||
args: ["
|
||||
kubectl get -o json deployments/{{inputs.parameters.green-deployment-name}} | jq -c '.metadata.name+=\"-{{inputs.parameters.suffix}}\" | (.metadata.labels.app, .spec.selector.matchLabels.app, .spec.template.metadata.labels.app) +=\"-{{inputs.parameters.suffix}}\"' | kubectl create -o json -f - > /tmp/blue-deployment;
|
||||
jq -j .metadata.name /tmp/blue-deployment > /tmp/blue-deployment-name;
|
||||
jq -j .spec.template.metadata.labels.app /tmp/blue-deployment > /tmp/blue-deployment-app-name
|
||||
"]
|
||||
outputs:
|
||||
parameters:
|
||||
- name: blue-deployment-name
|
||||
valueFrom:
|
||||
path: /tmp/blue-deployment-name
|
||||
- name: blue-deployment-app-name
|
||||
valueFrom:
|
||||
path: /tmp/blue-deployment-app-name
|
||||
|
||||
- name: patch-deployment
|
||||
inputs:
|
||||
parameters:
|
||||
- name: deployment-manifest-data
|
||||
container:
|
||||
image: argoproj/argoexec:latest
|
||||
command: [sh, -c]
|
||||
args: ["
|
||||
echo '{{inputs.parameters.deployment-manifest-data}}' | kubectl apply -o json -f - > /tmp/green-deployment;
|
||||
jq -j .metadata.name /tmp/green-deployment > /tmp/green-deployment-name;
|
||||
jq -j .spec.template.metadata.labels.app /tmp/green-deployment > /tmp/green-deployment-app-name
|
||||
"]
|
||||
outputs:
|
||||
parameters:
|
||||
- name: green-deployment-name
|
||||
valueFrom:
|
||||
path: /tmp/green-deployment-name
|
||||
- name: green-deployment-app-name
|
||||
valueFrom:
|
||||
path: /tmp/green-deployment-app-name
|
||||
|
||||
- name: wait-deployment-ready
|
||||
inputs:
|
||||
parameters:
|
||||
- name: deployment-name
|
||||
container:
|
||||
image: argoproj/argoexec:latest
|
||||
command: [sh, -c]
|
||||
args: ["kubectl rollout status --watch=true 'deployments/{{inputs.parameters.deployment-name}}'"]
|
||||
|
||||
- name: patch-service
|
||||
inputs:
|
||||
parameters:
|
||||
- name: service-name
|
||||
- name: app-name
|
||||
container:
|
||||
image: argoproj/argoexec:latest
|
||||
command: [sh, -c]
|
||||
args: ["kubectl patch service '{{inputs.parameters.service-name}}' -p '{\"spec\": {\"selector\": {\"app\": \"{{inputs.parameters.app-name}}\"}}}'"]
|
||||
|
||||
- name: delete-deployment
|
||||
inputs:
|
||||
parameters:
|
||||
- name: deployment-name
|
||||
resource:
|
||||
action: delete
|
||||
manifest: |
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{inputs.parameters.deployment-name}}
|
||||
Reference in New Issue
Block a user