mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-03-12 19:38:48 +01:00
Compare commits
71 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d876c7729 | ||
|
|
70465a0520 | ||
|
|
3c9845719f | ||
|
|
a36cc8946c | ||
|
|
9567b539d1 | ||
|
|
fdf9515de2 | ||
|
|
b320238487 | ||
|
|
7872a60499 | ||
|
|
5fea3846d1 | ||
|
|
86a4e0baaa | ||
|
|
917f1df250 | ||
|
|
11260f2476 | ||
|
|
38d20d0f04 | ||
|
|
938f40e817 | ||
|
|
5f9c8b862e | ||
|
|
d96d67bb9a | ||
|
|
19c3b87676 | ||
|
|
7d08ab4e2b | ||
|
|
efea09d216 | ||
|
|
2adaef547b | ||
|
|
d71bbf0d9a | ||
|
|
36b3b2b853 | ||
|
|
63dafa08cc | ||
|
|
afddbbe875 | ||
|
|
34811cafca | ||
|
|
8a2851169c | ||
|
|
1a85a2d805 | ||
|
|
cf2d00e1e0 | ||
|
|
266c948add | ||
|
|
dd564ee9dd | ||
|
|
b9d48cabb9 | ||
|
|
276e0674c3 | ||
|
|
f3c4a69327 | ||
|
|
1c60a69866 | ||
|
|
050f937a24 | ||
|
|
b24e478224 | ||
|
|
e34380ed76 | ||
|
|
338a1b826f | ||
|
|
b87c63c897 | ||
|
|
f6ed150bb7 | ||
|
|
d5c683bc76 | ||
|
|
3ac95f3f84 | ||
|
|
3be872ad32 | ||
|
|
80964a79b2 | ||
|
|
e719035ea5 | ||
|
|
f2bcf63b26 | ||
|
|
2c9843f1a0 | ||
|
|
0560406d81 | ||
|
|
db8083c657 | ||
|
|
fcc9f50b3f | ||
|
|
c1ffbad8d8 | ||
|
|
d7cdb1a5af | ||
|
|
6c41ce5e08 | ||
|
|
685a814f38 | ||
|
|
06b64047a4 | ||
|
|
8a90b32446 | ||
|
|
7e47b1ebae | ||
|
|
150b51a3ac | ||
|
|
0002f8db9e | ||
|
|
59ed50d230 | ||
|
|
820b4bac1a | ||
|
|
19c5ecdbfa | ||
|
|
66b0702c24 | ||
|
|
5b5dc0efc4 | ||
|
|
0febf05160 | ||
|
|
ee924bda6e | ||
|
|
ecfe571e75 | ||
|
|
173ecd9397 | ||
|
|
ba3db35ba0 | ||
|
|
074053dac7 | ||
|
|
6bc98f91b1 |
@@ -32,7 +32,7 @@ spec:
|
||||
value: "{{item}}"
|
||||
withItems:
|
||||
- dep ensure && make lint
|
||||
- dep ensure && make test
|
||||
- dep ensure && make test test-e2e
|
||||
|
||||
- name: ci-builder
|
||||
inputs:
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# Prevent vendor directory from being copied to ensure we are not not pulling unexpected cruft from
|
||||
# a user's workspace, and are only building off of what is locked by dep.
|
||||
vendor
|
||||
Dockerfile-argocd
|
||||
dist
|
||||
|
||||
@@ -39,22 +39,46 @@ RUN cd ${GOPATH}/src/dummy && \
|
||||
mv vendor/* ${GOPATH}/src/ && \
|
||||
rmdir vendor
|
||||
|
||||
ARG MAKE_TARGET
|
||||
|
||||
# Perform the build
|
||||
ARG MAKE_TARGET
|
||||
WORKDIR /root/go/src/github.com/argoproj/argo-cd
|
||||
COPY . .
|
||||
RUN make ${MAKE_TARGET}
|
||||
RUN wget https://github.com/ksonnet/ksonnet/releases/download/v0.9.1/ks_0.9.1_linux_amd64.tar.gz && tar -C /tmp/ -xf ks_0.9.1_linux_amd64.tar.gz
|
||||
|
||||
|
||||
##############################################################
|
||||
# This stage will pull in or build any CLI tooling we need for our final image
|
||||
|
||||
FROM golang:1.10 as cli-tooling
|
||||
|
||||
# NOTE: we frequently switch between tip of master ksonnet vs. official builds. Comment/uncomment
|
||||
# the corresponding section to switch between the two options:
|
||||
|
||||
# Option 1: build ksonnet ourselves
|
||||
#RUN go get -v -u github.com/ksonnet/ksonnet && mv ${GOPATH}/bin/ksonnet /ks
|
||||
|
||||
# Option 2: use official tagged ksonnet release
|
||||
env KSONNET_VERSION=0.10.1
|
||||
RUN wget https://github.com/ksonnet/ksonnet/releases/download/v${KSONNET_VERSION}/ks_${KSONNET_VERSION}_linux_amd64.tar.gz && \
|
||||
tar -C /tmp/ -xf ks_${KSONNET_VERSION}_linux_amd64.tar.gz && \
|
||||
mv /tmp/ks_${KSONNET_VERSION}_linux_amd64/ks /ks
|
||||
|
||||
RUN curl -o /kubectl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl && \
|
||||
chmod +x /kubectl
|
||||
|
||||
|
||||
##############################################################
|
||||
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=builder /tmp/ks_0.9.1_linux_amd64/ks /usr/local/bin/ks
|
||||
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
|
||||
|
||||
@@ -10,7 +10,12 @@ RUN curl -O https://get.docker.com/builds/Linux/x86_64/docker-1.13.1.tgz && \
|
||||
go get -u gopkg.in/alecthomas/gometalinter.v2 && \
|
||||
gometalinter.v2 --install
|
||||
|
||||
# Install kubectl
|
||||
RUN curl -o /usr/local/bin/kubectl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl
|
||||
|
||||
# Install ksonnet
|
||||
# TODO(jessesuen): pin this once we decide on a stable version
|
||||
RUN go get -v -u github.com/ksonnet/ksonnet && \
|
||||
mv ${GOPATH}/bin/ksonnet /usr/local/bin/ks
|
||||
env KSONNET_VERSION=0.10.1
|
||||
RUN wget https://github.com/ksonnet/ksonnet/releases/download/v${KSONNET_VERSION}/ks_${KSONNET_VERSION}_linux_amd64.tar.gz && \
|
||||
tar -C /tmp/ -xf ks_${KSONNET_VERSION}_linux_amd64.tar.gz && \
|
||||
mv /tmp/ks_${KSONNET_VERSION}_linux_amd64/ks /usr/local/bin/ks && \
|
||||
rm -rf /tmp/ks_${KSONNET_VERSION}
|
||||
|
||||
121
Gopkg.lock
generated
121
Gopkg.lock
generated
@@ -7,6 +7,14 @@
|
||||
revision = "767c40d6a2e058483c25fa193e963a22da17236d"
|
||||
version = "v0.18.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/GeertJohan/go.rice"
|
||||
packages = [
|
||||
".",
|
||||
"embedded"
|
||||
]
|
||||
revision = "c02ca9a983da5807ddf7d796784928f5be4afd09"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/PuerkitoBio/purell"
|
||||
packages = ["."]
|
||||
@@ -24,12 +32,24 @@
|
||||
revision = "2ee87856327ba09384cabd113bc6b5d174e9ec0f"
|
||||
version = "v3.5.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/daaku/go.zipexe"
|
||||
packages = ["."]
|
||||
revision = "a5fe2436ffcb3236e175e5149162b41cd28bd27d"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/davecgh/go-spew"
|
||||
packages = ["spew"]
|
||||
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/dgrijalva/jwt-go"
|
||||
packages = ["."]
|
||||
revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e"
|
||||
version = "v3.2.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/emicklei/go-restful"
|
||||
packages = [
|
||||
@@ -39,11 +59,6 @@
|
||||
revision = "26b41036311f2da8242db402557a0dbd09dc83da"
|
||||
version = "v2.6.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/fatih/color"
|
||||
packages = ["."]
|
||||
revision = "5df930a27be2502f99b292b7cc09ebad4d0891f4"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/ghodss/yaml"
|
||||
packages = ["."]
|
||||
@@ -106,6 +121,7 @@
|
||||
"protoc-gen-gogo/generator",
|
||||
"protoc-gen-gogo/grpc",
|
||||
"protoc-gen-gogo/plugin",
|
||||
"protoc-gen-gogofast",
|
||||
"sortkeys",
|
||||
"vanity",
|
||||
"vanity/command"
|
||||
@@ -120,6 +136,7 @@
|
||||
revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/golang/protobuf"
|
||||
packages = [
|
||||
"jsonpb",
|
||||
@@ -132,8 +149,7 @@
|
||||
"ptypes/struct",
|
||||
"ptypes/timestamp"
|
||||
]
|
||||
revision = "925541529c1fa6821df4e44ce2723319eb2be768"
|
||||
version = "v1.0.0"
|
||||
revision = "e09c5db296004fbe3f74490e84dcd62c3c5ddb1b"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@@ -141,24 +157,14 @@
|
||||
packages = ["."]
|
||||
revision = "e89373fe6b4a7413d7acd6da1725b83ef713e6e4"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/google/go-github"
|
||||
packages = ["github"]
|
||||
revision = "996760c56486beb81e91bb7bdb816f8c6f29284e"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/google/go-jsonnet"
|
||||
packages = [
|
||||
".",
|
||||
"ast",
|
||||
"parser"
|
||||
]
|
||||
revision = "405726fae23ace72b22c410a77b7bd825608f2c8"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/google/go-querystring"
|
||||
packages = ["query"]
|
||||
revision = "53e6ce116135b80d037921a7fdd5138cf32d7a8a"
|
||||
revision = "dfddf2b4e3aec377b0dcdf247ff92e7d078b8179"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@@ -186,15 +192,19 @@
|
||||
revision = "2bcd89a1743fd4b373f7370ce8ddc14dfbd18229"
|
||||
|
||||
[[projects]]
|
||||
branch = "bugfix/37-panic-on-err"
|
||||
branch = "master"
|
||||
name = "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
packages = [
|
||||
".",
|
||||
"auth",
|
||||
"logging",
|
||||
"logging/logrus",
|
||||
"tags"
|
||||
"logging/logrus/ctxlogrus",
|
||||
"tags",
|
||||
"tags/logrus",
|
||||
"util/metautils"
|
||||
]
|
||||
revision = "b9814e2370c8552db05c796c139b76a6ca45b34f"
|
||||
revision = "bc372cc64f55abd91995ba3f219b380ffbc59e9d"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/grpc-ecosystem/grpc-gateway"
|
||||
@@ -247,21 +257,28 @@
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/kardianos/osext"
|
||||
packages = ["."]
|
||||
revision = "ae77be60afb1dcacde03767a8c37337fad28ac14"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/ksonnet/ksonnet"
|
||||
packages = [
|
||||
"component",
|
||||
"env",
|
||||
"generator",
|
||||
"metadata",
|
||||
"metadata/app",
|
||||
"metadata/lib",
|
||||
"metadata/params",
|
||||
"metadata/parts",
|
||||
"metadata/registry",
|
||||
"prototype",
|
||||
"strings"
|
||||
"pkg/app",
|
||||
"pkg/component",
|
||||
"pkg/docparser",
|
||||
"pkg/lib",
|
||||
"pkg/node",
|
||||
"pkg/params",
|
||||
"pkg/prototype",
|
||||
"pkg/schema",
|
||||
"pkg/util/jsonnet",
|
||||
"pkg/util/kslib",
|
||||
"pkg/util/strings"
|
||||
]
|
||||
revision = "e570bff822d88b12f1841aa6e0069713d7e066c5"
|
||||
revision = "8c44a5b1545d3d03135f610170ef0167129294bc"
|
||||
version = "v0.10.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/ksonnet/ksonnet-lib"
|
||||
@@ -274,7 +291,8 @@
|
||||
"ksonnet-gen/nodemaker",
|
||||
"ksonnet-gen/printer"
|
||||
]
|
||||
revision = "9f27c242ff65da0620f8324cc0cd50c8ef00bf5b"
|
||||
revision = "d15220fdcdd07fd377894abff6276d86cb2d776d"
|
||||
version = "v0.1.3"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@@ -286,24 +304,6 @@
|
||||
]
|
||||
revision = "32fa128f234d041f196a9f3e0fea5ac9772c08e1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-colorable"
|
||||
packages = ["."]
|
||||
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
|
||||
version = "v0.0.9"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-isatty"
|
||||
packages = ["."]
|
||||
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
|
||||
version = "v0.0.3"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pborman/uuid"
|
||||
packages = ["."]
|
||||
revision = "e790cca94e6cc75c7064b1332e63811d4aae1a53"
|
||||
version = "v1.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/petar/GoLLRB"
|
||||
@@ -337,7 +337,8 @@
|
||||
[[projects]]
|
||||
name = "github.com/sirupsen/logrus"
|
||||
packages = ["."]
|
||||
revision = "3d4380f53a34dcdc95f0c1db702615992b38d9a4"
|
||||
revision = "c155da19408a8799da419ed3eeb0cb5db0ad5dbc"
|
||||
version = "v1.0.5"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/soheilhy/cmux"
|
||||
@@ -398,7 +399,11 @@
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = ["ssh/terminal"]
|
||||
packages = [
|
||||
"bcrypt",
|
||||
"blowfish",
|
||||
"ssh/terminal"
|
||||
]
|
||||
revision = "432090b8f568c018896cd8a0fb0345872bbac6ce"
|
||||
|
||||
[[projects]]
|
||||
@@ -518,6 +523,8 @@
|
||||
"metadata",
|
||||
"naming",
|
||||
"peer",
|
||||
"reflection",
|
||||
"reflection/grpc_reflection_v1alpha",
|
||||
"resolver",
|
||||
"resolver/dns",
|
||||
"resolver/passthrough",
|
||||
@@ -536,10 +543,9 @@
|
||||
version = "v0.9.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "v2"
|
||||
name = "gopkg.in/yaml.v2"
|
||||
packages = ["."]
|
||||
revision = "d670f9405373e636a5a2765eea47fac0c9bc91a4"
|
||||
revision = "eb3733d160e74a9c7e442f435eb3bea458e1d19f"
|
||||
|
||||
[[projects]]
|
||||
branch = "release-1.9"
|
||||
@@ -624,7 +630,6 @@
|
||||
"pkg/util/net",
|
||||
"pkg/util/runtime",
|
||||
"pkg/util/sets",
|
||||
"pkg/util/uuid",
|
||||
"pkg/util/validation",
|
||||
"pkg/util/validation/field",
|
||||
"pkg/util/wait",
|
||||
@@ -760,6 +765,6 @@
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "683981f86c65c4c2871048e1305c2468fb7dea01ec6de3dbff52c932d8c2f803"
|
||||
inputs-digest = "8e7142f84554c6f1665ef18e0fb906f82de8cd802b0211c4a46ec1ad228b8b7e"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
||||
13
Gopkg.toml
13
Gopkg.toml
@@ -1,5 +1,6 @@
|
||||
required = [
|
||||
"github.com/gogo/protobuf/protoc-gen-gofast",
|
||||
"github.com/gogo/protobuf/protoc-gen-gogofast",
|
||||
"golang.org/x/sync/errgroup",
|
||||
"k8s.io/code-generator/cmd/go-to-protobuf",
|
||||
]
|
||||
@@ -12,7 +13,8 @@ required = [
|
||||
name = "github.com/grpc-ecosystem/grpc-gateway"
|
||||
version = "v1.3.1"
|
||||
|
||||
[[constraint]]
|
||||
# override ksonnet's release-1.8 dependency
|
||||
[[override]]
|
||||
branch = "release-1.9"
|
||||
name = "k8s.io/apimachinery"
|
||||
|
||||
@@ -38,8 +40,9 @@ required = [
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/ksonnet/ksonnet"
|
||||
branch = "master"
|
||||
version = "v0.10.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/pborman/uuid"
|
||||
version = "1.1.0"
|
||||
# override ksonnet's logrus dependency
|
||||
[[override]]
|
||||
name = "github.com/sirupsen/logrus"
|
||||
version = "v1.0.3"
|
||||
|
||||
35
Makefile
35
Makefile
@@ -1,6 +1,7 @@
|
||||
PACKAGE=github.com/argoproj/argo-cd
|
||||
CURRENT_DIR=$(shell pwd)
|
||||
DIST_DIR=${CURRENT_DIR}/dist
|
||||
CLI_NAME=argocd
|
||||
|
||||
VERSION=$(shell cat ${CURRENT_DIR}/VERSION)
|
||||
BUILD_DATE=$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')
|
||||
@@ -57,7 +58,21 @@ codegen: protogen clientgen
|
||||
# This enables ease of maintenance of the yaml files.
|
||||
.PHONY: cli
|
||||
cli:
|
||||
CGO_ENABLED=0 ${PACKR_CMD} build -v -i -ldflags '${LDFLAGS} -extldflags "-static"' -o ${DIST_DIR}/argocd ./cmd/argocd
|
||||
CGO_ENABLED=0 ${PACKR_CMD} build -v -i -ldflags '${LDFLAGS} -extldflags "-static"' -o ${DIST_DIR}/${CLI_NAME} ./cmd/argocd
|
||||
|
||||
.PHONY: cli-linux
|
||||
cli-linux:
|
||||
docker build --iidfile /tmp/argocd-linux-id --target builder --build-arg MAKE_TARGET="cli IMAGE_TAG=$(IMAGE_TAG) IMAGE_NAMESPACE=$(IMAGE_NAMESPACE) CLI_NAME=argocd-linux-amd64" -f Dockerfile-argocd .
|
||||
docker create --name tmp-argocd-linux `cat /tmp/argocd-linux-id`
|
||||
docker cp tmp-argocd-linux:/root/go/src/github.com/argoproj/argo-cd/dist/argocd-linux-amd64 dist/
|
||||
docker rm tmp-argocd-linux
|
||||
|
||||
.PHONY: cli-darwin
|
||||
cli-darwin:
|
||||
docker build --iidfile /tmp/argocd-darwin-id --target builder --build-arg MAKE_TARGET="cli GOOS=darwin IMAGE_TAG=$(IMAGE_TAG) IMAGE_NAMESPACE=$(IMAGE_NAMESPACE) CLI_NAME=argocd-darwin-amd64" -f Dockerfile-argocd .
|
||||
docker create --name tmp-argocd-darwin `cat /tmp/argocd-darwin-id`
|
||||
docker cp tmp-argocd-darwin:/root/go/src/github.com/argoproj/argo-cd/dist/argocd-darwin-amd64 dist/
|
||||
docker rm tmp-argocd-darwin
|
||||
|
||||
.PHONY: server
|
||||
server:
|
||||
@@ -86,6 +101,11 @@ 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 .
|
||||
@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 .
|
||||
@if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)argocd-cli:$(IMAGE_TAG) ; fi
|
||||
|
||||
.PHONY: builder-image
|
||||
builder-image:
|
||||
docker build -t $(IMAGE_PREFIX)argo-cd-ci-builder:$(IMAGE_TAG) -f Dockerfile-ci-builder .
|
||||
@@ -96,7 +116,11 @@ lint:
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
go test ./...
|
||||
go test `go list ./... | grep -v "github.com/argoproj/argo-cd/test/e2e"`
|
||||
|
||||
.PHONY: test-e2e
|
||||
test-e2e:
|
||||
go test ./test/e2e
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@@ -105,3 +129,10 @@ clean:
|
||||
.PHONY: precheckin
|
||||
precheckin: test lint
|
||||
|
||||
.PHONY: release-precheck
|
||||
release-precheck:
|
||||
@if [ "$(GIT_TREE_STATE)" != "clean" ]; then echo 'git tree state is $(GIT_TREE_STATE)' ; exit 1; fi
|
||||
@if [ -z "$(GIT_TAG)" ]; then echo 'commit must be tagged to perform release' ; exit 1; fi
|
||||
|
||||
.PHONY: release
|
||||
release: release-precheck precheckin cli-darwin cli-linux server-image controller-image repo-server-image cli-image
|
||||
|
||||
2
Procfile
2
Procfile
@@ -1,3 +1,3 @@
|
||||
controller: go run ./cmd/argocd-application-controller/main.go --app-resync 10
|
||||
api-server: go run ./cmd/argocd-server/main.go
|
||||
api-server: go run ./cmd/argocd-server/main.go --insecure
|
||||
repo-server: go run ./cmd/argocd-repo-server/main.go
|
||||
|
||||
@@ -12,7 +12,7 @@ Application deployment and lifecycle management should be automated, auditable,
|
||||
|
||||
## Getting Started
|
||||
|
||||
Follow our [getting started guide](docs/GETTING_STARTED.md).
|
||||
Follow our [getting started guide](docs/getting_started.md).
|
||||
|
||||
## How it works
|
||||
|
||||
@@ -23,6 +23,10 @@ application states in the specified target environments.
|
||||
|
||||

|
||||
|
||||
Application deployments can track updates to branches, tags, or pinned to a specific version of
|
||||
manifests at a git commit. See [tracking strategies](docs/tracking_strategies.md) for additional
|
||||
details about the different tracking strategies available.
|
||||
|
||||
Argo CD is implemented as a kubernetes controller which continuously monitors running applications
|
||||
and compares the current, live state against the desired target state (as specified in the git repo).
|
||||
A deployed application whose live state deviates from the target state is considered out-of-sync.
|
||||
@@ -31,7 +35,7 @@ manually sync the live state back to the desired target state. Any modifications
|
||||
target state in the git repo can be automatically applied and reflected in the specified target
|
||||
environments.
|
||||
|
||||
For additional details, see [architecture overview](docs/ARCHITECTURE.md).
|
||||
For additional details, see [architecture overview](docs/architecture.md).
|
||||
|
||||
## Features
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/controller"
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
|
||||
"github.com/argoproj/argo-cd/reposerver"
|
||||
"github.com/argoproj/argo-cd/server/cluster"
|
||||
apirepository "github.com/argoproj/argo-cd/server/repository"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
@@ -21,7 +22,6 @@ import (
|
||||
// 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).
|
||||
"github.com/argoproj/argo-cd/reposerver"
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
|
||||
)
|
||||
|
||||
@@ -29,7 +29,7 @@ const (
|
||||
// CLIName is the name of the CLI
|
||||
cliName = "argocd-application-controller"
|
||||
// Default time in seconds for application resync period
|
||||
defaultAppResyncPeriod = 600
|
||||
defaultAppResyncPeriod = 180
|
||||
)
|
||||
|
||||
func newCommand() *cobra.Command {
|
||||
@@ -37,11 +37,17 @@ func newCommand() *cobra.Command {
|
||||
clientConfig clientcmd.ClientConfig
|
||||
appResyncPeriod int64
|
||||
repoServerAddress string
|
||||
workers int
|
||||
logLevel string
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: cliName,
|
||||
Short: "application-controller is a controller to operate on applications CRD",
|
||||
RunE: func(c *cobra.Command, args []string) error {
|
||||
level, err := log.ParseLevel(logLevel)
|
||||
errors.CheckError(err)
|
||||
log.SetLevel(level)
|
||||
|
||||
config, err := clientConfig.ClientConfig()
|
||||
errors.CheckError(err)
|
||||
|
||||
@@ -58,14 +64,17 @@ func newCommand() *cobra.Command {
|
||||
}
|
||||
resyncDuration := time.Duration(appResyncPeriod) * time.Second
|
||||
apiRepoServer := apirepository.NewServer(namespace, kubeClient, appClient)
|
||||
apiClusterServer := cluster.NewServer(namespace, kubeClient, appClient)
|
||||
clusterService := cluster.NewServer(namespace, kubeClient, appClient)
|
||||
appComparator := controller.NewKsonnetAppComparator(clusterService)
|
||||
|
||||
appController := controller.NewApplicationController(
|
||||
namespace,
|
||||
kubeClient,
|
||||
appClient,
|
||||
reposerver.NewRepositoryServerClientset(repoServerAddress),
|
||||
apiRepoServer,
|
||||
apiClusterServer,
|
||||
appComparator,
|
||||
resyncDuration,
|
||||
&controllerConfig)
|
||||
@@ -74,7 +83,7 @@ func newCommand() *cobra.Command {
|
||||
defer cancel()
|
||||
|
||||
log.Infof("Application Controller (version: %s) starting (namespace: %s)", argocd.GetVersion(), namespace)
|
||||
go appController.Run(ctx, 1)
|
||||
go appController.Run(ctx, workers)
|
||||
// Wait forever
|
||||
select {}
|
||||
},
|
||||
@@ -83,6 +92,8 @@ func newCommand() *cobra.Command {
|
||||
clientConfig = cli.AddKubectlFlagsToCmd(&command)
|
||||
command.Flags().Int64Var(&appResyncPeriod, "app-resync", defaultAppResyncPeriod, "Time period in seconds for application resync.")
|
||||
command.Flags().StringVar(&repoServerAddress, "repo-server", "localhost:8081", "Repo server address.")
|
||||
command.Flags().IntVar(&workers, "workers", 1, "Number of application workers")
|
||||
command.Flags().StringVar(&logLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
|
||||
return &command
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/git"
|
||||
"github.com/argoproj/argo-cd/util/ksonnet"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
@@ -55,7 +56,11 @@ func newCommand() *cobra.Command {
|
||||
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
|
||||
errors.CheckError(err)
|
||||
|
||||
ksVers, err := ksonnet.KsonnetVersion()
|
||||
errors.CheckError(err)
|
||||
|
||||
log.Infof("argocd-repo-server %s serving on %s (namespace: %s)", argocd.GetVersion(), listener.Addr(), namespace)
|
||||
log.Infof("ksonnet version: %s", ksVers)
|
||||
err = grpc.Serve(listener)
|
||||
errors.CheckError(err)
|
||||
return nil
|
||||
|
||||
@@ -15,11 +15,12 @@ import (
|
||||
// NewCommand returns a new instance of an argocd command
|
||||
func NewCommand() *cobra.Command {
|
||||
var (
|
||||
insecure bool
|
||||
logLevel string
|
||||
clientConfig clientcmd.ClientConfig
|
||||
staticAssetsDir string
|
||||
repoServerAddress string
|
||||
configMapName string
|
||||
disableAuth bool
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: cliName,
|
||||
@@ -40,16 +41,26 @@ func NewCommand() *cobra.Command {
|
||||
appclientset := appclientset.NewForConfigOrDie(config)
|
||||
repoclientset := reposerver.NewRepositoryServerClientset(repoServerAddress)
|
||||
|
||||
argocd := server.NewServer(kubeclientset, appclientset, repoclientset, namespace, staticAssetsDir, configMapName)
|
||||
argoCDOpts := server.ArgoCDServerOpts{
|
||||
Insecure: insecure,
|
||||
Namespace: namespace,
|
||||
StaticAssetsDir: staticAssetsDir,
|
||||
KubeClientset: kubeclientset,
|
||||
AppClientset: appclientset,
|
||||
RepoClientset: repoclientset,
|
||||
DisableAuth: disableAuth,
|
||||
}
|
||||
argocd := server.NewServer(argoCDOpts)
|
||||
argocd.Run()
|
||||
},
|
||||
}
|
||||
|
||||
clientConfig = cli.AddKubectlFlagsToCmd(command)
|
||||
command.Flags().BoolVar(&insecure, "insecure", false, "Run server without TLS")
|
||||
command.Flags().StringVar(&staticAssetsDir, "staticassets", "", "Static assets directory path")
|
||||
command.Flags().StringVar(&logLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
|
||||
command.Flags().StringVar(&repoServerAddress, "repo-server", "localhost:8081", "Repo server address.")
|
||||
command.Flags().StringVar(&configMapName, "config-map", "", "Name of a Kubernetes config map to use.")
|
||||
command.Flags().BoolVar(&disableAuth, "disable-auth", false, "Disable client authentication")
|
||||
command.AddCommand(cli.NewVersionCmd(cliName))
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -3,9 +3,10 @@ package commands
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
@@ -13,8 +14,11 @@ import (
|
||||
argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/server/application"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/diff"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/yudai/gojsondiff/formatter"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -24,36 +28,35 @@ import (
|
||||
func NewApplicationCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "app",
|
||||
Short: fmt.Sprintf("%s app COMMAND", cliName),
|
||||
Short: "Manage applications",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
},
|
||||
}
|
||||
|
||||
command.AddCommand(NewApplicationAddCommand(clientOpts))
|
||||
command.AddCommand(NewApplicationCreateCommand(clientOpts))
|
||||
command.AddCommand(NewApplicationGetCommand(clientOpts))
|
||||
command.AddCommand(NewApplicationDiffCommand(clientOpts))
|
||||
command.AddCommand(NewApplicationSetCommand(clientOpts))
|
||||
command.AddCommand(NewApplicationSyncCommand(clientOpts))
|
||||
command.AddCommand(NewApplicationHistoryCommand(clientOpts))
|
||||
command.AddCommand(NewApplicationRollbackCommand(clientOpts))
|
||||
command.AddCommand(NewApplicationListCommand(clientOpts))
|
||||
command.AddCommand(NewApplicationRemoveCommand(clientOpts))
|
||||
command.AddCommand(NewApplicationDeleteCommand(clientOpts))
|
||||
return command
|
||||
}
|
||||
|
||||
// NewApplicationAddCommand returns a new instance of an `argocd app add` command
|
||||
func NewApplicationAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
// NewApplicationCreateCommand returns a new instance of an `argocd app create` command
|
||||
func NewApplicationCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var (
|
||||
fileURL string
|
||||
repoURL string
|
||||
appPath string
|
||||
appName string
|
||||
env string
|
||||
destServer string
|
||||
destNamespace string
|
||||
appOpts appOptions
|
||||
fileURL string
|
||||
appName string
|
||||
syncPolicy string
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "add",
|
||||
Short: fmt.Sprintf("%s app add", cliName),
|
||||
Use: "create",
|
||||
Short: "Create an application from a git location",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) != 0 {
|
||||
c.HelpFunc()(c, args)
|
||||
@@ -61,59 +64,58 @@ func NewApplicationAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com
|
||||
}
|
||||
var app argoappv1.Application
|
||||
if fileURL != "" {
|
||||
var (
|
||||
fileContents []byte
|
||||
err error
|
||||
)
|
||||
_, err = url.ParseRequestURI(fileURL)
|
||||
_, err := url.ParseRequestURI(fileURL)
|
||||
if err != nil {
|
||||
fileContents, err = readLocalFile(fileURL)
|
||||
err = cli.UnmarshalLocalFile(fileURL, &app)
|
||||
} else {
|
||||
fileContents, err = readRemoteFile(fileURL)
|
||||
err = cli.UnmarshalRemoteFile(fileURL, &app)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
unmarshalApplication(fileContents, &app)
|
||||
|
||||
} else {
|
||||
// all these params are required if we're here
|
||||
if repoURL == "" || appPath == "" || appName == "" {
|
||||
if syncPolicy != "" && syncPolicy != "Always" {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
if appOpts.repoURL == "" || appOpts.appPath == "" || appOpts.env == "" || appName == "" {
|
||||
log.Fatal("name, repo, path, env are required")
|
||||
os.Exit(1)
|
||||
}
|
||||
app = argoappv1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: appName,
|
||||
},
|
||||
Spec: argoappv1.ApplicationSpec{
|
||||
Source: argoappv1.ApplicationSource{
|
||||
RepoURL: repoURL,
|
||||
Path: appPath,
|
||||
Environment: env,
|
||||
RepoURL: appOpts.repoURL,
|
||||
Path: appOpts.appPath,
|
||||
Environment: appOpts.env,
|
||||
TargetRevision: appOpts.revision,
|
||||
},
|
||||
SyncPolicy: syncPolicy,
|
||||
},
|
||||
}
|
||||
}
|
||||
if destServer != "" || destNamespace != "" {
|
||||
if appOpts.destServer != "" || appOpts.destNamespace != "" {
|
||||
app.Spec.Destination = &argoappv1.ApplicationDestination{
|
||||
Server: destServer,
|
||||
Namespace: destNamespace,
|
||||
Server: appOpts.destServer,
|
||||
Namespace: appOpts.destNamespace,
|
||||
}
|
||||
}
|
||||
setParameterOverrides(&app, appOpts.parameters)
|
||||
conn, appIf := argocdclient.NewClientOrDie(clientOpts).NewApplicationClientOrDie()
|
||||
defer util.Close(conn)
|
||||
_, err := appIf.Create(context.Background(), &app)
|
||||
created, err := appIf.Create(context.Background(), &app)
|
||||
errors.CheckError(err)
|
||||
fmt.Printf("application '%s' created\n", created.ObjectMeta.Name)
|
||||
},
|
||||
}
|
||||
command.Flags().StringVarP(&fileURL, "file", "f", "", "Filename or URL to Kubernetes manifests for the app")
|
||||
command.Flags().StringVar(&appName, "name", "", "A name for the app, ignored if a file is set")
|
||||
command.Flags().StringVar(&repoURL, "repo", "", "Repository URL, ignored if a file is set")
|
||||
command.Flags().StringVar(&appPath, "path", "", "Path in repository to the ksonnet app directory, ignored if a file is set")
|
||||
command.Flags().StringVar(&env, "env", "", "Application environment to monitor")
|
||||
command.Flags().StringVar(&destServer, "dest-server", "", "K8s cluster URL (overrides the server URL specified in the ksonnet app.yaml)")
|
||||
command.Flags().StringVar(&destNamespace, "dest-namespace", "", "K8s target namespace (overrides the namespace specified in the ksonnet app.yaml)")
|
||||
addAppFlags(command, &appOpts)
|
||||
//command.Flags().StringVar(&syncPolicy, "sync-policy", "", "Synchronization policy for application (e.g., Always)")
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -121,7 +123,7 @@ func NewApplicationAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com
|
||||
func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "get",
|
||||
Short: fmt.Sprintf("%s app get APPNAME", cliName),
|
||||
Short: "Get application details",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
c.HelpFunc()(c, args)
|
||||
@@ -167,11 +169,87 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com
|
||||
return command
|
||||
}
|
||||
|
||||
// NewApplicationSetCommand returns a new instance of an `argocd app set` command
|
||||
func NewApplicationSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var (
|
||||
appOpts appOptions
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "set",
|
||||
Short: "Set application parameters",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
appName := args[0]
|
||||
conn, appIf := argocdclient.NewClientOrDie(clientOpts).NewApplicationClientOrDie()
|
||||
defer util.Close(conn)
|
||||
app, err := appIf.Get(context.Background(), &application.ApplicationQuery{Name: appName})
|
||||
errors.CheckError(err)
|
||||
visited := 0
|
||||
c.Flags().Visit(func(f *pflag.Flag) {
|
||||
visited++
|
||||
switch f.Name {
|
||||
case "repo":
|
||||
app.Spec.Source.RepoURL = appOpts.repoURL
|
||||
case "path":
|
||||
app.Spec.Source.Path = appOpts.appPath
|
||||
case "env":
|
||||
app.Spec.Source.Environment = appOpts.env
|
||||
case "revision":
|
||||
app.Spec.Source.TargetRevision = appOpts.revision
|
||||
case "dest-server":
|
||||
if app.Spec.Destination == nil {
|
||||
app.Spec.Destination = &argoappv1.ApplicationDestination{}
|
||||
}
|
||||
app.Spec.Destination.Server = appOpts.destServer
|
||||
case "dest-namespace":
|
||||
if app.Spec.Destination == nil {
|
||||
app.Spec.Destination = &argoappv1.ApplicationDestination{}
|
||||
}
|
||||
app.Spec.Destination.Namespace = appOpts.destNamespace
|
||||
}
|
||||
})
|
||||
if visited == 0 {
|
||||
log.Error("Please set at least one option to update")
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
setParameterOverrides(app, appOpts.parameters)
|
||||
_, err = appIf.Update(context.Background(), app)
|
||||
errors.CheckError(err)
|
||||
},
|
||||
}
|
||||
addAppFlags(command, &appOpts)
|
||||
return command
|
||||
}
|
||||
|
||||
type appOptions struct {
|
||||
repoURL string
|
||||
appPath string
|
||||
env string
|
||||
revision string
|
||||
destServer string
|
||||
destNamespace string
|
||||
parameters []string
|
||||
}
|
||||
|
||||
func addAppFlags(command *cobra.Command, opts *appOptions) {
|
||||
command.Flags().StringVar(&opts.repoURL, "repo", "", "Repository URL, ignored if a file is set")
|
||||
command.Flags().StringVar(&opts.appPath, "path", "", "Path in repository to the ksonnet app directory, ignored if a file is set")
|
||||
command.Flags().StringVar(&opts.env, "env", "", "Application environment to monitor")
|
||||
command.Flags().StringVar(&opts.revision, "revision", "HEAD", "The tracking source branch, tag, or commit the application will sync to")
|
||||
command.Flags().StringVar(&opts.destServer, "dest-server", "", "K8s cluster URL (overrides the server URL specified in the ksonnet app.yaml)")
|
||||
command.Flags().StringVar(&opts.destNamespace, "dest-namespace", "", "K8s target namespace (overrides the namespace specified in the ksonnet app.yaml)")
|
||||
command.Flags().StringArrayVarP(&opts.parameters, "parameter", "p", []string{}, "set a parameter override (e.g. -p guestbook=image=example/guestbook:latest)")
|
||||
}
|
||||
|
||||
// NewApplicationDiffCommand returns a new instance of an `argocd app diff` command
|
||||
func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "diff",
|
||||
Short: fmt.Sprintf("%s app diff APPNAME", cliName),
|
||||
Short: "Perform a diff against the target and live state",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
c.HelpFunc()(c, args)
|
||||
@@ -206,11 +284,14 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
return command
|
||||
}
|
||||
|
||||
// NewApplicationRemoveCommand returns a new instance of an `argocd app list` command
|
||||
func NewApplicationRemoveCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
// NewApplicationDeleteCommand returns a new instance of an `argocd app delete` command
|
||||
func NewApplicationDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var (
|
||||
force bool
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "rm",
|
||||
Short: fmt.Sprintf("%s app rm APPNAME", cliName),
|
||||
Use: "delete",
|
||||
Short: "Delete an application",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
c.HelpFunc()(c, args)
|
||||
@@ -219,19 +300,24 @@ func NewApplicationRemoveCommand(clientOpts *argocdclient.ClientOptions) *cobra.
|
||||
conn, appIf := argocdclient.NewClientOrDie(clientOpts).NewApplicationClientOrDie()
|
||||
defer util.Close(conn)
|
||||
for _, appName := range args {
|
||||
_, err := appIf.Delete(context.Background(), &application.DeleteApplicationRequest{Name: appName})
|
||||
appDeleteReq := application.DeleteApplicationRequest{
|
||||
Name: appName,
|
||||
Force: force,
|
||||
}
|
||||
_, err := appIf.Delete(context.Background(), &appDeleteReq)
|
||||
errors.CheckError(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
command.Flags().BoolVar(&force, "force", false, "Force delete application even if cascaded deletion unsuccessful")
|
||||
return command
|
||||
}
|
||||
|
||||
// NewApplicationListCommand returns a new instance of an `argocd app rm` command
|
||||
// NewApplicationListCommand returns a new instance of an `argocd app list` command
|
||||
func NewApplicationListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "list",
|
||||
Short: fmt.Sprintf("%s app list", cliName),
|
||||
Short: "List applications",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
conn, appIf := argocdclient.NewClientOrDie(clientOpts).NewApplicationClientOrDie()
|
||||
defer util.Close(conn)
|
||||
@@ -262,13 +348,15 @@ func NewApplicationListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
// NewApplicationSyncCommand returns a new instance of an `argocd app sync` command
|
||||
func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var (
|
||||
dryRun bool
|
||||
revision string
|
||||
prune bool
|
||||
dryRun bool
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "sync",
|
||||
Short: fmt.Sprintf("%s app sync APPNAME", cliName),
|
||||
Short: "Sync an application to its target state",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
if len(args) != 1 {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -276,8 +364,10 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
defer util.Close(conn)
|
||||
appName := args[0]
|
||||
syncReq := application.ApplicationSyncRequest{
|
||||
Name: appName,
|
||||
DryRun: dryRun,
|
||||
Name: appName,
|
||||
DryRun: dryRun,
|
||||
Revision: revision,
|
||||
Prune: prune,
|
||||
}
|
||||
syncRes, err := appIf.Sync(context.Background(), &syncReq)
|
||||
errors.CheckError(err)
|
||||
@@ -291,5 +381,132 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
},
|
||||
}
|
||||
command.Flags().BoolVar(&dryRun, "dry-run", false, "Preview apply without affecting cluster")
|
||||
command.Flags().BoolVar(&prune, "prune", false, "Allow deleting unexpected resources")
|
||||
command.Flags().StringVar(&revision, "revision", "", "Sync to a specific revision. Preserves parameter overrides")
|
||||
return command
|
||||
}
|
||||
|
||||
// setParameterOverrides updates an existing or appends a new parameter override in the application
|
||||
func setParameterOverrides(app *argoappv1.Application, parameters []string) {
|
||||
if len(parameters) == 0 {
|
||||
return
|
||||
}
|
||||
var newParams []argoappv1.ComponentParameter
|
||||
if len(app.Spec.Source.ComponentParameterOverrides) > 0 {
|
||||
newParams = app.Spec.Source.ComponentParameterOverrides
|
||||
} else {
|
||||
newParams = make([]argoappv1.ComponentParameter, 0)
|
||||
}
|
||||
for _, paramStr := range parameters {
|
||||
parts := strings.SplitN(paramStr, "=", 3)
|
||||
if len(parts) != 3 {
|
||||
log.Fatalf("Expected parameter of the form: component=param=value. Received: %s", paramStr)
|
||||
}
|
||||
newParam := argoappv1.ComponentParameter{
|
||||
Component: parts[0],
|
||||
Name: parts[1],
|
||||
Value: parts[2],
|
||||
}
|
||||
index := -1
|
||||
for i, cp := range newParams {
|
||||
if cp.Component == newParam.Component && cp.Name == newParam.Name {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if index == -1 {
|
||||
newParams = append(newParams, newParam)
|
||||
} else {
|
||||
newParams[index] = newParam
|
||||
}
|
||||
}
|
||||
app.Spec.Source.ComponentParameterOverrides = newParams
|
||||
}
|
||||
|
||||
// NewApplicationHistoryCommand returns a new instance of an `argocd app history` command
|
||||
func NewApplicationHistoryCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "history",
|
||||
Short: "Show application deployment history",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
conn, appIf := argocdclient.NewClientOrDie(clientOpts).NewApplicationClientOrDie()
|
||||
defer util.Close(conn)
|
||||
appName := args[0]
|
||||
app, err := appIf.Get(context.Background(), &application.ApplicationQuery{Name: appName})
|
||||
errors.CheckError(err)
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
fmt.Fprintf(w, "ID\tDATE\tCOMMIT\tPARAMETERS\n")
|
||||
for _, depInfo := range app.Status.RecentDeployments {
|
||||
paramStr := paramString(depInfo.Params)
|
||||
fmt.Fprintf(w, "%d\t%s\t%s\t%s\n", depInfo.ID, depInfo.DeployedAt, depInfo.Revision, paramStr)
|
||||
}
|
||||
_ = w.Flush()
|
||||
},
|
||||
}
|
||||
return command
|
||||
}
|
||||
|
||||
func paramString(params []argoappv1.ComponentParameter) string {
|
||||
if len(params) == 0 {
|
||||
return ""
|
||||
}
|
||||
paramNames := []string{}
|
||||
for _, param := range params {
|
||||
paramNames = append(paramNames, fmt.Sprintf("%s=%s=%s", param.Component, param.Name, param.Value))
|
||||
}
|
||||
return strings.Join(paramNames, ",")
|
||||
}
|
||||
|
||||
// NewApplicationRollbackCommand returns a new instance of an `argocd app rollback` command
|
||||
func NewApplicationRollbackCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var (
|
||||
prune bool
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "rollback",
|
||||
Short: "Rollback application to a previous deployed version",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) != 2 {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
appName := args[0]
|
||||
depID, err := strconv.Atoi(args[1])
|
||||
errors.CheckError(err)
|
||||
conn, appIf := argocdclient.NewClientOrDie(clientOpts).NewApplicationClientOrDie()
|
||||
defer util.Close(conn)
|
||||
ctx := context.Background()
|
||||
app, err := appIf.Get(ctx, &application.ApplicationQuery{Name: appName})
|
||||
errors.CheckError(err)
|
||||
var depInfo *argoappv1.DeploymentInfo
|
||||
for _, di := range app.Status.RecentDeployments {
|
||||
if di.ID == int64(depID) {
|
||||
depInfo = &di
|
||||
break
|
||||
}
|
||||
}
|
||||
if depInfo == nil {
|
||||
log.Fatalf("Application '%s' does not have deployment id '%d' in history\n", app.ObjectMeta.Name, depID)
|
||||
}
|
||||
syncRes, err := appIf.Rollback(ctx, &application.ApplicationRollbackRequest{
|
||||
Name: appName,
|
||||
ID: int64(depID),
|
||||
Prune: prune,
|
||||
})
|
||||
errors.CheckError(err)
|
||||
fmt.Printf("%s %s\n", appName, syncRes.Message)
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
fmt.Fprintf(w, "NAME\tKIND\tMESSAGE\n")
|
||||
for _, resDetails := range syncRes.Resources {
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\n", resDetails.Name, resDetails.Kind, resDetails.Message)
|
||||
}
|
||||
_ = w.Flush()
|
||||
},
|
||||
}
|
||||
command.Flags().BoolVar(&prune, "prune", false, "Allow deleting unexpected resources")
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import (
|
||||
func NewClusterCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clientcmd.PathOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "cluster",
|
||||
Short: fmt.Sprintf("%s cluster COMMAND", cliName),
|
||||
Short: "Manage cluster credentials",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
@@ -49,7 +49,7 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie
|
||||
var configAccess clientcmd.ConfigAccess = pathOpts
|
||||
if len(args) == 0 {
|
||||
log.Error("Choose a context name from:")
|
||||
printContexts(configAccess)
|
||||
printKubeContexts(configAccess)
|
||||
os.Exit(1)
|
||||
}
|
||||
config, err := configAccess.GetStartingConfig()
|
||||
@@ -65,14 +65,12 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie
|
||||
conf, err := clientConfig.ClientConfig()
|
||||
errors.CheckError(err)
|
||||
|
||||
if conf.Username == "" || conf.Password == "" {
|
||||
// Install RBAC resources for managing the cluster if username and password are not specified
|
||||
conf.BearerToken = common.InstallClusterManagerRBAC(conf)
|
||||
}
|
||||
// Install RBAC resources for managing the cluster
|
||||
managerBearerToken := common.InstallClusterManagerRBAC(conf)
|
||||
|
||||
conn, clusterIf := argocdclient.NewClientOrDie(clientOpts).NewClusterClientOrDie()
|
||||
defer util.Close(conn)
|
||||
clst := NewCluster(args[0], conf)
|
||||
clst := NewCluster(args[0], conf, managerBearerToken)
|
||||
clst, err = clusterIf.Create(context.Background(), clst)
|
||||
errors.CheckError(err)
|
||||
fmt.Printf("Cluster '%s' added\n", clst.Name)
|
||||
@@ -82,12 +80,12 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie
|
||||
return command
|
||||
}
|
||||
|
||||
func printContexts(ca clientcmd.ConfigAccess) {
|
||||
func printKubeContexts(ca clientcmd.ConfigAccess) {
|
||||
config, err := ca.GetStartingConfig()
|
||||
errors.CheckError(err)
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
defer func() { _ = w.Flush() }()
|
||||
columnNames := []string{"CURRENT", "NAME", "CLUSTER", "AUTHINFO", "NAMESPACE"}
|
||||
columnNames := []string{"CURRENT", "NAME", "CLUSTER", "SERVER"}
|
||||
_, err = fmt.Fprintf(w, "%s\n", strings.Join(columnNames, "\t"))
|
||||
errors.CheckError(err)
|
||||
|
||||
@@ -100,16 +98,17 @@ func printContexts(ca clientcmd.ConfigAccess) {
|
||||
|
||||
for _, name := range contextNames {
|
||||
context := config.Contexts[name]
|
||||
cluster := config.Clusters[context.Cluster]
|
||||
prefix := " "
|
||||
if config.CurrentContext == name {
|
||||
prefix = "*"
|
||||
}
|
||||
_, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", prefix, name, context.Cluster, context.AuthInfo, context.Namespace)
|
||||
_, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", prefix, name, context.Cluster, cluster.Server)
|
||||
errors.CheckError(err)
|
||||
}
|
||||
}
|
||||
|
||||
func NewCluster(name string, conf *rest.Config) *argoappv1.Cluster {
|
||||
func NewCluster(name string, conf *rest.Config, managerBearerToken string) *argoappv1.Cluster {
|
||||
tlsClientConfig := argoappv1.TLSClientConfig{
|
||||
Insecure: conf.TLSClientConfig.Insecure,
|
||||
ServerName: conf.TLSClientConfig.ServerName,
|
||||
@@ -136,9 +135,7 @@ func NewCluster(name string, conf *rest.Config) *argoappv1.Cluster {
|
||||
Server: conf.Host,
|
||||
Name: name,
|
||||
Config: argoappv1.ClusterConfig{
|
||||
Username: conf.Username,
|
||||
Password: conf.Password,
|
||||
BearerToken: conf.BearerToken,
|
||||
BearerToken: managerBearerToken,
|
||||
TLSClientConfig: tlsClientConfig,
|
||||
},
|
||||
}
|
||||
@@ -149,7 +146,7 @@ func NewCluster(name string, conf *rest.Config) *argoappv1.Cluster {
|
||||
func NewClusterGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "get",
|
||||
Short: fmt.Sprintf("%s cluster get SERVER", cliName),
|
||||
Short: "Get cluster information",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
c.HelpFunc()(c, args)
|
||||
@@ -173,7 +170,7 @@ func NewClusterGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
|
||||
func NewClusterRemoveCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "rm",
|
||||
Short: fmt.Sprintf("%s cluster rm SERVER", cliName),
|
||||
Short: "Remove cluster credentials",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
c.HelpFunc()(c, args)
|
||||
@@ -196,7 +193,7 @@ func NewClusterRemoveCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comm
|
||||
func NewClusterListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "list",
|
||||
Short: fmt.Sprintf("%s cluster list", cliName),
|
||||
Short: "List configured clusters",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
conn, clusterIf := argocdclient.NewClientOrDie(clientOpts).NewClusterClientOrDie()
|
||||
defer util.Close(conn)
|
||||
|
||||
84
cmd/argocd/commands/context.go
Normal file
84
cmd/argocd/commands/context.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/util/localconfig"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// NewContextCommand returns a new instance of an `argocd ctx` command
|
||||
func NewContextCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "context",
|
||||
Aliases: []string{"ctx"},
|
||||
Short: "Switch between contexts",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
printArgoCDContexts(clientOpts.ConfigPath)
|
||||
return
|
||||
}
|
||||
ctxName := args[0]
|
||||
argoCDDir, err := localconfig.DefaultConfigDir()
|
||||
errors.CheckError(err)
|
||||
prevCtxFile := path.Join(argoCDDir, ".prev-ctx")
|
||||
|
||||
if ctxName == "-" {
|
||||
prevCtxBytes, err := ioutil.ReadFile(prevCtxFile)
|
||||
errors.CheckError(err)
|
||||
ctxName = string(prevCtxBytes)
|
||||
}
|
||||
localCfg, err := localconfig.ReadLocalConfig(clientOpts.ConfigPath)
|
||||
errors.CheckError(err)
|
||||
if localCfg.CurrentContext == ctxName {
|
||||
fmt.Printf("Already at context '%s'\n", localCfg.CurrentContext)
|
||||
return
|
||||
}
|
||||
if _, err = localCfg.ResolveContext(ctxName); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
prevCtx := localCfg.CurrentContext
|
||||
localCfg.CurrentContext = ctxName
|
||||
err = localconfig.WriteLocalConfig(*localCfg, clientOpts.ConfigPath)
|
||||
errors.CheckError(err)
|
||||
err = ioutil.WriteFile(prevCtxFile, []byte(prevCtx), 0644)
|
||||
errors.CheckError(err)
|
||||
fmt.Printf("Switched to context '%s'\n", localCfg.CurrentContext)
|
||||
},
|
||||
}
|
||||
return command
|
||||
}
|
||||
|
||||
func printArgoCDContexts(configPath string) {
|
||||
localCfg, err := localconfig.ReadLocalConfig(configPath)
|
||||
errors.CheckError(err)
|
||||
if localCfg == nil {
|
||||
log.Fatalf("No contexts defined in %s", configPath)
|
||||
}
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
defer func() { _ = w.Flush() }()
|
||||
columnNames := []string{"CURRENT", "NAME", "SERVER"}
|
||||
_, err = fmt.Fprintf(w, "%s\n", strings.Join(columnNames, "\t"))
|
||||
errors.CheckError(err)
|
||||
|
||||
for _, contextRef := range localCfg.Contexts {
|
||||
context, err := localCfg.ResolveContext(contextRef.Name)
|
||||
if err != nil {
|
||||
log.Warnf("Context '%s' had error: %v", contextRef.Name, err)
|
||||
}
|
||||
prefix := " "
|
||||
if localCfg.CurrentContext == context.Name {
|
||||
prefix = "*"
|
||||
}
|
||||
_, err = fmt.Fprintf(w, "%s\t%s\t%s\n", prefix, context.Name, context.Server.Server)
|
||||
errors.CheckError(err)
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,11 @@ func NewInstallCommand() *cobra.Command {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
conf, err := clientConfig.ClientConfig()
|
||||
errors.CheckError(err)
|
||||
namespace, wasSpecified, err := clientConfig.Namespace()
|
||||
errors.CheckError(err)
|
||||
if wasSpecified {
|
||||
installOpts.Namespace = namespace
|
||||
}
|
||||
installer, err := install.NewInstaller(conf, installOpts)
|
||||
errors.CheckError(err)
|
||||
installer.Install()
|
||||
@@ -29,8 +34,8 @@ func NewInstallCommand() *cobra.Command {
|
||||
command.Flags().BoolVar(&installOpts.Upgrade, "upgrade", false, "upgrade controller/ui deployments and configmap if already installed")
|
||||
command.Flags().BoolVar(&installOpts.DryRun, "dry-run", false, "print the kubernetes manifests to stdout instead of installing")
|
||||
command.Flags().BoolVar(&installOpts.ConfigSuperuser, "config-superuser", false, "create or update a superuser username and password")
|
||||
command.Flags().BoolVar(&installOpts.CreateSignature, "create-signature", false, "create or update the server-side token signing signature")
|
||||
command.Flags().StringVar(&installOpts.ConfigMap, "config-map", "", "apply settings from a Kubernetes config map")
|
||||
command.Flags().StringVar(&installOpts.Namespace, "install-namespace", install.DefaultInstallNamespace, "install into a specific namespace")
|
||||
command.Flags().StringVar(&installOpts.ControllerImage, "controller-image", install.DefaultControllerImage, "use a specified controller image")
|
||||
command.Flags().StringVar(&installOpts.ServerImage, "server-image", install.DefaultServerImage, "use a specified api server image")
|
||||
command.Flags().StringVar(&installOpts.UIImage, "ui-image", install.DefaultUIImage, "use a specified ui image")
|
||||
|
||||
124
cmd/argocd/commands/login.go
Normal file
124
cmd/argocd/commands/login.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/server/session"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
grpc_util "github.com/argoproj/argo-cd/util/grpc"
|
||||
"github.com/argoproj/argo-cd/util/localconfig"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// NewLoginCommand returns a new instance of `argocd login` command
|
||||
func NewLoginCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var (
|
||||
name string
|
||||
username string
|
||||
password string
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "login SERVER",
|
||||
Short: "Log in to Argo CD",
|
||||
Long: "Log in to Argo CD",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
server := args[0]
|
||||
tlsTestResult, err := grpc_util.TestTLS(server)
|
||||
errors.CheckError(err)
|
||||
if !tlsTestResult.TLS {
|
||||
if !globalClientOpts.PlainText {
|
||||
askToProceed("WARNING: server is not configured with TLS. Proceed (y/n)? ")
|
||||
globalClientOpts.PlainText = true
|
||||
}
|
||||
} else if tlsTestResult.InsecureErr != nil {
|
||||
if !globalClientOpts.Insecure {
|
||||
askToProceed(fmt.Sprintf("WARNING: server certificate had error: %s. Proceed insecurely (y/n)? ", tlsTestResult.InsecureErr))
|
||||
globalClientOpts.Insecure = true
|
||||
}
|
||||
}
|
||||
|
||||
username, password = cli.PromptCredentials(username, password)
|
||||
|
||||
clientOpts := argocdclient.ClientOptions{
|
||||
ConfigPath: "",
|
||||
ServerAddr: server,
|
||||
Insecure: globalClientOpts.Insecure,
|
||||
PlainText: globalClientOpts.PlainText,
|
||||
}
|
||||
conn, sessionIf := argocdclient.NewClientOrDie(&clientOpts).NewSessionClientOrDie()
|
||||
defer util.Close(conn)
|
||||
|
||||
sessionRequest := session.SessionCreateRequest{
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
createdSession, err := sessionIf.Create(context.Background(), &sessionRequest)
|
||||
errors.CheckError(err)
|
||||
fmt.Printf("user %q logged in successfully\n", username)
|
||||
|
||||
// login successful. Persist the config
|
||||
localCfg, err := localconfig.ReadLocalConfig(globalClientOpts.ConfigPath)
|
||||
errors.CheckError(err)
|
||||
if localCfg == nil {
|
||||
localCfg = &localconfig.LocalConfig{}
|
||||
}
|
||||
localCfg.UpsertServer(localconfig.Server{
|
||||
Server: server,
|
||||
PlainText: globalClientOpts.PlainText,
|
||||
Insecure: globalClientOpts.Insecure,
|
||||
})
|
||||
localCfg.UpsertUser(localconfig.User{
|
||||
Name: server,
|
||||
AuthToken: createdSession.Token,
|
||||
})
|
||||
if name == "" {
|
||||
name = server
|
||||
}
|
||||
localCfg.CurrentContext = name
|
||||
localCfg.UpsertContext(localconfig.ContextRef{
|
||||
Name: name,
|
||||
User: server,
|
||||
Server: server,
|
||||
})
|
||||
|
||||
err = localconfig.WriteLocalConfig(*localCfg, globalClientOpts.ConfigPath)
|
||||
errors.CheckError(err)
|
||||
|
||||
},
|
||||
}
|
||||
command.Flags().StringVar(&name, "name", "", "name to use for the context")
|
||||
command.Flags().StringVar(&username, "username", "", "the username of an account to authenticate")
|
||||
command.Flags().StringVar(&password, "password", "", "the password of an account to authenticate")
|
||||
return command
|
||||
}
|
||||
|
||||
func askToProceed(message string) {
|
||||
proceed := ""
|
||||
acceptedAnswers := map[string]bool{
|
||||
"y": true,
|
||||
"yes": true,
|
||||
"n": true,
|
||||
"no": true,
|
||||
}
|
||||
for !acceptedAnswers[proceed] {
|
||||
fmt.Print(message)
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
proceedRaw, err := reader.ReadString('\n')
|
||||
errors.CheckError(err)
|
||||
proceed = strings.TrimSpace(proceedRaw)
|
||||
}
|
||||
if proceed == "no" || proceed == "n" {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,10 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"syscall"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
@@ -14,17 +12,17 @@ import (
|
||||
appsv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/server/repository"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/git"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
)
|
||||
|
||||
// NewRepoCommand returns a new instance of an `argocd repo` command
|
||||
func NewRepoCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "repo",
|
||||
Short: fmt.Sprintf("%s repo COMMAND", cliName),
|
||||
Short: "Manage git repository credentials",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
@@ -45,7 +43,7 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "add",
|
||||
Short: fmt.Sprintf("%s repo add REPO", cliName),
|
||||
Short: "Add git repository credentials",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
c.HelpFunc()(c, args)
|
||||
@@ -66,7 +64,7 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// If we can't test the repo, it's probably private. Prompt for credentials and try again.
|
||||
promptCredentials(&repo)
|
||||
repo.Username, repo.Password = cli.PromptCredentials(repo.Username, repo.Password)
|
||||
err = git.TestRepo(repo.Repo, repo.Username, repo.Password, repo.SSHPrivateKey)
|
||||
}
|
||||
errors.CheckError(err)
|
||||
@@ -83,25 +81,11 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
return command
|
||||
}
|
||||
|
||||
func promptCredentials(repo *appsv1.Repository) {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
if repo.Username == "" {
|
||||
fmt.Print("Username: ")
|
||||
username, _ := reader.ReadString('\n')
|
||||
repo.Username = username
|
||||
}
|
||||
if repo.Password == "" {
|
||||
fmt.Print("Password: ")
|
||||
bytePassword, _ := terminal.ReadPassword(syscall.Stdin)
|
||||
repo.Password = string(bytePassword)
|
||||
}
|
||||
}
|
||||
|
||||
// NewRepoRemoveCommand returns a new instance of an `argocd repo list` command
|
||||
func NewRepoRemoveCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "rm",
|
||||
Short: fmt.Sprintf("%s repo rm REPO", cliName),
|
||||
Short: "Remove git repository credentials",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
c.HelpFunc()(c, args)
|
||||
@@ -122,7 +106,7 @@ func NewRepoRemoveCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
|
||||
func NewRepoListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "list",
|
||||
Short: fmt.Sprintf("%s repo list", cliName),
|
||||
Short: "List configured repositories",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
conn, repoIf := argocdclient.NewClientOrDie(clientOpts).NewRepoClientOrDie()
|
||||
defer util.Close(conn)
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/localconfig"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
@@ -22,15 +23,23 @@ func NewCommand() *cobra.Command {
|
||||
},
|
||||
}
|
||||
|
||||
command.AddCommand(cli.NewVersionCmd(cliName))
|
||||
command.AddCommand(NewVersionCmd(&clientOpts))
|
||||
command.AddCommand(NewClusterCommand(&clientOpts, pathOpts))
|
||||
command.AddCommand(NewApplicationCommand(&clientOpts))
|
||||
command.AddCommand(NewLoginCommand(&clientOpts))
|
||||
command.AddCommand(NewRepoCommand(&clientOpts))
|
||||
command.AddCommand(NewInstallCommand())
|
||||
command.AddCommand(NewUninstallCommand())
|
||||
command.AddCommand(NewContextCommand(&clientOpts))
|
||||
|
||||
defaultLocalConfigPath, err := localconfig.DefaultLocalConfigPath()
|
||||
errors.CheckError(err)
|
||||
command.PersistentFlags().StringVar(&clientOpts.ConfigPath, "config", defaultLocalConfigPath, "Path to ArgoCD config")
|
||||
command.PersistentFlags().StringVar(&clientOpts.ServerAddr, "server", "", "ArgoCD server address")
|
||||
command.PersistentFlags().BoolVar(&clientOpts.Insecure, "insecure", true, "Disable transport security for the client connection")
|
||||
command.PersistentFlags().BoolVar(&clientOpts.PlainText, "plaintext", false, "Disable TLS")
|
||||
command.PersistentFlags().BoolVar(&clientOpts.Insecure, "insecure", false, "Skip server certificate and domain verification")
|
||||
command.PersistentFlags().StringVar(&clientOpts.CertFile, "server-crt", "", "Server certificate file")
|
||||
command.PersistentFlags().StringVar(&clientOpts.AuthToken, "auth-token", "", "Authentication token")
|
||||
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -21,12 +21,16 @@ func NewUninstallCommand() *cobra.Command {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
conf, err := clientConfig.ClientConfig()
|
||||
errors.CheckError(err)
|
||||
namespace, wasSpecified, err := clientConfig.Namespace()
|
||||
errors.CheckError(err)
|
||||
if wasSpecified {
|
||||
installOpts.Namespace = namespace
|
||||
}
|
||||
installer, err := install.NewInstaller(conf, installOpts)
|
||||
errors.CheckError(err)
|
||||
installer.Uninstall()
|
||||
},
|
||||
}
|
||||
command.Flags().StringVar(&installOpts.Namespace, "install-namespace", install.DefaultInstallNamespace, "uninstall from a specific namespace")
|
||||
clientConfig = cli.AddKubectlFlagsToCmd(command)
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/ghodss/yaml"
|
||||
)
|
||||
|
||||
// unmarshalApplication tries to convert a YAML or JSON byte array into an Application struct.
|
||||
func unmarshalApplication(data []byte, app *argoappv1.Application) {
|
||||
// first, try unmarshaling as JSON
|
||||
// Based on technique from Kubectl, which supports both YAML and JSON:
|
||||
// https://mlafeldt.github.io/blog/teaching-go-programs-to-love-json-and-yaml/
|
||||
// http://ghodss.com/2014/the-right-way-to-handle-yaml-in-golang/
|
||||
// Short version: JSON unmarshaling won't zero out null fields; YAML unmarshaling will.
|
||||
// This may have unintended effects or hard-to-catch issues when populating our application object.
|
||||
data, err := yaml.YAMLToJSON(data)
|
||||
if err != nil {
|
||||
log.Fatal("Could not decode valid JSON or YAML Kubernetes manifest")
|
||||
}
|
||||
err = json.Unmarshal(data, &app)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not unmarshal Kubernetes manifest: %s", string(data))
|
||||
}
|
||||
}
|
||||
|
||||
// readLocalFile reads a file from disk and returns its contents as a byte array.
|
||||
// The caller is responsible for checking error return values.
|
||||
func readLocalFile(path string) (data []byte, err error) {
|
||||
data, err = ioutil.ReadFile(path)
|
||||
return
|
||||
}
|
||||
|
||||
// readRemoteFile issues a GET request to retrieve the contents of the specified URL as a byte array.
|
||||
// The caller is responsible for checking error return values.
|
||||
func readRemoteFile(url string) (data []byte, err error) {
|
||||
resp, err := http.Get(url)
|
||||
if err == nil {
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
data, err = ioutil.ReadAll(resp.Body)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestReadLocalFile(t *testing.T) {
|
||||
sentinel := "Hello, world!"
|
||||
|
||||
file, err := ioutil.TempFile(os.TempDir(), "")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer func() {
|
||||
_ = os.Remove(file.Name())
|
||||
}()
|
||||
|
||||
_, _ = file.WriteString(sentinel)
|
||||
_ = file.Sync()
|
||||
|
||||
data, err := readLocalFile(file.Name())
|
||||
if string(data) != sentinel {
|
||||
t.Errorf("Test data did not match (err = %v)! Expected \"%s\" and received \"%s\"", err, sentinel, string(data))
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadRemoteFile(t *testing.T) {
|
||||
sentinel := "Hello, world!"
|
||||
|
||||
serve := func(c chan<- string) {
|
||||
// listen on first available dynamic (unprivileged) port
|
||||
listener, err := net.Listen("tcp", ":0")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// send back the address so that it can be used
|
||||
c <- listener.Addr().String()
|
||||
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
// return the sentinel text at root URL
|
||||
fmt.Fprint(w, sentinel)
|
||||
})
|
||||
|
||||
panic(http.Serve(listener, nil))
|
||||
}
|
||||
|
||||
c := make(chan string, 1)
|
||||
|
||||
// run a local webserver to test data retrieval
|
||||
go serve(c)
|
||||
|
||||
address := <-c
|
||||
data, err := readRemoteFile("http://" + address)
|
||||
t.Logf("Listening at address: %s", address)
|
||||
if string(data) != sentinel {
|
||||
t.Errorf("Test data did not match (err = %v)! Expected \"%s\" and received \"%s\"", err, sentinel, string(data))
|
||||
}
|
||||
}
|
||||
64
cmd/argocd/commands/version.go
Normal file
64
cmd/argocd/commands/version.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
argocd "github.com/argoproj/argo-cd"
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// NewVersionCmd returns a new `version` command to be used as a sub-command to root
|
||||
func NewVersionCmd(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var short bool
|
||||
var client bool
|
||||
|
||||
versionCmd := cobra.Command{
|
||||
Use: "version",
|
||||
Short: fmt.Sprintf("Print version information"),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
version := argocd.GetVersion()
|
||||
fmt.Printf("%s: %s\n", cliName, version)
|
||||
if !short {
|
||||
fmt.Printf(" BuildDate: %s\n", version.BuildDate)
|
||||
fmt.Printf(" GitCommit: %s\n", version.GitCommit)
|
||||
fmt.Printf(" GitTreeState: %s\n", version.GitTreeState)
|
||||
if version.GitTag != "" {
|
||||
fmt.Printf(" GitTag: %s\n", version.GitTag)
|
||||
}
|
||||
fmt.Printf(" GoVersion: %s\n", version.GoVersion)
|
||||
fmt.Printf(" Compiler: %s\n", version.Compiler)
|
||||
fmt.Printf(" Platform: %s\n", version.Platform)
|
||||
}
|
||||
if client {
|
||||
return
|
||||
}
|
||||
|
||||
// Get Server version
|
||||
conn, versionIf := argocdclient.NewClientOrDie(clientOpts).NewVersionClientOrDie()
|
||||
defer util.Close(conn)
|
||||
serverVers, err := versionIf.Version(context.Background(), &empty.Empty{})
|
||||
errors.CheckError(err)
|
||||
fmt.Printf("%s: %s\n", "argocd-server", serverVers.Version)
|
||||
if !short {
|
||||
fmt.Printf(" BuildDate: %s\n", serverVers.BuildDate)
|
||||
fmt.Printf(" GitCommit: %s\n", serverVers.GitCommit)
|
||||
fmt.Printf(" GitTreeState: %s\n", serverVers.GitTreeState)
|
||||
if version.GitTag != "" {
|
||||
fmt.Printf(" GitTag: %s\n", serverVers.GitTag)
|
||||
}
|
||||
fmt.Printf(" GoVersion: %s\n", serverVers.GoVersion)
|
||||
fmt.Printf(" Compiler: %s\n", serverVers.Compiler)
|
||||
fmt.Printf(" Platform: %s\n", serverVers.Platform)
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
versionCmd.Flags().BoolVar(&short, "short", false, "print just the version number")
|
||||
versionCmd.Flags().BoolVar(&client, "client", false, "client version only (no server required)")
|
||||
return &versionCmd
|
||||
}
|
||||
@@ -16,6 +16,12 @@ const (
|
||||
SecretTypeCluster = "cluster"
|
||||
)
|
||||
|
||||
const (
|
||||
ArgoCDAdminUsername = "admin"
|
||||
ArgoCDSecretName = "argocd-secret"
|
||||
ArgoCDConfigMapName = "argocd-cm"
|
||||
)
|
||||
|
||||
var (
|
||||
// LabelKeyAppInstance refers to the application instance resource name
|
||||
LabelKeyAppInstance = MetadataPrefix + "/app-instance"
|
||||
@@ -25,6 +31,7 @@ var (
|
||||
|
||||
// LabelKeyApplicationControllerInstanceID is the label which allows to separate application among multiple running application controllers.
|
||||
LabelKeyApplicationControllerInstanceID = application.ApplicationFullName + "/controller-instanceid"
|
||||
|
||||
// LabelApplicationName is the label which indicates that resource belongs to application with the specified name
|
||||
LabelApplicationName = application.ApplicationFullName + "/app-name"
|
||||
)
|
||||
@@ -44,46 +51,3 @@ var ArgoCDManagerPolicyRules = []rbacv1.PolicyRule{
|
||||
Verbs: []string{"*"},
|
||||
},
|
||||
}
|
||||
|
||||
const (
|
||||
ArgoCDServerServiceAccount = "argocd-server"
|
||||
ArgoCDServerRole = "argocd-server-role"
|
||||
ArgoCDServerRoleBinding = "argocd-server-role-binding"
|
||||
)
|
||||
|
||||
var ArgoCDServerPolicyRules = []rbacv1.PolicyRule{
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"pods", "pods/exec", "pods/log"},
|
||||
Verbs: []string{"get", "list", "watch"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"secrets"},
|
||||
Verbs: []string{"create", "get", "list", "watch", "update", "patch", "delete"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"argoproj.io"},
|
||||
Resources: []string{"applications"},
|
||||
Verbs: []string{"create", "get", "list", "watch", "update", "patch", "delete"},
|
||||
},
|
||||
}
|
||||
|
||||
const (
|
||||
ApplicationControllerServiceAccount = "application-controller"
|
||||
ApplicationControllerRole = "application-controller-role"
|
||||
ApplicationControllerRoleBinding = "application-controller-role-binding"
|
||||
)
|
||||
|
||||
var ApplicationControllerPolicyRules = []rbacv1.PolicyRule{
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"secrets"},
|
||||
Verbs: []string{"get"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"argoproj.io"},
|
||||
Resources: []string{"applications"},
|
||||
Verbs: []string{"create", "get", "list", "watch", "update", "patch", "delete"},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -3,8 +3,10 @@ package controller
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/server/cluster"
|
||||
"github.com/argoproj/argo-cd/util/diff"
|
||||
@@ -12,6 +14,8 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/client-go/dynamic"
|
||||
)
|
||||
|
||||
// AppComparator defines methods which allow to compare application spec and actual application state.
|
||||
@@ -24,6 +28,46 @@ type KsonnetAppComparator struct {
|
||||
clusterService cluster.ClusterServiceServer
|
||||
}
|
||||
|
||||
// groupLiveObjects deduplicate list of kubernetes resources and choose correct version of resource: if resource has corresponding expected application resource then method pick
|
||||
// kubernetes resource with matching version, otherwise chooses single kubernetes resource with any version
|
||||
func (ks *KsonnetAppComparator) groupLiveObjects(liveObjs []*unstructured.Unstructured, targetObjs []*unstructured.Unstructured) map[string]*unstructured.Unstructured {
|
||||
targetByFullName := make(map[string]*unstructured.Unstructured)
|
||||
for _, obj := range targetObjs {
|
||||
targetByFullName[getResourceFullName(obj)] = obj
|
||||
}
|
||||
|
||||
liveListByFullName := make(map[string][]*unstructured.Unstructured)
|
||||
for _, obj := range liveObjs {
|
||||
list := liveListByFullName[getResourceFullName(obj)]
|
||||
if list == nil {
|
||||
list = make([]*unstructured.Unstructured, 0)
|
||||
}
|
||||
list = append(list, obj)
|
||||
liveListByFullName[getResourceFullName(obj)] = list
|
||||
}
|
||||
|
||||
liveByFullName := make(map[string]*unstructured.Unstructured)
|
||||
|
||||
for fullName, list := range liveListByFullName {
|
||||
targetObj := targetByFullName[fullName]
|
||||
var liveObj *unstructured.Unstructured
|
||||
if targetObj != nil {
|
||||
for i := range list {
|
||||
if list[i].GetAPIVersion() == targetObj.GetAPIVersion() {
|
||||
liveObj = list[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
liveObj = list[0]
|
||||
}
|
||||
if liveObj != nil {
|
||||
liveByFullName[getResourceFullName(liveObj)] = liveObj
|
||||
}
|
||||
}
|
||||
return liveByFullName
|
||||
}
|
||||
|
||||
// CompareAppState compares application spec and real app state using KSonnet
|
||||
func (ks *KsonnetAppComparator) CompareAppState(
|
||||
server string,
|
||||
@@ -37,59 +81,170 @@ func (ks *KsonnetAppComparator) CompareAppState(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
restConfig := clst.RESTConfig()
|
||||
|
||||
// Retrieve the live versions of the objects
|
||||
liveObjs, err := kubeutil.GetLiveResources(clst.RESTConfig(), targetObjs, namespace)
|
||||
liveObjs, err := kubeutil.GetResourcesWithLabel(restConfig, namespace, common.LabelApplicationName, app.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
liveObjByFullName := ks.groupLiveObjects(liveObjs, targetObjs)
|
||||
|
||||
controlledLiveObj := make([]*unstructured.Unstructured, len(targetObjs))
|
||||
|
||||
// Move live resources which have corresponding target object to controlledLiveObj
|
||||
dynClientPool := dynamic.NewDynamicClientPool(restConfig)
|
||||
disco, err := discovery.NewDiscoveryClientForConfig(restConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i, targetObj := range targetObjs {
|
||||
fullName := getResourceFullName(targetObj)
|
||||
liveObj := liveObjByFullName[fullName]
|
||||
if liveObj == nil {
|
||||
// If we get here, it indicates we did not find the live resource when querying using
|
||||
// our app label. However, it is possible that the resource was created/modified outside
|
||||
// of ArgoCD. In order to determine that it is truly missing, we fall back to perform a
|
||||
// direct lookup of the resource by name. See issue #141
|
||||
gvk := targetObj.GroupVersionKind()
|
||||
dclient, err := dynClientPool.ClientForGroupVersionKind(gvk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apiResource, err := kubeutil.ServerResourceForGroupVersionKind(disco, gvk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
liveObj, err = kubeutil.GetLiveResource(dclient, targetObj, apiResource, namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
controlledLiveObj[i] = liveObj
|
||||
delete(liveObjByFullName, fullName)
|
||||
}
|
||||
|
||||
// Move root level live resources to controlledLiveObj and add nil to targetObjs to indicate that target object is missing
|
||||
for fullName := range liveObjByFullName {
|
||||
liveObj := liveObjByFullName[fullName]
|
||||
if !hasParent(liveObj) {
|
||||
targetObjs = append(targetObjs, nil)
|
||||
controlledLiveObj = append(controlledLiveObj, liveObj)
|
||||
}
|
||||
}
|
||||
|
||||
// Do the actual comparison
|
||||
diffResults, err := diff.DiffArray(targetObjs, liveObjs)
|
||||
diffResults, err := diff.DiffArray(targetObjs, controlledLiveObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
comparisonStatus := v1alpha1.ComparisonStatusSynced
|
||||
|
||||
resources := make([]v1alpha1.ResourceState, len(targetObjs))
|
||||
for i := 0; i < len(targetObjs); i++ {
|
||||
resState := v1alpha1.ResourceState{}
|
||||
targetObjBytes, err := json.Marshal(targetObjs[i].Object)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
resState := v1alpha1.ResourceState{
|
||||
ChildLiveResources: make([]v1alpha1.ResourceNode, 0),
|
||||
}
|
||||
resState.TargetState = string(targetObjBytes)
|
||||
if liveObjs[i] == nil {
|
||||
resState.LiveState = "null"
|
||||
diffResult := diffResults.Diffs[i]
|
||||
if diffResult.Modified {
|
||||
// Set resource state to 'OutOfSync' since target and corresponding live resource are different
|
||||
resState.Status = v1alpha1.ComparisonStatusOutOfSync
|
||||
comparisonStatus = v1alpha1.ComparisonStatusOutOfSync
|
||||
} else {
|
||||
liveObjBytes, err := json.Marshal(liveObjs[i].Object)
|
||||
resState.Status = v1alpha1.ComparisonStatusSynced
|
||||
}
|
||||
|
||||
if targetObjs[i] == nil {
|
||||
resState.TargetState = "null"
|
||||
// Set resource state to 'OutOfSync' since target resource is missing and live resource is unexpected
|
||||
resState.Status = v1alpha1.ComparisonStatusOutOfSync
|
||||
comparisonStatus = v1alpha1.ComparisonStatusOutOfSync
|
||||
} else {
|
||||
targetObjBytes, err := json.Marshal(targetObjs[i].Object)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resState.TargetState = string(targetObjBytes)
|
||||
}
|
||||
|
||||
if controlledLiveObj[i] == nil {
|
||||
resState.LiveState = "null"
|
||||
// Set resource state to 'OutOfSync' since target resource present but corresponding live resource is missing
|
||||
resState.Status = v1alpha1.ComparisonStatusOutOfSync
|
||||
comparisonStatus = v1alpha1.ComparisonStatusOutOfSync
|
||||
} else {
|
||||
liveObjBytes, err := json.Marshal(controlledLiveObj[i].Object)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resState.LiveState = string(liveObjBytes)
|
||||
}
|
||||
diffResult := diffResults.Diffs[i]
|
||||
if diffResult.Modified {
|
||||
resState.Status = v1alpha1.ComparisonStatusOutOfSync
|
||||
} else {
|
||||
resState.Status = v1alpha1.ComparisonStatusSynced
|
||||
}
|
||||
|
||||
resources[i] = resState
|
||||
}
|
||||
|
||||
for i, resource := range resources {
|
||||
liveResource := controlledLiveObj[i]
|
||||
if liveResource != nil {
|
||||
childResources, err := getChildren(liveResource, liveObjByFullName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resource.ChildLiveResources = childResources
|
||||
resources[i] = resource
|
||||
}
|
||||
}
|
||||
compResult := v1alpha1.ComparisonResult{
|
||||
ComparedTo: app.Spec.Source,
|
||||
ComparedAt: metav1.Time{Time: time.Now().UTC()},
|
||||
Server: clst.Server,
|
||||
Namespace: namespace,
|
||||
Resources: resources,
|
||||
}
|
||||
if diffResults.Modified {
|
||||
compResult.Status = v1alpha1.ComparisonStatusOutOfSync
|
||||
} else {
|
||||
compResult.Status = v1alpha1.ComparisonStatusSynced
|
||||
Status: comparisonStatus,
|
||||
}
|
||||
return &compResult, nil
|
||||
}
|
||||
|
||||
func hasParent(obj *unstructured.Unstructured) bool {
|
||||
// TODO: remove special case after Service and Endpoint get explicit relationship ( https://github.com/kubernetes/kubernetes/issues/28483 )
|
||||
return obj.GetKind() == kubeutil.EndpointsKind || metav1.GetControllerOf(obj) != nil
|
||||
}
|
||||
|
||||
func isControlledBy(obj *unstructured.Unstructured, parent *unstructured.Unstructured) bool {
|
||||
// TODO: remove special case after Service and Endpoint get explicit relationship ( https://github.com/kubernetes/kubernetes/issues/28483 )
|
||||
if obj.GetKind() == kubeutil.EndpointsKind && parent.GetKind() == kubeutil.ServiceKind {
|
||||
return obj.GetName() == parent.GetName()
|
||||
}
|
||||
return metav1.IsControlledBy(obj, parent)
|
||||
}
|
||||
|
||||
func getChildren(parent *unstructured.Unstructured, liveObjByFullName map[string]*unstructured.Unstructured) ([]v1alpha1.ResourceNode, error) {
|
||||
children := make([]v1alpha1.ResourceNode, 0)
|
||||
for fullName, obj := range liveObjByFullName {
|
||||
if isControlledBy(obj, parent) {
|
||||
delete(liveObjByFullName, fullName)
|
||||
childResource := v1alpha1.ResourceNode{}
|
||||
json, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
childResource.State = string(json)
|
||||
childResourceChildren, err := getChildren(obj, liveObjByFullName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
childResource.Children = childResourceChildren
|
||||
children = append(children, childResource)
|
||||
}
|
||||
}
|
||||
return children, nil
|
||||
}
|
||||
|
||||
func getResourceFullName(obj *unstructured.Unstructured) string {
|
||||
return fmt.Sprintf("%s:%s", obj.GetKind(), obj.GetName())
|
||||
}
|
||||
|
||||
// NewKsonnetAppComparator creates new instance of Ksonnet app comparator
|
||||
func NewKsonnetAppComparator(clusterService cluster.ClusterServiceServer) AppComparator {
|
||||
return &KsonnetAppComparator{
|
||||
|
||||
@@ -6,15 +6,19 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"sync"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
|
||||
appinformers "github.com/argoproj/argo-cd/pkg/client/informers/externalversions"
|
||||
"github.com/argoproj/argo-cd/reposerver"
|
||||
"github.com/argoproj/argo-cd/reposerver/repository"
|
||||
"github.com/argoproj/argo-cd/server/cluster"
|
||||
apireposerver "github.com/argoproj/argo-cd/server/repository"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
argoutil "github.com/argoproj/argo-cd/util/argo"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
log "github.com/sirupsen/logrus"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
@@ -23,21 +27,30 @@ import (
|
||||
"k8s.io/apimachinery/pkg/selection"
|
||||
"k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
)
|
||||
|
||||
const (
|
||||
watchResourcesRetryTimeout = 10 * time.Second
|
||||
)
|
||||
|
||||
// ApplicationController is the controller for application resources.
|
||||
type ApplicationController struct {
|
||||
repoClientset reposerver.Clientset
|
||||
kubeClientset kubernetes.Interface
|
||||
applicationClientset appclientset.Interface
|
||||
appQueue workqueue.RateLimitingInterface
|
||||
appInformer cache.SharedIndexInformer
|
||||
appComparator AppComparator
|
||||
statusRefreshTimeout time.Duration
|
||||
apiRepoService apireposerver.RepositoryServiceServer
|
||||
namespace string
|
||||
repoClientset reposerver.Clientset
|
||||
kubeClientset kubernetes.Interface
|
||||
applicationClientset appclientset.Interface
|
||||
appQueue workqueue.RateLimitingInterface
|
||||
appInformer cache.SharedIndexInformer
|
||||
appComparator AppComparator
|
||||
statusRefreshTimeout time.Duration
|
||||
apiRepoService apireposerver.RepositoryServiceServer
|
||||
apiClusterService *cluster.Server
|
||||
forceRefreshApps map[string]bool
|
||||
forceRefreshAppsMutex *sync.Mutex
|
||||
}
|
||||
|
||||
type ApplicationControllerConfig struct {
|
||||
@@ -47,24 +60,30 @@ type ApplicationControllerConfig struct {
|
||||
|
||||
// NewApplicationController creates new instance of ApplicationController.
|
||||
func NewApplicationController(
|
||||
namespace string,
|
||||
kubeClientset kubernetes.Interface,
|
||||
applicationClientset appclientset.Interface,
|
||||
repoClientset reposerver.Clientset,
|
||||
apiRepoService apireposerver.RepositoryServiceServer,
|
||||
apiClusterService *cluster.Server,
|
||||
appComparator AppComparator,
|
||||
appResyncPeriod time.Duration,
|
||||
config *ApplicationControllerConfig,
|
||||
) *ApplicationController {
|
||||
appQueue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())
|
||||
return &ApplicationController{
|
||||
kubeClientset: kubeClientset,
|
||||
applicationClientset: applicationClientset,
|
||||
repoClientset: repoClientset,
|
||||
appQueue: appQueue,
|
||||
apiRepoService: apiRepoService,
|
||||
appComparator: appComparator,
|
||||
appInformer: newApplicationInformer(applicationClientset, appQueue, appResyncPeriod, config),
|
||||
statusRefreshTimeout: appResyncPeriod,
|
||||
namespace: namespace,
|
||||
kubeClientset: kubeClientset,
|
||||
applicationClientset: applicationClientset,
|
||||
repoClientset: repoClientset,
|
||||
appQueue: appQueue,
|
||||
apiRepoService: apiRepoService,
|
||||
apiClusterService: apiClusterService,
|
||||
appComparator: appComparator,
|
||||
appInformer: newApplicationInformer(applicationClientset, appQueue, appResyncPeriod, config),
|
||||
statusRefreshTimeout: appResyncPeriod,
|
||||
forceRefreshApps: make(map[string]bool),
|
||||
forceRefreshAppsMutex: &sync.Mutex{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +93,7 @@ func (ctrl *ApplicationController) Run(ctx context.Context, appWorkers int) {
|
||||
defer ctrl.appQueue.ShutDown()
|
||||
|
||||
go ctrl.appInformer.Run(ctx.Done())
|
||||
go ctrl.watchAppsResources()
|
||||
|
||||
if !cache.WaitForCacheSync(ctx.Done(), ctrl.appInformer.HasSynced) {
|
||||
log.Error("Timed out waiting for caches to sync")
|
||||
@@ -87,6 +107,94 @@ func (ctrl *ApplicationController) Run(ctx context.Context, appWorkers int) {
|
||||
<-ctx.Done()
|
||||
}
|
||||
|
||||
func (ctrl *ApplicationController) forceAppRefresh(appName string) {
|
||||
ctrl.forceRefreshAppsMutex.Lock()
|
||||
defer ctrl.forceRefreshAppsMutex.Unlock()
|
||||
ctrl.forceRefreshApps[appName] = true
|
||||
}
|
||||
|
||||
func (ctrl *ApplicationController) isRefreshForced(appName string) bool {
|
||||
ctrl.forceRefreshAppsMutex.Lock()
|
||||
defer ctrl.forceRefreshAppsMutex.Unlock()
|
||||
_, ok := ctrl.forceRefreshApps[appName]
|
||||
if ok {
|
||||
delete(ctrl.forceRefreshApps, appName)
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
// watchClusterResources watches for resource changes annotated with application label on specified cluster and schedule corresponding app refresh.
|
||||
func (ctrl *ApplicationController) watchClusterResources(ctx context.Context, item appv1.Cluster) {
|
||||
config := item.RESTConfig()
|
||||
retryUntilSucceed(func() error {
|
||||
ch, err := kube.WatchResourcesWithLabel(ctx, config, "", common.LabelApplicationName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for event := range ch {
|
||||
eventObj := event.Object.(*unstructured.Unstructured)
|
||||
objLabels := eventObj.GetLabels()
|
||||
if objLabels == nil {
|
||||
objLabels = make(map[string]string)
|
||||
}
|
||||
if appName, ok := objLabels[common.LabelApplicationName]; ok {
|
||||
ctrl.forceAppRefresh(appName)
|
||||
ctrl.appQueue.Add(ctrl.namespace + "/" + appName)
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("resource updates channel has closed")
|
||||
}, fmt.Sprintf("watch app resources on %s", config.Host), ctx, watchResourcesRetryTimeout)
|
||||
|
||||
}
|
||||
|
||||
// watchAppsResources watches for resource changes annotated with application label on all registered clusters and schedule corresponding app refresh.
|
||||
func (ctrl *ApplicationController) watchAppsResources() {
|
||||
watchingClusters := make(map[string]context.CancelFunc)
|
||||
|
||||
retryUntilSucceed(func() error {
|
||||
return ctrl.apiClusterService.WatchClusters(context.Background(), func(event *cluster.ClusterEvent) {
|
||||
cancel, ok := watchingClusters[event.Cluster.Server]
|
||||
if event.Type == watch.Deleted && ok {
|
||||
cancel()
|
||||
delete(watchingClusters, event.Cluster.Server)
|
||||
} else if event.Type != watch.Deleted && !ok {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
watchingClusters[event.Cluster.Server] = cancel
|
||||
go ctrl.watchClusterResources(ctx, *event.Cluster)
|
||||
}
|
||||
})
|
||||
}, "watch clusters", context.Background(), watchResourcesRetryTimeout)
|
||||
|
||||
<-context.Background().Done()
|
||||
}
|
||||
|
||||
// retryUntilSucceed keep retrying given action with specified timeout until action succeed or specified context is done.
|
||||
func retryUntilSucceed(action func() error, desc string, ctx context.Context, timeout time.Duration) {
|
||||
ctxCompleted := false
|
||||
go func() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
ctxCompleted = true
|
||||
}
|
||||
}()
|
||||
for {
|
||||
err := action()
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
if ctxCompleted {
|
||||
log.Infof("Stop retrying %s", desc)
|
||||
return
|
||||
} else {
|
||||
log.Warnf("Failed to %s: %v, retrying in %v", desc, err, timeout)
|
||||
time.Sleep(timeout)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (ctrl *ApplicationController) processNextItem() bool {
|
||||
appKey, shutdown := ctrl.appQueue.Get()
|
||||
if shutdown {
|
||||
@@ -110,20 +218,21 @@ func (ctrl *ApplicationController) processNextItem() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
if app.NeedRefreshAppStatus(ctrl.statusRefreshTimeout) {
|
||||
updatedApp := app.DeepCopy()
|
||||
status, err := ctrl.tryRefreshAppStatus(updatedApp)
|
||||
isForceRefreshed := ctrl.isRefreshForced(app.Name)
|
||||
if isForceRefreshed || app.NeedRefreshAppStatus(ctrl.statusRefreshTimeout) {
|
||||
log.Infof("Refreshing application '%s' status (force refreshed: %v)", app.Name, isForceRefreshed)
|
||||
|
||||
status, err := ctrl.tryRefreshAppStatus(app.DeepCopy())
|
||||
if err != nil {
|
||||
updatedApp.Status.ComparisonResult = appv1.ComparisonResult{
|
||||
status = app.Status.DeepCopy()
|
||||
status.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()},
|
||||
}
|
||||
} else {
|
||||
updatedApp.Status = *status
|
||||
}
|
||||
ctrl.persistApp(updatedApp)
|
||||
ctrl.updateAppStatus(app.Name, app.Namespace, status)
|
||||
}
|
||||
|
||||
return true
|
||||
@@ -183,6 +292,20 @@ func (ctrl *ApplicationController) tryRefreshAppStatus(app *appv1.Application) (
|
||||
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,
|
||||
Path: app.Spec.Source.Path,
|
||||
Environment: app.Spec.Source.Environment,
|
||||
}
|
||||
params, err := client.GetEnvParams(context.Background(), ¶msReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newStatus.Parameters = make([]appv1.ComponentParameter, len(params.Params))
|
||||
for i := range params.Params {
|
||||
newStatus.Parameters[i] = *params.Params[i]
|
||||
}
|
||||
return &newStatus, nil
|
||||
}
|
||||
|
||||
@@ -191,13 +314,24 @@ func (ctrl *ApplicationController) runWorker() {
|
||||
}
|
||||
}
|
||||
|
||||
func (ctrl *ApplicationController) persistApp(app *appv1.Application) {
|
||||
appClient := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(app.ObjectMeta.Namespace)
|
||||
_, err := appClient.Update(app)
|
||||
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)
|
||||
if err != nil {
|
||||
log.Warnf("Error updating application: %v", err)
|
||||
log.Warnf("Failed to get application '%s' from informer index: %+v", appKey, 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")
|
||||
}
|
||||
|
||||
func newApplicationInformer(
|
||||
|
||||
@@ -9,11 +9,16 @@ An example Ksonnet guestbook application is provided to demonstrates how Argo CD
|
||||
|
||||
## 1. Download Argo CD
|
||||
|
||||
Download the latest Argo CD version from the [releases](https://github.com/argoproj/argo-cd/releases) page.
|
||||
|
||||
## 2. Install the Argo CD components
|
||||
Download the latest Argo CD version
|
||||
```
|
||||
$ argocd install
|
||||
curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/download/v0.3.1/argocd-darwin-amd64
|
||||
chmod +x /usr/local/bin/argocd
|
||||
```
|
||||
|
||||
|
||||
## 2. Install Argo CD
|
||||
```
|
||||
argocd install
|
||||
```
|
||||
This will create a new namespace, `argocd`, where Argo CD services and application resources will live.
|
||||
|
||||
@@ -23,24 +28,23 @@ By default, the Argo CD API server is not exposed with an external IP. To expose
|
||||
change service type to `LoadBalancer`:
|
||||
|
||||
```
|
||||
$ kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "LoadBalancer"}}'
|
||||
kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "LoadBalancer"}}'
|
||||
```
|
||||
|
||||
Export API server URL into `ARGOCD_SERVER` environment variable, which the CLI looks to for
|
||||
connection information:
|
||||
# 4. Login to the server from the CLI
|
||||
|
||||
```
|
||||
$ export ARGOCD_SERVER=$(minikube service argocd-server -n argocd --url | cut -d'/' -f 3)
|
||||
argocd login $(minikube service argocd-server -n argocd --url | cut -d'/' -f 3)
|
||||
```
|
||||
|
||||
Now, Argo CD is able to talk to API server and you can deploy your first application.
|
||||
Now, the Argo CD cli is configured to talk to API server and you can deploy your first application.
|
||||
|
||||
## 4. Connect and deploy the Guestbook application
|
||||
## 5. Connect and deploy the Guestbook application
|
||||
|
||||
1. Register the minikube cluster to Argo CD:
|
||||
|
||||
```
|
||||
$ argocd cluster add minikube
|
||||
argocd cluster add minikube
|
||||
```
|
||||
The `argocd cluster add CONTEXT` command installs an `argocd-manager` ServiceAccount and ClusterRole into
|
||||
the cluster associated with the supplied kubectl context. Argo CD then uses the associated service account
|
||||
@@ -49,21 +53,21 @@ token to perform its required management tasks (i.e. deploy/monitoring).
|
||||
2. Add the guestbook application and github repository containing the Guestbook application
|
||||
|
||||
```
|
||||
$ argocd app add --name guestbook --repo https://github.com/argoproj/argo-cd.git --path examples/guestbook --env minikube --dest-server https://$(minikube ip):8443
|
||||
argocd app create --name guestbook --repo https://github.com/argoproj/argo-cd.git --path examples/guestbook --env minikube --dest-server https://$(minikube ip):8443
|
||||
```
|
||||
|
||||
Once the application is added, you can now see its status:
|
||||
|
||||
```
|
||||
$ argocd app list
|
||||
$ argocd app sync guestbook
|
||||
argocd app list
|
||||
argocd app get guestbook
|
||||
```
|
||||
|
||||
The application status is initially in an `OutOfSync` state, since the application has yet to be
|
||||
deployed, and no Kubernetes resouces have been created. To sync (deploy) the application, run:
|
||||
deployed, and no Kubernetes resources have been created. To sync (deploy) the application, run:
|
||||
|
||||
```
|
||||
$ argocd app sync guestbook
|
||||
argocd app sync guestbook
|
||||
```
|
||||
|
||||
[](https://asciinema.org/a/uYnbFMy5WI2rc9S49oEAyGLb0)
|
||||
@@ -71,7 +75,7 @@ $ argocd app sync guestbook
|
||||
Argo CD also allows to view and manager applications using web UI. Get the web UI URL by running:
|
||||
|
||||
```
|
||||
$ minikube service argocd-server -n argocd --url
|
||||
minikube service argocd-server -n argocd --url
|
||||
```
|
||||
|
||||

|
||||
40
docs/tracking_strategies.md
Normal file
40
docs/tracking_strategies.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Tracking and Deployment Strategies
|
||||
|
||||
An ArgoCD application spec provides several different ways of track kubernetes resource manifests in git. This document describes the different techniques and the means of deploying those manifests to the target environment.
|
||||
|
||||
## Auto-Sync
|
||||
|
||||
In all tracking strategies described below, the application has the option to sync automatically. If auto-sync is configured, the new resources manifests will be applied automatically -- as soon as a difference is detected between the target state (git) and live state. If auto-sync is disabled, a manual sync will be needed using the Argo UI, CLI, or API.
|
||||
|
||||
## Branch Tracking
|
||||
|
||||
If a branch name is specified, ArgoCD will continually compare live state against the resource manifests defined at the tip of the specified branch.
|
||||
|
||||
To redeploy an application, a user makes changes to the manifests, and commit/pushes those the changes to the tracked branch, which will then be detected by ArgoCD controller.
|
||||
|
||||
## Tag Tracking
|
||||
|
||||
If a tag is specified, the manifests at the specified git tag will be used to perform the sync comparison. This provides some advantages over branch tracking in that a tag is generally considered more stable, and less frequently updated, with some manual judgement of what constitutes a tag.
|
||||
|
||||
To redeploy an application, the user uses git to change the meaning of a tag by retagging it to a different commit SHA. ArgoCD will detect the new meaning of the tag when performing the comparison/sync.
|
||||
|
||||
## Commit Pinning
|
||||
|
||||
If a git commit SHA is specified, the application is effectively pinned to the manifests defined at the specified commit. This is the most restrictive of the techniques and is typically used to control production environments.
|
||||
|
||||
Since commit SHAs cannot change meaning, the only way to change the live state of an application which is pinned to a commit, is by updating the tracking revision in the application to a different commit containing the new manifests.
|
||||
|
||||
## Parameter Overrides
|
||||
|
||||
ArgoCD provides means to override the parameters of a ksonnet app. This gives some extra flexibility in having *some* parts of the k8s manifests determined dynamically. It also serves as an alternative way of redeploying an application by changing application parameters via ArgoCD, instead of making the changes to the manifests in git.
|
||||
|
||||
The following is an example of where this would be useful: A team maintains a "dev" environment, which needs to be continually updated with the latest version of their guestbook application after every build in the tip of master. To solve this, the ksonnet application would expose an parameter named `image`, whose value used in the `dev` environment contains a placeholder value (e.g. `example/guestbook:replaceme`) intended to be set externally (outside of git) such as by build systems. As part of the build pipeline, the parameter value of the `image` would be continually updated to the freshly built image (e.g. `example/guestbook:abcd123`). A sync operation would result in the application being redeployed with the new image.
|
||||
|
||||
ArgoCD provides these operations conveniently via the CLI, or alternatively via the gRPC/REST API.
|
||||
```
|
||||
$ argocd app set guestbook -p guestbook=image=example/guestbook:abcd123
|
||||
$ argocd app sync guestbook
|
||||
```
|
||||
|
||||
Note that in all tracking strategies, any parameter overrides set in the application instance will be honored.
|
||||
|
||||
@@ -39,10 +39,19 @@ go-to-protobuf \
|
||||
--apimachinery-packages=$(IFS=, ; echo "${APIMACHINERY_PKGS[*]}") \
|
||||
--proto-import=./vendor
|
||||
|
||||
# protoc-gen-go or protoc-gen-gofast is used to build server/*/<service>.pb.go from .proto files
|
||||
# NOTE: it is possible to use golang/protobuf or gogo/protobuf interchangeably
|
||||
go build -i -o dist/protoc-gen-gofast ./vendor/github.com/gogo/protobuf/protoc-gen-gofast
|
||||
# Either protoc-gen-go, protoc-gen-gofast, or protoc-gen-gogofast can be used to build
|
||||
# server/*/<service>.pb.go from .proto files. golang/protobuf and gogo/protobuf can be used
|
||||
# interchangeably. The difference in the options are:
|
||||
# 1. protoc-gen-go - official golang/protobuf
|
||||
#go build -i -o dist/protoc-gen-go ./vendor/github.com/golang/protobuf/protoc-gen-go
|
||||
#GOPROTOBINARY=go
|
||||
# 2. protoc-gen-gofast - fork of golang golang/protobuf. Faster code generation
|
||||
#go build -i -o dist/protoc-gen-gofast ./vendor/github.com/gogo/protobuf/protoc-gen-gofast
|
||||
#GOPROTOBINARY=gofast
|
||||
# 3. protoc-gen-gogofast - faster code generation and gogo extensions and flexibility in controlling
|
||||
# the generated go code (e.g. customizing field names, nullable fields)
|
||||
go build -i -o dist/protoc-gen-gogofast ./vendor/github.com/gogo/protobuf/protoc-gen-gogofast
|
||||
GOPROTOBINARY=gogofast
|
||||
|
||||
# protoc-gen-grpc-gateway is used to build <service>.pb.gw.go files from from .proto files
|
||||
go build -i -o dist/protoc-gen-grpc-gateway ./vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
|
||||
@@ -50,7 +59,6 @@ go build -i -o dist/protoc-gen-grpc-gateway ./vendor/github.com/grpc-ecosystem/g
|
||||
# Generate server/<service>/(<service>.pb.go|<service>.pb.gw.go)
|
||||
PROTO_FILES=$(find $PROJECT_ROOT \( -name "*.proto" -and -path '*/server/*' -or -path '*/reposerver/*' -and -name "*.proto" \))
|
||||
for i in ${PROTO_FILES}; do
|
||||
|
||||
# Path to the google API gateway annotations.proto will be different depending if we are
|
||||
# building natively (e.g. from workspace) vs. part of a docker build.
|
||||
if [ -f /.dockerenv ]; then
|
||||
@@ -67,7 +75,7 @@ for i in ${PROTO_FILES}; do
|
||||
-I$GOPATH/src \
|
||||
-I${GOOGLE_PROTO_API_PATH} \
|
||||
-I${GOGO_PROTOBUF_PATH} \
|
||||
--go_out=plugins=grpc:$GOPATH/src \
|
||||
--${GOPROTOBINARY}_out=plugins=grpc:$GOPATH/src \
|
||||
--grpc-gateway_out=logtostderr=true:$GOPATH/src \
|
||||
$i
|
||||
done
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
package install
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
"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"
|
||||
tlsutil "github.com/argoproj/argo-cd/util/tls"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/gobuffalo/packr"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/yudai/gojsondiff/formatter"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
appsv1beta2 "k8s.io/api/apps/v1beta2"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
apierr "k8s.io/apimachinery/pkg/api/errors"
|
||||
@@ -50,6 +50,7 @@ type InstallOptions struct {
|
||||
DryRun bool
|
||||
Upgrade bool
|
||||
ConfigSuperuser bool
|
||||
CreateSignature bool
|
||||
ConfigMap string
|
||||
Namespace string
|
||||
ControllerImage string
|
||||
@@ -74,6 +75,9 @@ func NewInstaller(config *rest.Config, opts InstallOptions) (*Installer, error)
|
||||
box: packr.NewBox("./manifests"),
|
||||
config: &shallowCopy,
|
||||
}
|
||||
if opts.Namespace == "" {
|
||||
inst.Namespace = DefaultInstallNamespace
|
||||
}
|
||||
var err error
|
||||
inst.dynClientPool = dynamic.NewDynamicClientPool(inst.config)
|
||||
inst.disco, err = discovery.NewDiscoveryClientForConfig(inst.config)
|
||||
@@ -86,6 +90,7 @@ func NewInstaller(config *rest.Config, opts InstallOptions) (*Installer, error)
|
||||
func (i *Installer) Install() {
|
||||
i.InstallNamespace()
|
||||
i.InstallApplicationCRD()
|
||||
i.InstallSettings()
|
||||
i.InstallApplicationController()
|
||||
i.InstallArgoCDServer()
|
||||
i.InstallArgoCDRepoServer()
|
||||
@@ -104,12 +109,6 @@ func (i *Installer) Uninstall() {
|
||||
i.MustUninstallResource(&obj)
|
||||
}
|
||||
}
|
||||
|
||||
// i.InstallNamespace()
|
||||
// i.InstallApplicationCRD()
|
||||
// i.InstallApplicationController()
|
||||
// i.InstallArgoCDServer()
|
||||
// i.InstallArgoCDRepoServer()
|
||||
}
|
||||
|
||||
func (i *Installer) InstallNamespace() {
|
||||
@@ -129,15 +128,81 @@ func (i *Installer) InstallApplicationCRD() {
|
||||
i.MustInstallResource(kube.MustToUnstructured(&applicationCRD))
|
||||
}
|
||||
|
||||
func (i *Installer) InstallSettings() {
|
||||
kubeclientset, err := kubernetes.NewForConfig(i.config)
|
||||
errors.CheckError(err)
|
||||
configManager := config.NewConfigManager(kubeclientset, i.Namespace)
|
||||
_, err = configManager.GetSettings()
|
||||
if err == nil {
|
||||
log.Infof("Settings already exists. Skipping creation")
|
||||
return
|
||||
}
|
||||
if !apierr.IsNotFound(err) {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// configmap/secret not yet created
|
||||
var newSettings config.ArgoCDSettings
|
||||
|
||||
// set JWT signature
|
||||
signature, err := session.MakeSignature(32)
|
||||
errors.CheckError(err)
|
||||
newSettings.ServerSignature = signature
|
||||
|
||||
// generate admin password
|
||||
passwordRaw := readAndConfirmPassword()
|
||||
hashedPassword, err := password.HashPassword(passwordRaw)
|
||||
errors.CheckError(err)
|
||||
newSettings.LocalUsers = map[string]string{
|
||||
common.ArgoCDAdminUsername: hashedPassword,
|
||||
}
|
||||
|
||||
// generate TLS cert
|
||||
hosts := []string{
|
||||
"localhost",
|
||||
"argocd-server",
|
||||
fmt.Sprintf("argocd-server.%s", i.Namespace),
|
||||
fmt.Sprintf("argocd-server.%s.svc", i.Namespace),
|
||||
fmt.Sprintf("argocd-server.%s.svc.cluster.local", i.Namespace),
|
||||
}
|
||||
certOpts := tlsutil.CertOptions{
|
||||
Hosts: hosts,
|
||||
Organization: "Argo CD",
|
||||
IsCA: true,
|
||||
}
|
||||
cert, err := tlsutil.GenerateX509KeyPair(certOpts)
|
||||
errors.CheckError(err)
|
||||
newSettings.Certificate = cert
|
||||
|
||||
err = configManager.SaveSettings(&newSettings)
|
||||
errors.CheckError(err)
|
||||
}
|
||||
|
||||
func readAndConfirmPassword() string {
|
||||
for {
|
||||
fmt.Print("*** Enter an admin password: ")
|
||||
password, err := terminal.ReadPassword(syscall.Stdin)
|
||||
errors.CheckError(err)
|
||||
fmt.Print("\n")
|
||||
fmt.Print("*** Confirm the admin password: ")
|
||||
confirmPassword, err := terminal.ReadPassword(syscall.Stdin)
|
||||
errors.CheckError(err)
|
||||
fmt.Print("\n")
|
||||
if string(password) == string(confirmPassword) {
|
||||
return string(password)
|
||||
}
|
||||
log.Error("Passwords do not match")
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Installer) InstallApplicationController() {
|
||||
var applicationControllerServiceAccount apiv1.ServiceAccount
|
||||
var applicationControllerRole rbacv1.Role
|
||||
var applicationControllerRoleBinding rbacv1.RoleBinding
|
||||
var applicationControllerDeployment appsv1beta2.Deployment
|
||||
i.unmarshalManifest("02a_application-controller-sa.yaml", &applicationControllerServiceAccount)
|
||||
i.unmarshalManifest("02b_application-controller-role.yaml", &applicationControllerRole)
|
||||
i.unmarshalManifest("02c_application-controller-rolebinding.yaml", &applicationControllerRoleBinding)
|
||||
i.unmarshalManifest("02d_application-controller-deployment.yaml", &applicationControllerDeployment)
|
||||
i.unmarshalManifest("03a_application-controller-sa.yaml", &applicationControllerServiceAccount)
|
||||
i.unmarshalManifest("03b_application-controller-role.yaml", &applicationControllerRole)
|
||||
i.unmarshalManifest("03c_application-controller-rolebinding.yaml", &applicationControllerRoleBinding)
|
||||
i.unmarshalManifest("03d_application-controller-deployment.yaml", &applicationControllerDeployment)
|
||||
applicationControllerDeployment.Spec.Template.Spec.Containers[0].Image = i.ControllerImage
|
||||
applicationControllerDeployment.Spec.Template.Spec.Containers[0].ImagePullPolicy = apiv1.PullPolicy(i.ImagePullPolicy)
|
||||
i.MustInstallResource(kube.MustToUnstructured(&applicationControllerServiceAccount))
|
||||
@@ -152,57 +217,28 @@ func (i *Installer) InstallArgoCDServer() {
|
||||
var argoCDServerControllerRoleBinding rbacv1.RoleBinding
|
||||
var argoCDServerControllerDeployment appsv1beta2.Deployment
|
||||
var argoCDServerService apiv1.Service
|
||||
i.unmarshalManifest("03a_argocd-server-sa.yaml", &argoCDServerServiceAccount)
|
||||
i.unmarshalManifest("03b_argocd-server-role.yaml", &argoCDServerControllerRole)
|
||||
i.unmarshalManifest("03c_argocd-server-rolebinding.yaml", &argoCDServerControllerRoleBinding)
|
||||
i.unmarshalManifest("03d_argocd-server-deployment.yaml", &argoCDServerControllerDeployment)
|
||||
i.unmarshalManifest("03e_argocd-server-service.yaml", &argoCDServerService)
|
||||
i.unmarshalManifest("04a_argocd-server-sa.yaml", &argoCDServerServiceAccount)
|
||||
i.unmarshalManifest("04b_argocd-server-role.yaml", &argoCDServerControllerRole)
|
||||
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].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)
|
||||
|
||||
kubeclientset, err := kubernetes.NewForConfig(i.config)
|
||||
errors.CheckError(err)
|
||||
|
||||
configManager := util.NewConfigManager(kubeclientset, i.Namespace, i.ConfigMap)
|
||||
errors.CheckError(err)
|
||||
|
||||
if i.InstallOptions.ConfigMap != "" {
|
||||
quotedConfigMapName := strconv.Quote(i.InstallOptions.ConfigMap)
|
||||
container := &argoCDServerControllerDeployment.Spec.Template.Spec.Containers[0]
|
||||
container.Command = append(container.Command, "--config-map", quotedConfigMapName)
|
||||
}
|
||||
|
||||
i.MustInstallResource(kube.MustToUnstructured(&argoCDServerServiceAccount))
|
||||
i.MustInstallResource(kube.MustToUnstructured(&argoCDServerControllerRole))
|
||||
i.MustInstallResource(kube.MustToUnstructured(&argoCDServerControllerRoleBinding))
|
||||
i.MustInstallResource(kube.MustToUnstructured(&argoCDServerControllerDeployment))
|
||||
i.MustInstallResource(kube.MustToUnstructured(&argoCDServerService))
|
||||
|
||||
if i.InstallOptions.ConfigSuperuser {
|
||||
inputReader := bufio.NewReader(os.Stdin)
|
||||
|
||||
fmt.Print("*** Please enter a superuser username: ")
|
||||
rootUsername, err := inputReader.ReadString('\n')
|
||||
errors.CheckError(err)
|
||||
rootUsername = strings.Trim(rootUsername, "\n")
|
||||
|
||||
fmt.Print("*** Please enter a superuser password: ")
|
||||
rawPassword, err := terminal.ReadPassword(syscall.Stdin)
|
||||
errors.CheckError(err)
|
||||
fmt.Print("\n")
|
||||
|
||||
err = configManager.SetRootUserCredentials(rootUsername, string(rawPassword))
|
||||
errors.CheckError(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Installer) InstallArgoCDRepoServer() {
|
||||
var argoCDRepoServerControllerDeployment appsv1beta2.Deployment
|
||||
var argoCDRepoServerService apiv1.Service
|
||||
i.unmarshalManifest("04a_argocd-repo-server-deployment.yaml", &argoCDRepoServerControllerDeployment)
|
||||
i.unmarshalManifest("04b_argocd-repo-server-service.yaml", &argoCDRepoServerService)
|
||||
i.unmarshalManifest("05a_argocd-repo-server-deployment.yaml", &argoCDRepoServerControllerDeployment)
|
||||
i.unmarshalManifest("05b_argocd-repo-server-service.yaml", &argoCDRepoServerService)
|
||||
argoCDRepoServerControllerDeployment.Spec.Template.Spec.Containers[0].Image = i.RepoServerImage
|
||||
argoCDRepoServerControllerDeployment.Spec.Template.Spec.Containers[0].ImagePullPolicy = apiv1.PullPolicy(i.ImagePullPolicy)
|
||||
i.MustInstallResource(kube.MustToUnstructured(&argoCDRepoServerControllerDeployment))
|
||||
|
||||
7
install/manifests/02a_argocd-cm.yaml
Normal file
7
install/manifests/02a_argocd-cm.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: argocd-cm
|
||||
namespace: argocd
|
||||
# TODO: future argocd tuning keys go here (e.g. resync period)
|
||||
data: {}
|
||||
13
install/manifests/02b_argocd-secret.yaml
Normal file
13
install/manifests/02b_argocd-secret.yaml
Normal file
@@ -0,0 +1,13 @@
|
||||
# NOTE: the values in this secret are provided as working manifest example and are not the values
|
||||
# used during an install. New values will be generated as part of `argocd install`
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: argocd-secret
|
||||
namespace: argocd
|
||||
type: Opaque
|
||||
data:
|
||||
# bcrypt hash of 'password'
|
||||
admin.password: JDJhJDEwJGVYYkZmOEt3NUMzTDJVbE9FRDNqUU9QMC5reVNBamVLUXY0N3NqaFFpWlZwTkkyU2dMTzd1
|
||||
# random server signature key for session validation
|
||||
server.secretkey: aEDvv73vv70F77+9CRBSNu+/vTYQ77+9EUFh77+9LzFyJ++/vXfLsO+/vWRbeu+/ve+/vQ==
|
||||
@@ -10,6 +10,7 @@ rules:
|
||||
- secrets
|
||||
verbs:
|
||||
- get
|
||||
- watch
|
||||
- apiGroups:
|
||||
- argoproj.io
|
||||
resources:
|
||||
@@ -10,4 +10,3 @@ roleRef:
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: application-controller
|
||||
namespace: argocd
|
||||
@@ -10,4 +10,3 @@ roleRef:
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: argocd-server
|
||||
namespace: argocd
|
||||
@@ -5,7 +5,13 @@ metadata:
|
||||
namespace: argocd
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
- name: http
|
||||
protocol: TCP
|
||||
port: 80
|
||||
targetPort: 8080
|
||||
- name: https
|
||||
protocol: TCP
|
||||
port: 443
|
||||
targetPort: 8080
|
||||
selector:
|
||||
app: argocd-server
|
||||
@@ -6,5 +6,6 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 8081
|
||||
targetPort: 8081
|
||||
selector:
|
||||
app: argocd-repo-server
|
||||
@@ -1,21 +1,36 @@
|
||||
package apiclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"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/version"
|
||||
grpc_util "github.com/argoproj/argo-cd/util/grpc"
|
||||
"github.com/argoproj/argo-cd/util/localconfig"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
)
|
||||
|
||||
const (
|
||||
// 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
|
||||
EnvArgoCDAuthToken = "ARGOCD_AUTH_TOKEN"
|
||||
)
|
||||
|
||||
// ServerClient defines an interface for interaction with an Argo CD server.
|
||||
type ServerClient interface {
|
||||
NewConn() (*grpc.ClientConn, error)
|
||||
NewRepoClient() (*grpc.ClientConn, repository.RepositoryServiceClient, error)
|
||||
@@ -24,30 +39,97 @@ type ServerClient interface {
|
||||
NewClusterClientOrDie() (*grpc.ClientConn, cluster.ClusterServiceClient)
|
||||
NewApplicationClient() (*grpc.ClientConn, application.ApplicationServiceClient, error)
|
||||
NewApplicationClientOrDie() (*grpc.ClientConn, application.ApplicationServiceClient)
|
||||
NewSessionClient() (*grpc.ClientConn, session.SessionServiceClient, error)
|
||||
NewSessionClientOrDie() (*grpc.ClientConn, session.SessionServiceClient)
|
||||
NewVersionClient() (*grpc.ClientConn, version.VersionServiceClient, error)
|
||||
NewVersionClientOrDie() (*grpc.ClientConn, version.VersionServiceClient)
|
||||
}
|
||||
|
||||
// ClientOptions hold address, security, and other settings for the API client.
|
||||
type ClientOptions struct {
|
||||
ServerAddr string
|
||||
PlainText bool
|
||||
Insecure bool
|
||||
CertFile string
|
||||
AuthToken string
|
||||
ConfigPath string
|
||||
Context string
|
||||
}
|
||||
|
||||
type client struct {
|
||||
ClientOptions
|
||||
ServerAddr string
|
||||
PlainText bool
|
||||
Insecure bool
|
||||
CertPEMData []byte
|
||||
AuthToken string
|
||||
}
|
||||
|
||||
// NewClient creates a new API client from a set of config options.
|
||||
func NewClient(opts *ClientOptions) (ServerClient, error) {
|
||||
clientOpts := *opts
|
||||
if clientOpts.ServerAddr == "" {
|
||||
clientOpts.ServerAddr = os.Getenv(EnvArgoCDServer)
|
||||
var c client
|
||||
localCfg, err := localconfig.ReadLocalConfig(opts.ConfigPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if clientOpts.ServerAddr == "" {
|
||||
return nil, errors.New("Argo CD server address not supplied")
|
||||
if localCfg != nil {
|
||||
configCtx, err := localCfg.ResolveContext(opts.Context)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if configCtx != nil {
|
||||
c.ServerAddr = configCtx.Server.Server
|
||||
if configCtx.Server.CACertificateAuthorityData != "" {
|
||||
c.CertPEMData, err = base64.StdEncoding.DecodeString(configCtx.Server.CACertificateAuthorityData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
c.PlainText = configCtx.Server.PlainText
|
||||
c.Insecure = configCtx.Server.Insecure
|
||||
c.AuthToken = configCtx.User.AuthToken
|
||||
}
|
||||
}
|
||||
return &client{
|
||||
ClientOptions: clientOpts,
|
||||
}, nil
|
||||
// Override server address if specified in env or CLI flag
|
||||
if serverFromEnv := os.Getenv(EnvArgoCDServer); serverFromEnv != "" {
|
||||
c.ServerAddr = serverFromEnv
|
||||
}
|
||||
if opts.ServerAddr != "" {
|
||||
c.ServerAddr = opts.ServerAddr
|
||||
}
|
||||
// Make sure we got the server address and auth token from somewhere
|
||||
if c.ServerAddr == "" {
|
||||
return nil, errors.New("ArgoCD server address unspecified")
|
||||
}
|
||||
if parts := strings.Split(c.ServerAddr, ":"); len(parts) == 1 {
|
||||
// If port is unspecified, assume the most likely port
|
||||
c.ServerAddr += ":443"
|
||||
}
|
||||
// Override auth-token if specified in env variable or CLI flag
|
||||
if authFromEnv := os.Getenv(EnvArgoCDAuthToken); authFromEnv != "" {
|
||||
c.AuthToken = authFromEnv
|
||||
}
|
||||
if opts.AuthToken != "" {
|
||||
c.AuthToken = opts.AuthToken
|
||||
}
|
||||
// Override certificate data if specified from CLI flag
|
||||
if opts.CertFile != "" {
|
||||
b, err := ioutil.ReadFile(opts.CertFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.CertPEMData = b
|
||||
}
|
||||
// Override insecure/plaintext options if specified from CLI
|
||||
if opts.PlainText {
|
||||
c.PlainText = true
|
||||
}
|
||||
if opts.Insecure {
|
||||
c.Insecure = true
|
||||
}
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
// NewClientOrDie creates a new API client from a set of config options, or fails fatally if the new client creation fails.
|
||||
func NewClientOrDie(opts *ClientOptions) ServerClient {
|
||||
client, err := NewClient(opts)
|
||||
if err != nil {
|
||||
@@ -56,16 +138,41 @@ func NewClientOrDie(opts *ClientOptions) ServerClient {
|
||||
return client
|
||||
}
|
||||
|
||||
// JwtCredentials holds a token for authentication.
|
||||
type jwtCredentials struct {
|
||||
Token string
|
||||
}
|
||||
|
||||
func (c jwtCredentials) RequireTransportSecurity() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (c jwtCredentials) GetRequestMetadata(context.Context, ...string) (map[string]string, error) {
|
||||
return map[string]string{
|
||||
"tokens": c.Token,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *client) NewConn() (*grpc.ClientConn, error) {
|
||||
var dialOpts []grpc.DialOption
|
||||
if c.Insecure {
|
||||
dialOpts = append(dialOpts, grpc.WithInsecure())
|
||||
} else {
|
||||
return nil, errors.New("secure authentication unsupported")
|
||||
} // else if opts.Credentials != nil {
|
||||
// dialOpts = append(dialOpts, grpc.WithTransportCredentials(opts.Credentials))
|
||||
//}
|
||||
return grpc.Dial(c.ServerAddr, dialOpts...)
|
||||
var creds credentials.TransportCredentials
|
||||
if !c.PlainText {
|
||||
var tlsConfig tls.Config
|
||||
if len(c.CertPEMData) > 0 {
|
||||
cp := x509.NewCertPool()
|
||||
if !cp.AppendCertsFromPEM(c.CertPEMData) {
|
||||
return nil, fmt.Errorf("credentials: failed to append certificates")
|
||||
}
|
||||
tlsConfig.RootCAs = cp
|
||||
}
|
||||
if c.Insecure {
|
||||
tlsConfig.InsecureSkipVerify = true
|
||||
}
|
||||
creds = credentials.NewTLS(&tlsConfig)
|
||||
}
|
||||
endpointCredentials := jwtCredentials{
|
||||
Token: c.AuthToken,
|
||||
}
|
||||
return grpc_util.BlockingDial(context.Background(), "tcp", c.ServerAddr, creds, grpc.WithPerRPCCredentials(endpointCredentials))
|
||||
}
|
||||
|
||||
func (c *client) NewRepoClient() (*grpc.ClientConn, repository.RepositoryServiceClient, error) {
|
||||
@@ -118,3 +225,37 @@ func (c *client) NewApplicationClientOrDie() (*grpc.ClientConn, application.Appl
|
||||
}
|
||||
return conn, repoIf
|
||||
}
|
||||
|
||||
func (c *client) NewSessionClient() (*grpc.ClientConn, session.SessionServiceClient, error) {
|
||||
conn, err := c.NewConn()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sessionIf := session.NewSessionServiceClient(conn)
|
||||
return conn, sessionIf, nil
|
||||
}
|
||||
|
||||
func (c *client) NewSessionClientOrDie() (*grpc.ClientConn, session.SessionServiceClient) {
|
||||
conn, sessionIf, err := c.NewSessionClient()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to establish connection to %s: %v", c.ServerAddr, err)
|
||||
}
|
||||
return conn, sessionIf
|
||||
}
|
||||
|
||||
func (c *client) NewVersionClient() (*grpc.ClientConn, version.VersionServiceClient, error) {
|
||||
conn, err := c.NewConn()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
versionIf := version.NewVersionServiceClient(conn)
|
||||
return conn, versionIf, nil
|
||||
}
|
||||
|
||||
func (c *client) NewVersionClientOrDie() (*grpc.ClientConn, version.VersionServiceClient) {
|
||||
conn, versionIf, err := c.NewVersionClient()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to establish connection to %s: %v", c.ServerAddr, err)
|
||||
}
|
||||
return conn, versionIf
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
DeploymentInfo
|
||||
Repository
|
||||
RepositoryList
|
||||
ResourceNode
|
||||
ResourceState
|
||||
TLSClientConfig
|
||||
*/
|
||||
@@ -110,13 +111,17 @@ func (m *RepositoryList) Reset() { *m = RepositoryList{} }
|
||||
func (*RepositoryList) ProtoMessage() {}
|
||||
func (*RepositoryList) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{14} }
|
||||
|
||||
func (m *ResourceNode) Reset() { *m = ResourceNode{} }
|
||||
func (*ResourceNode) ProtoMessage() {}
|
||||
func (*ResourceNode) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{15} }
|
||||
|
||||
func (m *ResourceState) Reset() { *m = ResourceState{} }
|
||||
func (*ResourceState) ProtoMessage() {}
|
||||
func (*ResourceState) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{15} }
|
||||
func (*ResourceState) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{16} }
|
||||
|
||||
func (m *TLSClientConfig) Reset() { *m = TLSClientConfig{} }
|
||||
func (*TLSClientConfig) ProtoMessage() {}
|
||||
func (*TLSClientConfig) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{16} }
|
||||
func (*TLSClientConfig) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{17} }
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Application)(nil), "github.com.argoproj.argo_cd.pkg.apis.application.v1alpha1.Application")
|
||||
@@ -134,6 +139,7 @@ func init() {
|
||||
proto.RegisterType((*DeploymentInfo)(nil), "github.com.argoproj.argo_cd.pkg.apis.application.v1alpha1.DeploymentInfo")
|
||||
proto.RegisterType((*Repository)(nil), "github.com.argoproj.argo_cd.pkg.apis.application.v1alpha1.Repository")
|
||||
proto.RegisterType((*RepositoryList)(nil), "github.com.argoproj.argo_cd.pkg.apis.application.v1alpha1.RepositoryList")
|
||||
proto.RegisterType((*ResourceNode)(nil), "github.com.argoproj.argo_cd.pkg.apis.application.v1alpha1.ResourceNode")
|
||||
proto.RegisterType((*ResourceState)(nil), "github.com.argoproj.argo_cd.pkg.apis.application.v1alpha1.ResourceState")
|
||||
proto.RegisterType((*TLSClientConfig)(nil), "github.com.argoproj.argo_cd.pkg.apis.application.v1alpha1.TLSClientConfig")
|
||||
}
|
||||
@@ -322,6 +328,10 @@ func (m *ApplicationSpec) MarshalTo(dAtA []byte) (int, error) {
|
||||
}
|
||||
i += n6
|
||||
}
|
||||
dAtA[i] = 0x1a
|
||||
i++
|
||||
i = encodeVarintGenerated(dAtA, i, uint64(len(m.SyncPolicy)))
|
||||
i += copy(dAtA[i:], m.SyncPolicy)
|
||||
return i, nil
|
||||
}
|
||||
|
||||
@@ -360,6 +370,18 @@ func (m *ApplicationStatus) MarshalTo(dAtA []byte) (int, error) {
|
||||
i += n
|
||||
}
|
||||
}
|
||||
if len(m.Parameters) > 0 {
|
||||
for _, msg := range m.Parameters {
|
||||
dAtA[i] = 0x1a
|
||||
i++
|
||||
i = encodeVarintGenerated(dAtA, i, uint64(msg.Size()))
|
||||
n, err := msg.MarshalTo(dAtA[i:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i += n
|
||||
}
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
@@ -646,6 +668,9 @@ func (m *DeploymentInfo) MarshalTo(dAtA []byte) (int, error) {
|
||||
return 0, err
|
||||
}
|
||||
i += n14
|
||||
dAtA[i] = 0x28
|
||||
i++
|
||||
i = encodeVarintGenerated(dAtA, i, uint64(m.ID))
|
||||
return i, nil
|
||||
}
|
||||
|
||||
@@ -721,6 +746,40 @@ func (m *RepositoryList) MarshalTo(dAtA []byte) (int, error) {
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (m *ResourceNode) 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 *ResourceNode) MarshalTo(dAtA []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
dAtA[i] = 0xa
|
||||
i++
|
||||
i = encodeVarintGenerated(dAtA, i, uint64(len(m.State)))
|
||||
i += copy(dAtA[i:], m.State)
|
||||
if len(m.Children) > 0 {
|
||||
for _, msg := range m.Children {
|
||||
dAtA[i] = 0x12
|
||||
i++
|
||||
i = encodeVarintGenerated(dAtA, i, uint64(msg.Size()))
|
||||
n, err := msg.MarshalTo(dAtA[i:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i += n
|
||||
}
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (m *ResourceState) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
@@ -748,6 +807,18 @@ func (m *ResourceState) MarshalTo(dAtA []byte) (int, error) {
|
||||
i++
|
||||
i = encodeVarintGenerated(dAtA, i, uint64(len(m.Status)))
|
||||
i += copy(dAtA[i:], m.Status)
|
||||
if len(m.ChildLiveResources) > 0 {
|
||||
for _, msg := range m.ChildLiveResources {
|
||||
dAtA[i] = 0x22
|
||||
i++
|
||||
i = encodeVarintGenerated(dAtA, i, uint64(msg.Size()))
|
||||
n, err := msg.MarshalTo(dAtA[i:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i += n
|
||||
}
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
@@ -873,6 +944,8 @@ func (m *ApplicationSpec) Size() (n int) {
|
||||
l = m.Destination.Size()
|
||||
n += 1 + l + sovGenerated(uint64(l))
|
||||
}
|
||||
l = len(m.SyncPolicy)
|
||||
n += 1 + l + sovGenerated(uint64(l))
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -887,6 +960,12 @@ func (m *ApplicationStatus) Size() (n int) {
|
||||
n += 1 + l + sovGenerated(uint64(l))
|
||||
}
|
||||
}
|
||||
if len(m.Parameters) > 0 {
|
||||
for _, e := range m.Parameters {
|
||||
l = e.Size()
|
||||
n += 1 + l + sovGenerated(uint64(l))
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -995,6 +1074,7 @@ func (m *DeploymentInfo) Size() (n int) {
|
||||
}
|
||||
l = m.DeployedAt.Size()
|
||||
n += 1 + l + sovGenerated(uint64(l))
|
||||
n += 1 + sovGenerated(uint64(m.ID))
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -1026,6 +1106,20 @@ func (m *RepositoryList) Size() (n int) {
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *ResourceNode) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
l = len(m.State)
|
||||
n += 1 + l + sovGenerated(uint64(l))
|
||||
if len(m.Children) > 0 {
|
||||
for _, e := range m.Children {
|
||||
l = e.Size()
|
||||
n += 1 + l + sovGenerated(uint64(l))
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *ResourceState) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
@@ -1035,6 +1129,12 @@ func (m *ResourceState) Size() (n int) {
|
||||
n += 1 + l + sovGenerated(uint64(l))
|
||||
l = len(m.Status)
|
||||
n += 1 + l + sovGenerated(uint64(l))
|
||||
if len(m.ChildLiveResources) > 0 {
|
||||
for _, e := range m.ChildLiveResources {
|
||||
l = e.Size()
|
||||
n += 1 + l + sovGenerated(uint64(l))
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -1127,6 +1227,7 @@ func (this *ApplicationSpec) String() string {
|
||||
s := strings.Join([]string{`&ApplicationSpec{`,
|
||||
`Source:` + strings.Replace(strings.Replace(this.Source.String(), "ApplicationSource", "ApplicationSource", 1), `&`, ``, 1) + `,`,
|
||||
`Destination:` + strings.Replace(fmt.Sprintf("%v", this.Destination), "ApplicationDestination", "ApplicationDestination", 1) + `,`,
|
||||
`SyncPolicy:` + fmt.Sprintf("%v", this.SyncPolicy) + `,`,
|
||||
`}`,
|
||||
}, "")
|
||||
return s
|
||||
@@ -1138,6 +1239,7 @@ func (this *ApplicationStatus) String() string {
|
||||
s := strings.Join([]string{`&ApplicationStatus{`,
|
||||
`ComparisonResult:` + strings.Replace(strings.Replace(this.ComparisonResult.String(), "ComparisonResult", "ComparisonResult", 1), `&`, ``, 1) + `,`,
|
||||
`RecentDeployments:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.RecentDeployments), "DeploymentInfo", "DeploymentInfo", 1), `&`, ``, 1) + `,`,
|
||||
`Parameters:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Parameters), "ComponentParameter", "ComponentParameter", 1), `&`, ``, 1) + `,`,
|
||||
`}`,
|
||||
}, "")
|
||||
return s
|
||||
@@ -1226,6 +1328,7 @@ func (this *DeploymentInfo) String() string {
|
||||
`Revision:` + fmt.Sprintf("%v", this.Revision) + `,`,
|
||||
`ComponentParameterOverrides:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ComponentParameterOverrides), "ComponentParameter", "ComponentParameter", 1), `&`, ``, 1) + `,`,
|
||||
`DeployedAt:` + strings.Replace(strings.Replace(this.DeployedAt.String(), "Time", "k8s_io_apimachinery_pkg_apis_meta_v1.Time", 1), `&`, ``, 1) + `,`,
|
||||
`ID:` + fmt.Sprintf("%v", this.ID) + `,`,
|
||||
`}`,
|
||||
}, "")
|
||||
return s
|
||||
@@ -1254,6 +1357,17 @@ func (this *RepositoryList) String() string {
|
||||
}, "")
|
||||
return s
|
||||
}
|
||||
func (this *ResourceNode) String() string {
|
||||
if this == nil {
|
||||
return "nil"
|
||||
}
|
||||
s := strings.Join([]string{`&ResourceNode{`,
|
||||
`State:` + fmt.Sprintf("%v", this.State) + `,`,
|
||||
`Children:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Children), "ResourceNode", "ResourceNode", 1), `&`, ``, 1) + `,`,
|
||||
`}`,
|
||||
}, "")
|
||||
return s
|
||||
}
|
||||
func (this *ResourceState) String() string {
|
||||
if this == nil {
|
||||
return "nil"
|
||||
@@ -1262,6 +1376,7 @@ func (this *ResourceState) String() string {
|
||||
`TargetState:` + fmt.Sprintf("%v", this.TargetState) + `,`,
|
||||
`LiveState:` + fmt.Sprintf("%v", this.LiveState) + `,`,
|
||||
`Status:` + fmt.Sprintf("%v", this.Status) + `,`,
|
||||
`ChildLiveResources:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ChildLiveResources), "ResourceNode", "ResourceNode", 1), `&`, ``, 1) + `,`,
|
||||
`}`,
|
||||
}, "")
|
||||
return s
|
||||
@@ -1936,6 +2051,35 @@ func (m *ApplicationSpec) Unmarshal(dAtA []byte) error {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 3:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field SyncPolicy", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowGenerated
|
||||
}
|
||||
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 ErrInvalidLengthGenerated
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.SyncPolicy = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipGenerated(dAtA[iNdEx:])
|
||||
@@ -2047,6 +2191,37 @@ func (m *ApplicationStatus) Unmarshal(dAtA []byte) error {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 3:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Parameters", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowGenerated
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthGenerated
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Parameters = append(m.Parameters, ComponentParameter{})
|
||||
if err := m.Parameters[len(m.Parameters)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipGenerated(dAtA[iNdEx:])
|
||||
@@ -3137,6 +3312,25 @@ func (m *DeploymentInfo) Unmarshal(dAtA []byte) error {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 5:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
|
||||
}
|
||||
m.ID = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowGenerated
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.ID |= (int64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipGenerated(dAtA[iNdEx:])
|
||||
@@ -3435,6 +3629,116 @@ func (m *RepositoryList) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *ResourceNode) 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 ErrIntOverflowGenerated
|
||||
}
|
||||
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: ResourceNode: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: ResourceNode: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field State", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowGenerated
|
||||
}
|
||||
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 ErrInvalidLengthGenerated
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.State = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Children", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowGenerated
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthGenerated
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Children = append(m.Children, ResourceNode{})
|
||||
if err := m.Children[len(m.Children)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipGenerated(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthGenerated
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *ResourceState) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
@@ -3551,6 +3855,37 @@ func (m *ResourceState) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
m.Status = ComparisonStatus(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 4:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field ChildLiveResources", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowGenerated
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthGenerated
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.ChildLiveResources = append(m.ChildLiveResources, ResourceNode{})
|
||||
if err := m.ChildLiveResources[len(m.ChildLiveResources)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipGenerated(dAtA[iNdEx:])
|
||||
@@ -3874,96 +4209,104 @@ func init() {
|
||||
}
|
||||
|
||||
var fileDescriptorGenerated = []byte{
|
||||
// 1447 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x57, 0xcd, 0x6f, 0x1b, 0x45,
|
||||
0x14, 0xcf, 0xfa, 0x2b, 0xc9, 0xb8, 0xf9, 0xe8, 0xa0, 0x16, 0xd3, 0x4a, 0x6e, 0xb4, 0x15, 0x50,
|
||||
0x10, 0x5d, 0xd3, 0xf2, 0x0d, 0x12, 0x52, 0xd7, 0x09, 0x6a, 0x48, 0xda, 0x86, 0x89, 0x0b, 0x12,
|
||||
0x42, 0xc0, 0x64, 0xfd, 0x6a, 0x6f, 0x63, 0xef, 0x2e, 0x33, 0x63, 0x57, 0x3e, 0x20, 0xf5, 0xc0,
|
||||
0x11, 0xaa, 0xf2, 0x6f, 0x70, 0x04, 0x4e, 0xdc, 0x11, 0x3d, 0x70, 0xa8, 0x04, 0x87, 0x0a, 0xa1,
|
||||
0x8a, 0xa4, 0x17, 0xfe, 0x86, 0x9e, 0xd0, 0xcc, 0xce, 0xee, 0x8e, 0x6d, 0x85, 0xa4, 0x75, 0xa8,
|
||||
0xb8, 0x79, 0xe6, 0xfd, 0xe6, 0xf7, 0x7b, 0xfb, 0xe6, 0xcd, 0x7b, 0xcf, 0x68, 0xb5, 0xe5, 0x8b,
|
||||
0x76, 0x6f, 0xcb, 0xf1, 0xc2, 0x6e, 0x8d, 0xb2, 0x56, 0x18, 0xb1, 0xf0, 0xba, 0xfa, 0x71, 0xd6,
|
||||
0x6b, 0xd6, 0xa2, 0xed, 0x56, 0x8d, 0x46, 0x3e, 0xaf, 0xd1, 0x28, 0xea, 0xf8, 0x1e, 0x15, 0x7e,
|
||||
0x18, 0xd4, 0xfa, 0xe7, 0x68, 0x27, 0x6a, 0xd3, 0x73, 0xb5, 0x16, 0x04, 0xc0, 0xa8, 0x80, 0xa6,
|
||||
0x13, 0xb1, 0x50, 0x84, 0xf8, 0xad, 0x8c, 0xca, 0x49, 0xa8, 0xd4, 0x8f, 0xcf, 0xbc, 0xa6, 0x13,
|
||||
0x6d, 0xb7, 0x1c, 0x49, 0xe5, 0x18, 0x54, 0x4e, 0x42, 0x75, 0xe2, 0xac, 0xe1, 0x45, 0x2b, 0x6c,
|
||||
0x85, 0x35, 0xc5, 0xb8, 0xd5, 0xbb, 0xa6, 0x56, 0x6a, 0xa1, 0x7e, 0xc5, 0x4a, 0x27, 0x5e, 0xdd,
|
||||
0x7e, 0x93, 0x3b, 0x7e, 0x28, 0x7d, 0xeb, 0x52, 0xaf, 0xed, 0x07, 0xc0, 0x06, 0x99, 0xb3, 0x5d,
|
||||
0x10, 0xb4, 0xd6, 0x1f, 0xf3, 0xef, 0x44, 0x6d, 0xaf, 0x53, 0xac, 0x17, 0x08, 0xbf, 0x0b, 0x63,
|
||||
0x07, 0x5e, 0xdf, 0xef, 0x00, 0xf7, 0xda, 0xd0, 0xa5, 0x63, 0xe7, 0x5e, 0xd9, 0xeb, 0x5c, 0x4f,
|
||||
0xf8, 0x9d, 0x9a, 0x1f, 0x08, 0x2e, 0xd8, 0xe8, 0x21, 0xfb, 0xb7, 0x1c, 0x2a, 0x5f, 0xc8, 0x62,
|
||||
0x83, 0x3f, 0x47, 0x33, 0xf2, 0x43, 0x9a, 0x54, 0xd0, 0x8a, 0xb5, 0x64, 0x9d, 0x29, 0x9f, 0x7f,
|
||||
0xd9, 0x89, 0x79, 0x1d, 0x93, 0x37, 0x0b, 0xac, 0x44, 0x3b, 0xfd, 0x73, 0xce, 0x95, 0xad, 0xeb,
|
||||
0xe0, 0x89, 0x4b, 0x20, 0xa8, 0x8b, 0xef, 0xdc, 0x3f, 0x35, 0xb5, 0x7b, 0xff, 0x14, 0xca, 0xf6,
|
||||
0x48, 0xca, 0x8a, 0x3b, 0xa8, 0xc0, 0x23, 0xf0, 0x2a, 0x39, 0xc5, 0xfe, 0xbe, 0xf3, 0xd8, 0xd7,
|
||||
0xe7, 0x18, 0x7e, 0x6f, 0x46, 0xe0, 0xb9, 0x47, 0xb4, 0x6e, 0x41, 0xae, 0x88, 0x52, 0xc1, 0x02,
|
||||
0x95, 0xb8, 0xa0, 0xa2, 0xc7, 0x2b, 0x79, 0xa5, 0xb7, 0x7e, 0x48, 0x7a, 0x8a, 0xd3, 0x9d, 0xd7,
|
||||
0x8a, 0xa5, 0x78, 0x4d, 0xb4, 0x96, 0xfd, 0x05, 0x3a, 0x6e, 0x80, 0x97, 0x81, 0x0b, 0x3f, 0x88,
|
||||
0xe3, 0xfb, 0x1c, 0x2a, 0x71, 0x60, 0x7d, 0x60, 0x2a, 0xba, 0xb3, 0x06, 0x83, 0xda, 0x25, 0xda,
|
||||
0x8a, 0x6b, 0x68, 0x36, 0xa0, 0x5d, 0xe0, 0x11, 0xf5, 0x40, 0x85, 0x6a, 0xd6, 0x3d, 0xaa, 0xa1,
|
||||
0xb3, 0x97, 0x13, 0x03, 0xc9, 0x30, 0xf6, 0x9f, 0x16, 0x5a, 0x30, 0x34, 0xd7, 0x7d, 0x2e, 0xf0,
|
||||
0x27, 0x63, 0x97, 0xe9, 0x1c, 0xec, 0x32, 0xe5, 0x69, 0x75, 0x95, 0x8b, 0x5a, 0x73, 0x26, 0xd9,
|
||||
0x31, 0x2e, 0x72, 0x1b, 0x15, 0x7d, 0x01, 0x5d, 0x5e, 0xc9, 0x2d, 0xe5, 0xcf, 0x94, 0xcf, 0xbf,
|
||||
0x77, 0x38, 0x91, 0x75, 0xe7, 0xb4, 0x64, 0x71, 0x55, 0x92, 0x93, 0x58, 0xc3, 0xbe, 0x95, 0x47,
|
||||
0x47, 0xcd, 0xf8, 0x87, 0x3d, 0xe6, 0x01, 0x7e, 0x01, 0x4d, 0x33, 0x88, 0xc2, 0xab, 0x64, 0x5d,
|
||||
0x87, 0x73, 0x41, 0x1f, 0x9e, 0x26, 0xf1, 0x36, 0x49, 0xec, 0x78, 0x09, 0x15, 0x22, 0x2a, 0xda,
|
||||
0x3a, 0x96, 0x69, 0xaa, 0x6c, 0x50, 0xd1, 0x26, 0xca, 0x82, 0x5f, 0x43, 0x65, 0x08, 0xfa, 0x3e,
|
||||
0x0b, 0x83, 0x2e, 0x04, 0x42, 0xe5, 0xcb, 0xac, 0xfb, 0x94, 0x06, 0x96, 0x57, 0x32, 0x13, 0x31,
|
||||
0x71, 0xf8, 0x5d, 0x34, 0x2f, 0x28, 0x6b, 0x81, 0x20, 0xd0, 0xf7, 0xb9, 0x1f, 0x06, 0x95, 0x82,
|
||||
0x3a, 0x79, 0x5c, 0x9f, 0x9c, 0x6f, 0x0c, 0x59, 0xc9, 0x08, 0x1a, 0xff, 0x68, 0xa1, 0x93, 0x5e,
|
||||
0xd8, 0x8d, 0xc2, 0x00, 0x02, 0xb1, 0x41, 0x19, 0xed, 0x82, 0x00, 0x76, 0xa5, 0x0f, 0x8c, 0xf9,
|
||||
0x4d, 0xe0, 0x95, 0xa2, 0x8a, 0xee, 0xa5, 0x09, 0xa2, 0x5b, 0x1f, 0x63, 0x77, 0x4f, 0x6b, 0xe7,
|
||||
0x4e, 0xd6, 0xf7, 0x56, 0x26, 0xff, 0xe6, 0x96, 0x7d, 0x2b, 0x37, 0x94, 0x6f, 0x9b, 0xc9, 0x63,
|
||||
0x53, 0x17, 0xa3, 0xb3, 0xed, 0xb0, 0x1e, 0x9b, 0xe2, 0x34, 0x9e, 0x8a, 0x5a, 0x13, 0xad, 0x85,
|
||||
0xbf, 0xb2, 0x50, 0xb9, 0x99, 0x3d, 0x31, 0x5d, 0x58, 0x3e, 0x38, 0x1c, 0x6d, 0xe3, 0xed, 0xba,
|
||||
0x0b, 0x32, 0x0f, 0x8c, 0x0d, 0x62, 0xca, 0xda, 0x3f, 0xe5, 0x86, 0x33, 0x54, 0x55, 0x02, 0xfc,
|
||||
0xad, 0x85, 0x16, 0x65, 0x18, 0x29, 0xf3, 0x79, 0x18, 0x10, 0xe0, 0xbd, 0x8e, 0xd0, 0xd1, 0x59,
|
||||
0x9b, 0xf0, 0x4a, 0x4d, 0x4a, 0xb7, 0xa2, 0x83, 0xb3, 0x38, 0x6a, 0x21, 0x63, 0xf2, 0xf8, 0xb6,
|
||||
0x85, 0x16, 0x19, 0x78, 0x10, 0x88, 0x65, 0x88, 0x3a, 0xe1, 0x40, 0xa5, 0x7b, 0xfc, 0x88, 0x57,
|
||||
0x27, 0xf0, 0x29, 0x23, 0x5b, 0x0d, 0xae, 0x85, 0xee, 0x33, 0xda, 0xa3, 0xa3, 0x64, 0x44, 0x8a,
|
||||
0x93, 0x31, 0x75, 0xfb, 0x81, 0x85, 0x8e, 0x19, 0xc1, 0xfb, 0x88, 0x0a, 0xaf, 0xbd, 0xd2, 0x97,
|
||||
0xcf, 0x6b, 0x0d, 0x15, 0xc4, 0x20, 0x02, 0xfd, 0xbe, 0xdf, 0x48, 0xde, 0x6d, 0x63, 0x10, 0xc1,
|
||||
0xc3, 0xfb, 0xa7, 0x9e, 0xdf, 0xab, 0xe7, 0xdd, 0x90, 0x0c, 0x8e, 0xa2, 0x90, 0x50, 0xa2, 0x48,
|
||||
0xf0, 0x97, 0xa8, 0x6c, 0xb8, 0xae, 0x33, 0xe5, 0xb0, 0x0a, 0x57, 0x5a, 0x2a, 0x8c, 0x4d, 0x62,
|
||||
0xea, 0xd9, 0x3f, 0x5b, 0x68, 0xba, 0xde, 0xe9, 0x71, 0x01, 0xec, 0xc0, 0x8d, 0x60, 0x09, 0x15,
|
||||
0x64, 0x91, 0x1f, 0xad, 0x5b, 0xb2, 0x07, 0x10, 0x65, 0xc1, 0x11, 0x2a, 0x79, 0x61, 0x70, 0xcd,
|
||||
0x6f, 0xe9, 0x16, 0x77, 0x71, 0x92, 0xbc, 0x8a, 0xbd, 0xab, 0x2b, 0xbe, 0xcc, 0xa7, 0x78, 0x4d,
|
||||
0xb4, 0x8e, 0xfd, 0x7d, 0x0e, 0xcd, 0x0d, 0x21, 0xf1, 0x4b, 0x68, 0xa6, 0xc7, 0x81, 0x29, 0x4f,
|
||||
0xe3, 0xef, 0x49, 0x3b, 0xc7, 0x55, 0xbd, 0x4f, 0x52, 0x84, 0x44, 0x47, 0x94, 0xf3, 0x1b, 0x21,
|
||||
0x6b, 0xea, 0xef, 0x4a, 0xd1, 0x1b, 0x7a, 0x9f, 0xa4, 0x08, 0x59, 0x97, 0xb7, 0x80, 0x32, 0x60,
|
||||
0x8d, 0x70, 0x1b, 0x82, 0xd1, 0xba, 0xec, 0x66, 0x26, 0x62, 0xe2, 0xf0, 0x37, 0x16, 0x5a, 0x10,
|
||||
0x1d, 0x5e, 0xef, 0xf8, 0x10, 0x88, 0xd8, 0x4d, 0x55, 0x99, 0x27, 0x9b, 0x39, 0x1a, 0xeb, 0x9b,
|
||||
0x26, 0xa3, 0xfb, 0xb4, 0xf6, 0x63, 0x61, 0xc4, 0x40, 0x46, 0xb5, 0xed, 0xdf, 0x2d, 0x54, 0xd6,
|
||||
0x41, 0x7b, 0x02, 0xcd, 0xb9, 0x35, 0xdc, 0x9c, 0xdd, 0xc9, 0x73, 0x62, 0x8f, 0xc6, 0xfc, 0x5d,
|
||||
0x01, 0x8d, 0xd5, 0x1c, 0xfc, 0x29, 0x42, 0x71, 0xd5, 0x81, 0xe6, 0x85, 0xa4, 0xdc, 0xbd, 0x78,
|
||||
0xb0, 0xaf, 0x6b, 0xf8, 0x5d, 0xc8, 0x26, 0xc8, 0x7a, 0xca, 0x42, 0x0c, 0x46, 0x7c, 0xd3, 0xca,
|
||||
0x04, 0x1a, 0xa1, 0x7e, 0xc7, 0x87, 0xdb, 0x6d, 0xc6, 0x5c, 0x68, 0x84, 0xc4, 0xd0, 0x34, 0xde,
|
||||
0x6f, 0xfe, 0xe0, 0x83, 0x5c, 0x61, 0xff, 0x41, 0x0e, 0xbf, 0x9d, 0x4e, 0xac, 0x45, 0x85, 0xb6,
|
||||
0x87, 0x67, 0xcc, 0x87, 0x43, 0x35, 0x7e, 0x78, 0xee, 0xc4, 0x03, 0x34, 0xcb, 0x20, 0x6e, 0x8b,
|
||||
0xbc, 0x52, 0x52, 0x37, 0x3f, 0x49, 0x35, 0x20, 0x9a, 0x4b, 0xaa, 0x40, 0xe6, 0x76, 0xb2, 0xcd,
|
||||
0x49, 0xa6, 0x86, 0x4f, 0xa3, 0x22, 0x30, 0x16, 0xb2, 0xca, 0xb4, 0xf2, 0x3a, 0x4d, 0x96, 0x15,
|
||||
0xb9, 0x49, 0x62, 0x9b, 0xfd, 0xb5, 0x85, 0xf0, 0xf8, 0xc4, 0x21, 0x63, 0x94, 0x8e, 0x1a, 0xba,
|
||||
0x7c, 0xa4, 0x62, 0x29, 0x9c, 0x64, 0x98, 0x03, 0x14, 0xc5, 0xd3, 0xa8, 0xd8, 0xa7, 0x9d, 0x1e,
|
||||
0xe8, 0xdb, 0x49, 0xdd, 0xf9, 0x50, 0x6e, 0x92, 0xd8, 0x66, 0xff, 0x9a, 0x47, 0xf3, 0xc3, 0x5d,
|
||||
0x0b, 0xf7, 0x50, 0x29, 0x92, 0x7e, 0xf1, 0x8a, 0xf5, 0x5f, 0xcc, 0x5d, 0x69, 0x96, 0xa8, 0x2d,
|
||||
0x4e, 0xb4, 0x98, 0xac, 0x88, 0x2c, 0x19, 0x1f, 0x47, 0x2a, 0x62, 0x3a, 0x38, 0xa6, 0x88, 0x7d,
|
||||
0x47, 0xc6, 0xfc, 0xff, 0x72, 0x64, 0x94, 0x55, 0xa1, 0xa9, 0xa2, 0xad, 0xaa, 0x42, 0xe1, 0xf1,
|
||||
0xab, 0xc2, 0x72, 0xca, 0x42, 0x0c, 0x46, 0xfb, 0x17, 0x0b, 0x21, 0x39, 0xf7, 0x73, 0x5f, 0x84,
|
||||
0x6c, 0x20, 0x93, 0x44, 0x0e, 0xff, 0x3a, 0xa1, 0xd2, 0x24, 0x91, 0x08, 0xa2, 0x2c, 0x43, 0x5d,
|
||||
0x2b, 0xf7, 0x48, 0x5d, 0x2b, 0xbf, 0x6f, 0xd7, 0x7a, 0x07, 0xcd, 0x71, 0xde, 0xde, 0x60, 0x7e,
|
||||
0x9f, 0x0a, 0x58, 0x83, 0x81, 0x7e, 0xfb, 0xc7, 0xf4, 0x91, 0xb9, 0xcd, 0xcd, 0x8b, 0x99, 0x91,
|
||||
0x0c, 0x63, 0xed, 0x3f, 0x2c, 0x34, 0x9f, 0x7d, 0xc9, 0x13, 0x68, 0x17, 0xd7, 0x87, 0xdb, 0xc5,
|
||||
0xca, 0x44, 0x45, 0x23, 0xf1, 0x7b, 0x8f, 0x8e, 0xf1, 0x83, 0x85, 0xe6, 0x86, 0x2a, 0x8b, 0xec,
|
||||
0xf0, 0xf1, 0x9f, 0x22, 0xb5, 0xd4, 0x17, 0x96, 0x76, 0xf8, 0x46, 0x66, 0x22, 0x26, 0x4e, 0x96,
|
||||
0x8d, 0x8e, 0xdf, 0x8f, 0x39, 0x46, 0xff, 0x23, 0xaf, 0x27, 0x06, 0x92, 0x61, 0x8c, 0xd2, 0x9a,
|
||||
0x7f, 0xd4, 0xd2, 0x6a, 0xff, 0x6d, 0xa1, 0xd1, 0x1e, 0x2f, 0x33, 0xc2, 0x0f, 0x38, 0x78, 0x3d,
|
||||
0x16, 0x3b, 0x3d, 0x93, 0xc5, 0x78, 0x55, 0xef, 0x93, 0x14, 0x81, 0xcf, 0x23, 0x14, 0xf7, 0x84,
|
||||
0xcb, 0x59, 0xbe, 0xa5, 0x29, 0xbd, 0x99, 0x5a, 0x88, 0x81, 0xc2, 0x67, 0xd0, 0x8c, 0x07, 0x4c,
|
||||
0x2c, 0xcb, 0x5b, 0x97, 0x3e, 0x1f, 0x71, 0x8f, 0x48, 0xf6, 0xba, 0xde, 0x23, 0xa9, 0x15, 0x3f,
|
||||
0x8b, 0xa6, 0xb7, 0x61, 0xa0, 0x80, 0x05, 0x05, 0x2c, 0xcb, 0xbf, 0xc1, 0x6b, 0xf1, 0x16, 0x49,
|
||||
0x6c, 0xd8, 0x46, 0x25, 0x8f, 0x2a, 0x54, 0x51, 0xa1, 0x90, 0x1a, 0xef, 0x2e, 0x28, 0x90, 0xb6,
|
||||
0xb8, 0xce, 0x9d, 0x9d, 0xea, 0xd4, 0xdd, 0x9d, 0xea, 0xd4, 0xbd, 0x9d, 0xea, 0xd4, 0xcd, 0xdd,
|
||||
0xaa, 0x75, 0x67, 0xb7, 0x6a, 0xdd, 0xdd, 0xad, 0x5a, 0xf7, 0x76, 0xab, 0xd6, 0x5f, 0xbb, 0x55,
|
||||
0xeb, 0xf6, 0x83, 0xea, 0xd4, 0xc7, 0x33, 0xc9, 0x8d, 0xff, 0x13, 0x00, 0x00, 0xff, 0xff, 0xbf,
|
||||
0x57, 0x11, 0xb6, 0xcd, 0x13, 0x00, 0x00,
|
||||
// 1579 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0xcd, 0x8f, 0x1b, 0x45,
|
||||
0x16, 0x9f, 0xb6, 0x3d, 0x5f, 0xcf, 0xf3, 0x95, 0x5a, 0x25, 0xeb, 0x9d, 0x48, 0x9e, 0x51, 0x47,
|
||||
0xbb, 0x9b, 0x5d, 0x6d, 0xda, 0x9b, 0xec, 0x07, 0x5f, 0x12, 0x52, 0xda, 0x33, 0x90, 0x61, 0x26,
|
||||
0xc9, 0x50, 0xe3, 0x80, 0x84, 0x10, 0xd0, 0xd3, 0xae, 0xd8, 0x9d, 0xb1, 0xbb, 0x9b, 0xaa, 0xb2,
|
||||
0x23, 0x1f, 0x90, 0x82, 0xc4, 0x11, 0x50, 0x10, 0xff, 0x05, 0x47, 0xc4, 0x99, 0x03, 0x12, 0x22,
|
||||
0xdc, 0x22, 0xc1, 0x21, 0x42, 0x68, 0x44, 0x26, 0x97, 0xfc, 0x0d, 0x39, 0xa1, 0xaa, 0xae, 0xae,
|
||||
0x2e, 0xdb, 0x0c, 0x33, 0x89, 0x9d, 0x88, 0x9b, 0xfb, 0xbd, 0x5f, 0xbd, 0xdf, 0xeb, 0x57, 0xef,
|
||||
0xab, 0x0d, 0x1b, 0x8d, 0x80, 0x37, 0x3b, 0xbb, 0x8e, 0x1f, 0xb5, 0x2b, 0x1e, 0x6d, 0x44, 0x31,
|
||||
0x8d, 0x6e, 0xc8, 0x1f, 0xe7, 0xfc, 0x7a, 0x25, 0xde, 0x6b, 0x54, 0xbc, 0x38, 0x60, 0x15, 0x2f,
|
||||
0x8e, 0x5b, 0x81, 0xef, 0xf1, 0x20, 0x0a, 0x2b, 0xdd, 0xf3, 0x5e, 0x2b, 0x6e, 0x7a, 0xe7, 0x2b,
|
||||
0x0d, 0x12, 0x12, 0xea, 0x71, 0x52, 0x77, 0x62, 0x1a, 0xf1, 0x08, 0xbd, 0x90, 0x99, 0x72, 0x52,
|
||||
0x53, 0xf2, 0xc7, 0xbb, 0x7e, 0xdd, 0x89, 0xf7, 0x1a, 0x8e, 0x30, 0xe5, 0x18, 0xa6, 0x9c, 0xd4,
|
||||
0xd4, 0xf2, 0x39, 0xc3, 0x8b, 0x46, 0xd4, 0x88, 0x2a, 0xd2, 0xe2, 0x6e, 0xe7, 0xba, 0x7c, 0x92,
|
||||
0x0f, 0xf2, 0x57, 0xc2, 0xb4, 0xfc, 0xdf, 0xbd, 0xe7, 0x99, 0x13, 0x44, 0xc2, 0xb7, 0xb6, 0xe7,
|
||||
0x37, 0x83, 0x90, 0xd0, 0x5e, 0xe6, 0x6c, 0x9b, 0x70, 0xaf, 0xd2, 0x1d, 0xf2, 0x6f, 0xb9, 0x72,
|
||||
0xd8, 0x29, 0xda, 0x09, 0x79, 0xd0, 0x26, 0x43, 0x07, 0xfe, 0x7f, 0xd4, 0x01, 0xe6, 0x37, 0x49,
|
||||
0xdb, 0x1b, 0x3a, 0xf7, 0x9f, 0xc3, 0xce, 0x75, 0x78, 0xd0, 0xaa, 0x04, 0x21, 0x67, 0x9c, 0x0e,
|
||||
0x1e, 0xb2, 0x7f, 0xc8, 0x41, 0xf1, 0x62, 0x16, 0x1b, 0xf4, 0x1e, 0xcc, 0x88, 0x17, 0xa9, 0x7b,
|
||||
0xdc, 0x2b, 0x59, 0xab, 0xd6, 0xd9, 0xe2, 0x85, 0x7f, 0x3b, 0x89, 0x5d, 0xc7, 0xb4, 0x9b, 0x05,
|
||||
0x56, 0xa0, 0x9d, 0xee, 0x79, 0xe7, 0xea, 0xee, 0x0d, 0xe2, 0xf3, 0xcb, 0x84, 0x7b, 0x2e, 0xba,
|
||||
0xb3, 0xbf, 0x32, 0x71, 0xb0, 0xbf, 0x02, 0x99, 0x0c, 0x6b, 0xab, 0xa8, 0x05, 0x05, 0x16, 0x13,
|
||||
0xbf, 0x94, 0x93, 0xd6, 0x5f, 0x73, 0x9e, 0xf8, 0xfa, 0x1c, 0xc3, 0xef, 0x9d, 0x98, 0xf8, 0xee,
|
||||
0x9c, 0xe2, 0x2d, 0x88, 0x27, 0x2c, 0x59, 0x10, 0x87, 0x29, 0xc6, 0x3d, 0xde, 0x61, 0xa5, 0xbc,
|
||||
0xe4, 0xdb, 0x1a, 0x13, 0x9f, 0xb4, 0xe9, 0x2e, 0x28, 0xc6, 0xa9, 0xe4, 0x19, 0x2b, 0x2e, 0xfb,
|
||||
0x7d, 0x38, 0x65, 0x80, 0xd7, 0x08, 0xe3, 0x41, 0x98, 0xc4, 0xf7, 0x6f, 0x30, 0xc5, 0x08, 0xed,
|
||||
0x12, 0x2a, 0xa3, 0x3b, 0x6b, 0x58, 0x90, 0x52, 0xac, 0xb4, 0xa8, 0x02, 0xb3, 0xa1, 0xd7, 0x26,
|
||||
0x2c, 0xf6, 0x7c, 0x22, 0x43, 0x35, 0xeb, 0x9e, 0x50, 0xd0, 0xd9, 0x2b, 0xa9, 0x02, 0x67, 0x18,
|
||||
0xfb, 0x67, 0x0b, 0x16, 0x0d, 0xce, 0xad, 0x80, 0x71, 0xf4, 0xf6, 0xd0, 0x65, 0x3a, 0xc7, 0xbb,
|
||||
0x4c, 0x71, 0x5a, 0x5e, 0xe5, 0x92, 0xe2, 0x9c, 0x49, 0x25, 0xc6, 0x45, 0xee, 0xc1, 0x64, 0xc0,
|
||||
0x49, 0x9b, 0x95, 0x72, 0xab, 0xf9, 0xb3, 0xc5, 0x0b, 0xaf, 0x8c, 0x27, 0xb2, 0xee, 0xbc, 0xa2,
|
||||
0x9c, 0xdc, 0x10, 0xc6, 0x71, 0xc2, 0x61, 0x7f, 0x9a, 0x87, 0x13, 0x66, 0xfc, 0xa3, 0x0e, 0xf5,
|
||||
0x09, 0xfa, 0x07, 0x4c, 0x53, 0x12, 0x47, 0xd7, 0xf0, 0x96, 0x0a, 0xe7, 0xa2, 0x3a, 0x3c, 0x8d,
|
||||
0x13, 0x31, 0x4e, 0xf5, 0x68, 0x15, 0x0a, 0xb1, 0xc7, 0x9b, 0x2a, 0x96, 0x3a, 0x55, 0xb6, 0x3d,
|
||||
0xde, 0xc4, 0x52, 0x83, 0xfe, 0x07, 0x45, 0x12, 0x76, 0x03, 0x1a, 0x85, 0x6d, 0x12, 0x72, 0x99,
|
||||
0x2f, 0xb3, 0xee, 0x9f, 0x14, 0xb0, 0xb8, 0x9e, 0xa9, 0xb0, 0x89, 0x43, 0x2f, 0xc3, 0x02, 0xf7,
|
||||
0x68, 0x83, 0x70, 0x4c, 0xba, 0x01, 0x0b, 0xa2, 0xb0, 0x54, 0x90, 0x27, 0x4f, 0xa9, 0x93, 0x0b,
|
||||
0xb5, 0x3e, 0x2d, 0x1e, 0x40, 0xa3, 0xaf, 0x2c, 0x38, 0xed, 0x47, 0xed, 0x38, 0x0a, 0x49, 0xc8,
|
||||
0xb7, 0x3d, 0xea, 0xb5, 0x09, 0x27, 0xf4, 0x6a, 0x97, 0x50, 0x1a, 0xd4, 0x09, 0x2b, 0x4d, 0xca,
|
||||
0xe8, 0x5e, 0x1e, 0x21, 0xba, 0xd5, 0x21, 0xeb, 0xee, 0x19, 0xe5, 0xdc, 0xe9, 0xea, 0xe1, 0xcc,
|
||||
0xf8, 0xf7, 0xdc, 0xb2, 0xbf, 0xc9, 0xf5, 0xe5, 0xdb, 0x4e, 0x5a, 0x6c, 0xf2, 0x62, 0x54, 0xb6,
|
||||
0x8d, 0xab, 0xd8, 0xa4, 0x4d, 0xa3, 0x54, 0xe4, 0x33, 0x56, 0x5c, 0xe8, 0x23, 0x0b, 0x8a, 0xf5,
|
||||
0xac, 0xc4, 0x54, 0x63, 0x79, 0x7d, 0x3c, 0xdc, 0x46, 0xed, 0xba, 0x8b, 0x22, 0x0f, 0x0c, 0x01,
|
||||
0x36, 0x69, 0xd1, 0x05, 0x00, 0xd6, 0x0b, 0xfd, 0xed, 0xa8, 0x15, 0xf8, 0x3d, 0x95, 0x3d, 0xba,
|
||||
0x13, 0xee, 0x68, 0x0d, 0x36, 0x50, 0xf6, 0xd7, 0x03, 0x59, 0x2d, 0xbb, 0x07, 0xfa, 0xcc, 0x82,
|
||||
0x25, 0x11, 0x7a, 0x8f, 0x06, 0x2c, 0x0a, 0x31, 0x61, 0x9d, 0x16, 0x57, 0x11, 0xdd, 0x1c, 0x31,
|
||||
0x0d, 0x4c, 0x93, 0x6e, 0x49, 0x79, 0xb7, 0x34, 0xa8, 0xc1, 0x43, 0xf4, 0xe8, 0xb6, 0x05, 0x4b,
|
||||
0x94, 0xf8, 0x24, 0xe4, 0x6b, 0x24, 0x6e, 0x45, 0x3d, 0x59, 0x22, 0x49, 0xe1, 0x6f, 0x8c, 0xe0,
|
||||
0x53, 0x66, 0x6c, 0x23, 0xbc, 0x1e, 0xb9, 0x7f, 0x51, 0x1e, 0x9d, 0xc0, 0x03, 0x54, 0x0c, 0x0f,
|
||||
0xb1, 0xa3, 0x0f, 0x2d, 0x80, 0x38, 0x4d, 0x4c, 0xd1, 0xdf, 0x9f, 0x42, 0x9d, 0xe8, 0x0b, 0xd4,
|
||||
0x22, 0x86, 0x0d, 0x52, 0xfb, 0x81, 0x05, 0x27, 0x8d, 0x0b, 0x7c, 0xd3, 0xe3, 0x7e, 0x73, 0xbd,
|
||||
0x2b, 0xbc, 0xdb, 0x84, 0x02, 0xef, 0xc5, 0x44, 0xf5, 0xa5, 0xe7, 0xd2, 0x7e, 0x53, 0xeb, 0xc5,
|
||||
0xe4, 0xd1, 0xfe, 0xca, 0xdf, 0x0f, 0x9b, 0xd5, 0x37, 0x85, 0x05, 0x47, 0x9a, 0x10, 0x50, 0x2c,
|
||||
0x8d, 0xa0, 0x0f, 0xa0, 0x68, 0x78, 0xac, 0x32, 0x7c, 0x5c, 0x0d, 0x57, 0xb7, 0x38, 0x43, 0x88,
|
||||
0x4d, 0x3e, 0xfb, 0x5b, 0x0b, 0xa6, 0xab, 0xad, 0x0e, 0xe3, 0x84, 0x1e, 0x7b, 0x80, 0xad, 0x42,
|
||||
0x41, 0x0c, 0xa7, 0xc1, 0x7e, 0x2b, 0x66, 0x17, 0x96, 0x1a, 0x14, 0xc3, 0x94, 0x1f, 0x85, 0xd7,
|
||||
0x83, 0x86, 0x1a, 0xcd, 0x97, 0x46, 0xb9, 0xba, 0xc4, 0xbb, 0xaa, 0xb4, 0x97, 0xf9, 0x94, 0x3c,
|
||||
0x63, 0xc5, 0x63, 0x7f, 0x99, 0x83, 0xf9, 0x3e, 0x24, 0xfa, 0x17, 0xcc, 0x74, 0x18, 0xa1, 0xd2,
|
||||
0xd3, 0xe4, 0x7d, 0xf4, 0xc4, 0xbb, 0xa6, 0xe4, 0x58, 0x23, 0x04, 0x3a, 0xf6, 0x18, 0xbb, 0x19,
|
||||
0xd1, 0xba, 0x7a, 0x2f, 0x8d, 0xde, 0x56, 0x72, 0xac, 0x11, 0x62, 0x9e, 0xec, 0x12, 0x8f, 0x12,
|
||||
0x5a, 0x8b, 0xf6, 0x48, 0x38, 0x38, 0x4f, 0xdc, 0x4c, 0x85, 0x4d, 0x1c, 0xfa, 0xc4, 0x82, 0x45,
|
||||
0xde, 0x62, 0xd5, 0x56, 0x40, 0x42, 0x9e, 0xb8, 0x29, 0x27, 0xca, 0x68, 0xbb, 0x52, 0x6d, 0x6b,
|
||||
0xc7, 0xb4, 0xe8, 0xfe, 0x59, 0xf9, 0xb1, 0x38, 0xa0, 0xc0, 0x83, 0xdc, 0xf6, 0x8f, 0x16, 0x14,
|
||||
0x55, 0xd0, 0x9e, 0xc1, 0x52, 0xd1, 0xe8, 0x5f, 0x2a, 0xdc, 0xd1, 0x73, 0xe2, 0x90, 0x85, 0xe2,
|
||||
0x8b, 0x02, 0x0c, 0xf5, 0x3d, 0xf4, 0x0e, 0x40, 0xd2, 0xf9, 0x48, 0xfd, 0x62, 0xda, 0x72, 0xff,
|
||||
0x79, 0xbc, 0xb7, 0xab, 0x05, 0x6d, 0x92, 0xb5, 0x8b, 0xaa, 0xb6, 0x82, 0x0d, 0x8b, 0xe8, 0x96,
|
||||
0x95, 0x11, 0xd4, 0x22, 0x55, 0xc7, 0xe3, 0x9d, 0x92, 0x43, 0x2e, 0xd4, 0x22, 0x6c, 0x70, 0x1a,
|
||||
0xf5, 0x9b, 0x3f, 0xfe, 0x02, 0x5a, 0x38, 0x7a, 0x01, 0x45, 0x2f, 0xea, 0x4d, 0x7b, 0x52, 0xa2,
|
||||
0xed, 0xfe, 0xdd, 0xf8, 0x51, 0xdf, 0x9c, 0xe9, 0xdf, 0x97, 0x51, 0x0f, 0x66, 0x29, 0x49, 0xc6,
|
||||
0x39, 0x2b, 0x4d, 0xc9, 0x9b, 0x1f, 0xa5, 0x1b, 0x60, 0x65, 0x4b, 0xb0, 0x90, 0xcc, 0xed, 0x54,
|
||||
0xcc, 0x70, 0xc6, 0x86, 0xce, 0xc0, 0x24, 0xa1, 0x34, 0xa2, 0xa5, 0x69, 0xe9, 0xb5, 0x4e, 0x96,
|
||||
0x75, 0x21, 0xc4, 0x89, 0xce, 0xfe, 0xd8, 0x02, 0x34, 0x3c, 0x1d, 0x44, 0x8c, 0xf4, 0x8a, 0xa4,
|
||||
0xda, 0x87, 0x26, 0xd3, 0x70, 0x9c, 0x61, 0x8e, 0xd1, 0x14, 0xcf, 0xc0, 0x64, 0xd7, 0x6b, 0x75,
|
||||
0x88, 0xba, 0x1d, 0xed, 0xce, 0x1b, 0x42, 0x88, 0x13, 0x9d, 0xfd, 0x30, 0x0f, 0x0b, 0xfd, 0x93,
|
||||
0x13, 0x75, 0x60, 0x4a, 0x8e, 0x25, 0x56, 0xb2, 0x9e, 0xc6, 0x1c, 0xd4, 0x59, 0x22, 0x45, 0x0c,
|
||||
0x2b, 0x32, 0xd1, 0x11, 0x69, 0xba, 0xf6, 0x0e, 0x74, 0x44, 0xbd, 0xf0, 0x6a, 0xc4, 0x91, 0xab,
|
||||
0x6e, 0xfe, 0x0f, 0xb9, 0xea, 0x8a, 0xae, 0x50, 0x97, 0xd1, 0x96, 0x5d, 0xa1, 0xf0, 0xe4, 0x5d,
|
||||
0x61, 0x4d, 0x5b, 0xc1, 0x86, 0x45, 0xb4, 0x0c, 0xb9, 0xa0, 0x2e, 0xab, 0x26, 0xef, 0x82, 0xc2,
|
||||
0xe6, 0x36, 0xd6, 0x70, 0x2e, 0xa8, 0xdb, 0xdf, 0x59, 0x00, 0xe2, 0x5b, 0x86, 0x05, 0x3c, 0xa2,
|
||||
0x3d, 0x91, 0x40, 0xe2, 0x83, 0x46, 0x25, 0x9b, 0x4e, 0x20, 0x81, 0xc0, 0x52, 0xd3, 0x37, 0xd1,
|
||||
0x72, 0x8f, 0x35, 0xd1, 0xf2, 0x47, 0x4e, 0xb4, 0x97, 0x60, 0x9e, 0xb1, 0xe6, 0x36, 0x0d, 0xba,
|
||||
0x1e, 0x27, 0x9b, 0xa4, 0xa7, 0xfa, 0xc2, 0x49, 0x75, 0x64, 0x7e, 0x67, 0xe7, 0x52, 0xa6, 0xc4,
|
||||
0xfd, 0x58, 0xfb, 0x27, 0x0b, 0x16, 0xb2, 0x37, 0x79, 0x06, 0xa3, 0xe4, 0x46, 0xff, 0x28, 0x59,
|
||||
0x1f, 0xa9, 0xa1, 0xa4, 0x7e, 0x1f, 0x36, 0x4d, 0x2c, 0x98, 0x4b, 0xdb, 0xcb, 0x95, 0xa8, 0x2e,
|
||||
0xeb, 0x58, 0xf4, 0xb6, 0x74, 0xab, 0xd0, 0xa7, 0x64, 0x4b, 0xc2, 0x89, 0x0e, 0x75, 0x60, 0xc6,
|
||||
0x6f, 0x06, 0xad, 0x3a, 0x25, 0xa1, 0x72, 0xf2, 0xd5, 0x31, 0x74, 0x3d, 0xc1, 0x9f, 0x05, 0xa6,
|
||||
0xaa, 0x08, 0xb0, 0xa6, 0xb2, 0xbf, 0xcf, 0xc1, 0x7c, 0x5f, 0x8b, 0x14, 0xab, 0x4a, 0xf2, 0x55,
|
||||
0xba, 0x63, 0xf8, 0xac, 0x57, 0x95, 0x5a, 0xa6, 0xc2, 0x26, 0x4e, 0xf4, 0xbf, 0x56, 0xd0, 0x4d,
|
||||
0x6c, 0x0c, 0xfe, 0x49, 0xb1, 0x95, 0x2a, 0x70, 0x86, 0x31, 0x66, 0x44, 0xfe, 0xb1, 0x67, 0xc4,
|
||||
0xe7, 0x16, 0x20, 0xf9, 0x0a, 0xc2, 0xb2, 0x6e, 0xe5, 0xa5, 0xc2, 0x78, 0xe3, 0xb6, 0xac, 0x3c,
|
||||
0x42, 0xd5, 0x21, 0x2a, 0xfc, 0x1b, 0xf4, 0xf6, 0x43, 0x0b, 0x06, 0x57, 0x28, 0x51, 0x54, 0x41,
|
||||
0xc8, 0x88, 0xdf, 0xa1, 0x49, 0x28, 0x67, 0xb2, 0xdb, 0xd8, 0x50, 0x72, 0xac, 0x11, 0xf2, 0xbb,
|
||||
0x51, 0x8e, 0xdc, 0x2b, 0x59, 0xc9, 0x66, 0xdf, 0x8d, 0x5a, 0x83, 0x0d, 0x14, 0x3a, 0x0b, 0x33,
|
||||
0x3e, 0xa1, 0x7c, 0x4d, 0x14, 0x8e, 0x88, 0xe4, 0x9c, 0x3b, 0x27, 0xef, 0x5a, 0xc9, 0xb0, 0xd6,
|
||||
0xa2, 0xbf, 0xc2, 0xf4, 0x1e, 0xe9, 0x49, 0x60, 0x41, 0x02, 0x8b, 0x07, 0xfb, 0x2b, 0xd3, 0x9b,
|
||||
0x89, 0x08, 0xa7, 0x3a, 0x64, 0xc3, 0x94, 0xef, 0x49, 0xd4, 0xa4, 0x44, 0x81, 0xdc, 0x9e, 0x2f,
|
||||
0x4a, 0x90, 0xd2, 0xb8, 0xce, 0x9d, 0xfb, 0xe5, 0x89, 0xbb, 0xf7, 0xcb, 0x13, 0xf7, 0xee, 0x97,
|
||||
0x27, 0x6e, 0x1d, 0x94, 0xad, 0x3b, 0x07, 0x65, 0xeb, 0xee, 0x41, 0xd9, 0xba, 0x77, 0x50, 0xb6,
|
||||
0x7e, 0x39, 0x28, 0x5b, 0xb7, 0x1f, 0x94, 0x27, 0xde, 0x9a, 0x49, 0xe3, 0xfa, 0x6b, 0x00, 0x00,
|
||||
0x00, 0xff, 0xff, 0xdc, 0x24, 0x80, 0x9e, 0xe4, 0x15, 0x00, 0x00,
|
||||
}
|
||||
|
||||
@@ -69,6 +69,9 @@ message ApplicationSpec {
|
||||
// Destination overrides the kubernetes server and namespace defined in the environment ksonnet app.yaml
|
||||
// This field is optional. If omitted, uses the server and namespace defined in the environment
|
||||
optional ApplicationDestination destination = 2;
|
||||
|
||||
// SyncPolicy dictates whether we auto-sync based on the delta between the tracked branch and live state
|
||||
optional string syncPolicy = 3;
|
||||
}
|
||||
|
||||
// ApplicationStatus contains information about application status in target environment.
|
||||
@@ -76,6 +79,8 @@ message ApplicationStatus {
|
||||
optional ComparisonResult comparisonResult = 1;
|
||||
|
||||
repeated DeploymentInfo recentDeployment = 2;
|
||||
|
||||
repeated ComponentParameter parameters = 3;
|
||||
}
|
||||
|
||||
// ApplicationWatchEvent contains information about application change.
|
||||
@@ -161,6 +166,8 @@ message DeploymentInfo {
|
||||
repeated ComponentParameter componentParameterOverrides = 3;
|
||||
|
||||
optional k8s.io.apimachinery.pkg.apis.meta.v1.Time deployedAt = 4;
|
||||
|
||||
optional int64 id = 5;
|
||||
}
|
||||
|
||||
// Repository is a Git repository holding application configurations
|
||||
@@ -181,6 +188,13 @@ message RepositoryList {
|
||||
repeated Repository items = 2;
|
||||
}
|
||||
|
||||
// ResourceNode contains information about live resource and its children
|
||||
message ResourceNode {
|
||||
optional string state = 1;
|
||||
|
||||
repeated ResourceNode children = 2;
|
||||
}
|
||||
|
||||
// ResourceState holds the target state of a resource and live state of a resource
|
||||
message ResourceState {
|
||||
optional string targetState = 1;
|
||||
@@ -188,6 +202,8 @@ message ResourceState {
|
||||
optional string liveState = 2;
|
||||
|
||||
optional string status = 3;
|
||||
|
||||
repeated ResourceNode childLiveResources = 4;
|
||||
}
|
||||
|
||||
// TLSClientConfig contains settings to enable transport layer security
|
||||
|
||||
@@ -17,6 +17,7 @@ type DeploymentInfo struct {
|
||||
Revision string `json:"revision" protobuf:"bytes,2,opt,name=revision"`
|
||||
ComponentParameterOverrides []ComponentParameter `json:"componentParameterOverrides,omitempty" protobuf:"bytes,3,opt,name=componentParameterOverrides"`
|
||||
DeployedAt metav1.Time `json:"deployedAt" protobuf:"bytes,4,opt,name=deployedAt"`
|
||||
ID int64 `json:"id" protobuf:"bytes,5,opt,name=id"`
|
||||
}
|
||||
|
||||
// Application is a definition of Application resource.
|
||||
@@ -57,6 +58,8 @@ type ApplicationSpec struct {
|
||||
// Destination overrides the kubernetes server and namespace defined in the environment ksonnet app.yaml
|
||||
// This field is optional. If omitted, uses the server and namespace defined in the environment
|
||||
Destination *ApplicationDestination `json:"destination,omitempty" protobuf:"bytes,2,opt,name=destination"`
|
||||
// SyncPolicy dictates whether we auto-sync based on the delta between the tracked branch and live state
|
||||
SyncPolicy string `json:"syncPolicy,omitempty" protobuf:"bytes,3,opt,name=syncPolicy"`
|
||||
}
|
||||
|
||||
// ComponentParameter contains information about component parameter value
|
||||
@@ -102,8 +105,9 @@ const (
|
||||
|
||||
// ApplicationStatus contains information about application status in target environment.
|
||||
type ApplicationStatus struct {
|
||||
ComparisonResult ComparisonResult `json:"comparisonResult" protobuf:"bytes,1,opt,name=comparisonResult"`
|
||||
RecentDeployments []DeploymentInfo `json:"recentDeployments" protobuf:"bytes,2,opt,name=recentDeployment"`
|
||||
ComparisonResult ComparisonResult `json:"comparisonResult" protobuf:"bytes,1,opt,name=comparisonResult"`
|
||||
RecentDeployments []DeploymentInfo `json:"recentDeployments" protobuf:"bytes,2,opt,name=recentDeployment"`
|
||||
Parameters []ComponentParameter `json:"parameters,omitempty" protobuf:"bytes,3,opt,name=parameters"`
|
||||
}
|
||||
|
||||
// ComparisonResult is a comparison result of application spec and deployed application.
|
||||
@@ -117,11 +121,18 @@ type ComparisonResult struct {
|
||||
Error string `json:"error,omitempty" protobuf:"bytes,7,opt,name=error"`
|
||||
}
|
||||
|
||||
// ResourceNode contains information about live resource and its children
|
||||
type ResourceNode struct {
|
||||
State string `json:"state,omitempty" protobuf:"bytes,1,opt,name=state"`
|
||||
Children []ResourceNode `json:"children,omitempty" protobuf:"bytes,2,opt,name=children"`
|
||||
}
|
||||
|
||||
// ResourceState holds the target state of a resource and live state of a resource
|
||||
type ResourceState struct {
|
||||
TargetState string `json:"targetState,omitempty" protobuf:"bytes,1,opt,name=targetState"`
|
||||
LiveState string `json:"liveState,omitempty" protobuf:"bytes,2,opt,name=liveState"`
|
||||
Status ComparisonStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
|
||||
TargetState string `json:"targetState,omitempty" protobuf:"bytes,1,opt,name=targetState"`
|
||||
LiveState string `json:"liveState,omitempty" protobuf:"bytes,2,opt,name=liveState"`
|
||||
Status ComparisonStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
|
||||
ChildLiveResources []ResourceNode `json:"childLiveResources,omitempty" protobuf:"bytes,4,opt,name=childLiveResources"`
|
||||
}
|
||||
|
||||
// Cluster is the definition of a cluster resource
|
||||
@@ -181,9 +192,9 @@ type TLSClientConfig struct {
|
||||
// Repository is a Git repository holding application configurations
|
||||
type Repository struct {
|
||||
Repo string `json:"repo" protobuf:"bytes,1,opt,name=repo"`
|
||||
Username string `json:"username" protobuf:"bytes,2,opt,name=username"`
|
||||
Password string `json:"password" protobuf:"bytes,3,opt,name=password"`
|
||||
SSHPrivateKey string `json:"sshPrivateKey" protobuf:"bytes,4,opt,name=sshPrivateKey"`
|
||||
Username string `json:"username,omitempty" protobuf:"bytes,2,opt,name=username"`
|
||||
Password string `json:"password,omitempty" protobuf:"bytes,3,opt,name=password"`
|
||||
SSHPrivateKey string `json:"sshPrivateKey,omitempty" protobuf:"bytes,4,opt,name=sshPrivateKey"`
|
||||
}
|
||||
|
||||
// RepositoryList is a collection of Repositories.
|
||||
|
||||
@@ -143,6 +143,11 @@ func (in *ApplicationStatus) DeepCopyInto(out *ApplicationStatus) {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.Parameters != nil {
|
||||
in, out := &in.Parameters, &out.Parameters
|
||||
*out = make([]ComponentParameter, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -239,7 +244,9 @@ func (in *ComparisonResult) DeepCopyInto(out *ComparisonResult) {
|
||||
if in.Resources != nil {
|
||||
in, out := &in.Resources, &out.Resources
|
||||
*out = make([]ResourceState, len(*in))
|
||||
copy(*out, *in)
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -335,9 +342,39 @@ func (in *RepositoryList) DeepCopy() *RepositoryList {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ResourceNode) DeepCopyInto(out *ResourceNode) {
|
||||
*out = *in
|
||||
if in.Children != nil {
|
||||
in, out := &in.Children, &out.Children
|
||||
*out = make([]ResourceNode, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceNode.
|
||||
func (in *ResourceNode) DeepCopy() *ResourceNode {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ResourceNode)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ResourceState) DeepCopyInto(out *ResourceState) {
|
||||
*out = *in
|
||||
if in.ChildLiveResources != nil {
|
||||
in, out := &in.ChildLiveResources, &out.ChildLiveResources
|
||||
*out = make([]ResourceNode, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -8,11 +8,13 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
"github.com/argoproj/argo-cd/util/git"
|
||||
ksutil "github.com/argoproj/argo-cd/util/ksonnet"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/context"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
)
|
||||
|
||||
@@ -34,16 +36,43 @@ func NewService(namespace string, kubeClient kubernetes.Interface, gitClient git
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) GenerateManifest(c context.Context, q *ManifestRequest) (*ManifestResponse, error) {
|
||||
appRepoPath := path.Join(os.TempDir(), strings.Replace(q.Repo.Repo, "/", "_", -1))
|
||||
func (s *Service) GetKsonnetApp(ctx context.Context, in *KsonnetAppRequest) (*KsonnetAppResponse, error) {
|
||||
appRepoPath := tempRepoPath(in.Repo.Repo)
|
||||
s.repoLock.Lock(appRepoPath)
|
||||
defer func() {
|
||||
err := s.gitClient.Reset(appRepoPath)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
defer s.unlockAndResetRepoPath(appRepoPath)
|
||||
ksApp, err := s.getAppSpec(*in.Repo, appRepoPath, in.Revision, in.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ksAppToResponse(ksApp)
|
||||
}
|
||||
|
||||
// ksAppToResponse converts a Ksonnet app instance to a API response object
|
||||
func ksAppToResponse(ksApp ksutil.KsonnetApp) (*KsonnetAppResponse, error) {
|
||||
var appRes KsonnetAppResponse
|
||||
appRes.Environments = make(map[string]*KsonnetEnvironment)
|
||||
for envName, env := range ksApp.Spec().Environments {
|
||||
if env.Destination == nil {
|
||||
return nil, fmt.Errorf("Environment '%s' has no destination defined", envName)
|
||||
}
|
||||
s.repoLock.Unlock(appRepoPath)
|
||||
}()
|
||||
envRes := KsonnetEnvironment{
|
||||
Name: envName,
|
||||
K8SVersion: env.KubernetesVersion,
|
||||
Path: env.Path,
|
||||
Destination: &KsonnetEnvironmentDestination{
|
||||
Server: env.Destination.Server,
|
||||
Namespace: env.Destination.Namespace,
|
||||
},
|
||||
}
|
||||
appRes.Environments[envName] = &envRes
|
||||
}
|
||||
return &appRes, nil
|
||||
}
|
||||
|
||||
func (s *Service) GenerateManifest(c context.Context, q *ManifestRequest) (*ManifestResponse, error) {
|
||||
appRepoPath := tempRepoPath(q.Repo.Repo)
|
||||
s.repoLock.Lock(appRepoPath)
|
||||
defer s.unlockAndResetRepoPath(appRepoPath)
|
||||
|
||||
err := s.gitClient.CloneOrFetch(q.Repo.Repo, q.Repo.Username, q.Repo.Password, q.Repo.SSHPrivateKey, appRepoPath)
|
||||
if err != nil {
|
||||
@@ -82,12 +111,10 @@ func (s *Service) GenerateManifest(c context.Context, q *ManifestRequest) (*Mani
|
||||
manifests := make([]string, len(targetObjs))
|
||||
for i, target := range targetObjs {
|
||||
if q.AppLabel != "" {
|
||||
labels := target.GetLabels()
|
||||
if labels == nil {
|
||||
labels = make(map[string]string)
|
||||
err = s.setAppLabels(target, q.AppLabel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
labels[common.LabelApplicationName] = q.AppLabel
|
||||
target.SetLabels(labels)
|
||||
}
|
||||
manifestStr, err := json.Marshal(target.Object)
|
||||
if err != nil {
|
||||
@@ -103,9 +130,48 @@ func (s *Service) GenerateManifest(c context.Context, q *ManifestRequest) (*Mani
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Service) getAppSpec(repo v1alpha1.Repository, appRepoPath, revision, subPath string) (ksutil.KsonnetApp, error) {
|
||||
err := s.gitClient.CloneOrFetch(repo.Repo, repo.Username, repo.Password, repo.SSHPrivateKey, appRepoPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = s.gitClient.Checkout(appRepoPath, revision)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
appPath := path.Join(appRepoPath, subPath)
|
||||
ksApp, err := ksutil.NewKsonnetApp(appPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ksApp, nil
|
||||
}
|
||||
|
||||
func (s *Service) setAppLabels(target *unstructured.Unstructured, appName string) error {
|
||||
labels := target.GetLabels()
|
||||
if labels == nil {
|
||||
labels = make(map[string]string)
|
||||
}
|
||||
labels[common.LabelApplicationName] = appName
|
||||
target.SetLabels(labels)
|
||||
// special case for deployment: make sure that derived replicaset and pod has application label
|
||||
if target.GetKind() == "Deployment" {
|
||||
labels, ok := unstructured.NestedMap(target.UnstructuredContent(), "spec", "template", "metadata", "labels")
|
||||
if ok {
|
||||
if labels == nil {
|
||||
labels = make(map[string]interface{})
|
||||
}
|
||||
labels[common.LabelApplicationName] = appName
|
||||
}
|
||||
unstructured.SetNestedMap(target.UnstructuredContent(), labels, "spec", "template", "metadata", "labels")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetEnvParams retrieves Ksonnet environment params in specified repo name and revision
|
||||
func (s *Service) GetEnvParams(c context.Context, q *EnvParamsRequest) (*EnvParamsResponse, error) {
|
||||
appRepoPath := path.Join(os.TempDir(), strings.Replace(q.Repo.Repo, "/", "_", -1))
|
||||
appRepoPath := tempRepoPath(q.Repo.Repo)
|
||||
s.repoLock.Lock(appRepoPath)
|
||||
defer s.repoLock.Unlock(appRepoPath)
|
||||
|
||||
@@ -133,3 +199,18 @@ func (s *Service) GetEnvParams(c context.Context, q *EnvParamsRequest) (*EnvPara
|
||||
Params: target,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// tempRepoPath returns a formulated temporary directory location to clone a repository
|
||||
func tempRepoPath(repo string) string {
|
||||
return path.Join(os.TempDir(), strings.Replace(repo, "/", "_", -1))
|
||||
}
|
||||
|
||||
// unlockAndResetRepoPath will reset any local changes in a local git repo and unlock the path
|
||||
// so that other workers can use the local repo
|
||||
func (s *Service) unlockAndResetRepoPath(appRepoPath string) {
|
||||
err := s.gitClient.Reset(appRepoPath)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
}
|
||||
s.repoLock.Unlock(appRepoPath)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -36,6 +36,45 @@ message EnvParamsResponse {
|
||||
repeated github.com.argoproj.argo_cd.pkg.apis.application.v1alpha1.ComponentParameter params = 1;
|
||||
}
|
||||
|
||||
// KsonnetAppRequest is a query for ksonnet app
|
||||
message KsonnetAppRequest {
|
||||
github.com.argoproj.argo_cd.pkg.apis.application.v1alpha1.Repository repo = 1;
|
||||
string revision = 2;
|
||||
string path = 3;
|
||||
}
|
||||
|
||||
|
||||
// KsonnetAppResponse contains Ksonnet app response
|
||||
// This roughly reflects: ksonnet/ksonnet/metadata/app/schema.go
|
||||
// NOTE: we may expose ksonnet apps from API server, in which case these definitions will move to
|
||||
// a more public place. For now, these types are only used internally.
|
||||
message KsonnetAppResponse {
|
||||
string name = 1;
|
||||
map<string, KsonnetEnvironment> environments = 2;
|
||||
}
|
||||
|
||||
message KsonnetEnvironment {
|
||||
// Name is the user defined name of an environment
|
||||
string name = 1;
|
||||
// KubernetesVersion is the kubernetes version the targetted cluster is running on.
|
||||
string k8sVersion = 2;
|
||||
// Path is the relative project path containing metadata for this environment.
|
||||
string path = 3;
|
||||
// Destination stores the cluster address that this environment points to.
|
||||
KsonnetEnvironmentDestination destination = 4;
|
||||
|
||||
// Targets contain the relative component paths that this environment
|
||||
//repeated string targets = X;
|
||||
}
|
||||
|
||||
message KsonnetEnvironmentDestination {
|
||||
// Server is the Kubernetes server that the cluster is running on.
|
||||
string server = 1;
|
||||
// Namespace is the namespace of the Kubernetes server that targets should be deployed to
|
||||
string namespace = 2;
|
||||
}
|
||||
|
||||
|
||||
// ManifestService
|
||||
service RepositoryService {
|
||||
|
||||
@@ -46,4 +85,9 @@ service RepositoryService {
|
||||
// Retrieve Ksonnet environment params in specified repo name and revision
|
||||
rpc GetEnvParams(EnvParamsRequest) returns (EnvParamsResponse) {
|
||||
}
|
||||
|
||||
// Retrieve Ksonnet environment params in specified repo name and revision
|
||||
rpc GetKsonnetApp(KsonnetAppRequest) returns (KsonnetAppResponse) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/reflection"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
)
|
||||
|
||||
@@ -44,5 +45,8 @@ func (a *ArgoCDRepoServer) CreateGRPC(gitClient git.Client) *grpc.Server {
|
||||
manifestService := repository.NewService(a.ns, a.kubeclientset, gitClient)
|
||||
repository.RegisterRepositoryServiceServer(server, manifestService)
|
||||
|
||||
// Register reflection service on gRPC server.
|
||||
reflection.Register(server)
|
||||
|
||||
return server
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/controller"
|
||||
appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
|
||||
"github.com/argoproj/argo-cd/reposerver"
|
||||
@@ -14,15 +17,19 @@ import (
|
||||
apirepository "github.com/argoproj/argo-cd/server/repository"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
argoutil "github.com/argoproj/argo-cd/util/argo"
|
||||
"github.com/argoproj/argo-cd/util/diff"
|
||||
"github.com/argoproj/argo-cd/util/git"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/context"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"k8s.io/api/core/v1"
|
||||
apierr "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -38,6 +45,7 @@ type Server struct {
|
||||
// TODO(jessesuen): move common cluster code to shared libraries
|
||||
clusterService cluster.ClusterServiceServer
|
||||
repoService apirepository.RepositoryServiceServer
|
||||
appComparator controller.AppComparator
|
||||
}
|
||||
|
||||
// NewServer returns a new instance of the Application service
|
||||
@@ -56,6 +64,7 @@ func NewServer(
|
||||
clusterService: clusterService,
|
||||
repoClientset: repoClientset,
|
||||
repoService: repoService,
|
||||
appComparator: controller.NewKsonnetAppComparator(clusterService),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,48 +73,63 @@ func (s *Server) List(ctx context.Context, q *ApplicationQuery) (*appv1.Applicat
|
||||
return s.appclientset.ArgoprojV1alpha1().Applications(s.ns).List(metav1.ListOptions{})
|
||||
}
|
||||
|
||||
// Create creates a application
|
||||
// Create creates an application
|
||||
func (s *Server) Create(ctx context.Context, a *appv1.Application) (*appv1.Application, error) {
|
||||
err := s.validateApp(ctx, a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Create(a)
|
||||
}
|
||||
|
||||
// Get returns a application by name
|
||||
// Get returns an application by name
|
||||
func (s *Server) Get(ctx context.Context, q *ApplicationQuery) (*appv1.Application, error) {
|
||||
return s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Get(q.Name, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// Update updates a application
|
||||
// Update updates an application
|
||||
func (s *Server) Update(ctx context.Context, a *appv1.Application) (*appv1.Application, error) {
|
||||
err := s.validateApp(ctx, a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Update(a)
|
||||
}
|
||||
|
||||
// Delete removes an application and all associated resources
|
||||
func (s *Server) Delete(ctx context.Context, q *DeleteApplicationRequest) (*ApplicationResponse, error) {
|
||||
err := s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Delete(q.Name, &metav1.DeleteOptions{})
|
||||
var err error
|
||||
server := q.Server
|
||||
namespace := q.Namespace
|
||||
if server == "" || namespace == "" {
|
||||
server, namespace, err = s.getApplicationDestination(ctx, q.Name)
|
||||
if err != nil && !apierr.IsNotFound(err) && !q.Force {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if server != "" && namespace != "" {
|
||||
clst, err := s.clusterService.Get(ctx, &cluster.ClusterQuery{Server: server})
|
||||
if err != nil && !q.Force {
|
||||
return nil, err
|
||||
}
|
||||
if clst != nil {
|
||||
config := clst.RESTConfig()
|
||||
err = kube.DeleteResourceWithLabel(config, namespace, common.LabelApplicationName, q.Name)
|
||||
if err != nil && !q.Force {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Delete(q.Name, &metav1.DeleteOptions{})
|
||||
if err != nil && !apierr.IsNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
if q.Server != "" && q.Namespace != "" {
|
||||
clst, err := s.clusterService.Get(ctx, &cluster.ClusterQuery{Server: q.Server})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config := clst.RESTConfig()
|
||||
err = kube.DeleteResourceWithLabel(config, q.Namespace, fmt.Sprintf("%s=%s", common.LabelApplicationName, q.Name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &ApplicationResponse{}, nil
|
||||
}
|
||||
|
||||
// ListPods returns pods in a application
|
||||
func (s *Server) ListPods(ctx context.Context, q *ApplicationQuery) (*apiv1.PodList, error) {
|
||||
// TODO: filter by the app label
|
||||
return s.kubeclientset.CoreV1().Pods(s.ns).List(metav1.ListOptions{})
|
||||
}
|
||||
|
||||
func (s *Server) Watch(q *ApplicationQuery, ws ApplicationService_WatchServer) error {
|
||||
w, err := s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Watch(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
@@ -135,26 +159,250 @@ func (s *Server) Watch(q *ApplicationQuery, ws ApplicationService_WatchServer) e
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sync syncs an application to its target state
|
||||
func (s *Server) Sync(ctx context.Context, syncReq *ApplicationSyncRequest) (*ApplicationSyncResult, error) {
|
||||
log.Infof("Syncing application %s", syncReq.Name)
|
||||
app, err := s.Get(ctx, &ApplicationQuery{Name: syncReq.Name})
|
||||
// validateApp will ensure:
|
||||
// * the git repository is accessible
|
||||
// * 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 {
|
||||
// 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})
|
||||
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, "", "", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Verify app.yaml is functional
|
||||
req := repository.KsonnetAppRequest{
|
||||
Repo: &appv1.Repository{
|
||||
Repo: a.Spec.Source.RepoURL,
|
||||
},
|
||||
Revision: a.Spec.Source.TargetRevision,
|
||||
Path: a.Spec.Source.Path,
|
||||
}
|
||||
if repoRes != nil {
|
||||
req.Repo.Username = repoRes.Username
|
||||
req.Repo.Password = repoRes.Password
|
||||
req.Repo.SSHPrivateKey = repoRes.SSHPrivateKey
|
||||
}
|
||||
ksAppRes, err := repoClient.GetKsonnetApp(ctx, &req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Verify the specified environment is defined in it
|
||||
envSpec, ok := ksAppRes.Environments[a.Spec.Source.Environment]
|
||||
if !ok {
|
||||
return status.Errorf(codes.InvalidArgument, "environment '%s' does not exist in app", a.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
|
||||
}
|
||||
_, err = s.clusterService.Get(ctx, &cluster.ClusterQuery{Server: clusterURL})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) getApplicationClusterConfig(applicationName string) (*rest.Config, string, error) {
|
||||
server, namespace, err := s.getApplicationDestination(context.Background(), applicationName)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
clst, err := s.clusterService.Get(context.Background(), &cluster.ClusterQuery{Server: server})
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
config := clst.RESTConfig()
|
||||
return config, namespace, err
|
||||
}
|
||||
|
||||
func (s *Server) ensurePodBelongsToApp(applicationName string, podName, namespace string, kubeClientset *kubernetes.Clientset) error {
|
||||
pod, err := kubeClientset.CoreV1().Pods(namespace).Get(podName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wrongPodError := fmt.Errorf("pod %s does not belong to application %s", podName, applicationName)
|
||||
if pod.Labels == nil {
|
||||
return wrongPodError
|
||||
}
|
||||
if value, ok := pod.Labels[common.LabelApplicationName]; !ok || value != applicationName {
|
||||
return wrongPodError
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) DeletePod(ctx context.Context, q *DeletePodQuery) (*ApplicationResponse, error) {
|
||||
config, namespace, err := s.getApplicationClusterConfig(q.ApplicationName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
revision := syncReq.Revision
|
||||
if revision == "" {
|
||||
kubeClientset, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = s.ensurePodBelongsToApp(q.ApplicationName, q.PodName, namespace, kubeClientset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = kubeClientset.CoreV1().Pods(namespace).Delete(q.PodName, &metav1.DeleteOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ApplicationResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *Server) PodLogs(q *PodLogsQuery, ws ApplicationService_PodLogsServer) error {
|
||||
config, namespace, err := s.getApplicationClusterConfig(q.ApplicationName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
kubeClientset, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.ensurePodBelongsToApp(q.ApplicationName, q.PodName, namespace, kubeClientset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var sinceSeconds, tailLines *int64
|
||||
if q.SinceSeconds > 0 {
|
||||
sinceSeconds = &q.SinceSeconds
|
||||
}
|
||||
if q.TailLines > 0 {
|
||||
tailLines = &q.TailLines
|
||||
}
|
||||
stream, err := kubeClientset.CoreV1().Pods(namespace).GetLogs(q.PodName, &v1.PodLogOptions{
|
||||
Container: q.Container,
|
||||
Follow: q.Follow,
|
||||
Timestamps: true,
|
||||
SinceSeconds: sinceSeconds,
|
||||
SinceTime: q.SinceTime,
|
||||
TailLines: tailLines,
|
||||
}).Stream()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
scanner := bufio.NewScanner(stream)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
parts := strings.Split(line, " ")
|
||||
logTime, err := time.Parse(time.RFC3339, parts[0])
|
||||
metaLogTime := metav1.NewTime(logTime)
|
||||
if err == nil {
|
||||
lines := strings.Join(parts[1:], " ")
|
||||
for _, line := range strings.Split(lines, "\r") {
|
||||
if line != "" {
|
||||
err = ws.Send(&LogEntry{
|
||||
Content: line,
|
||||
TimeStamp: &metaLogTime,
|
||||
})
|
||||
if err != nil {
|
||||
log.Warnf("Unable to send stream message: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
done <- true
|
||||
}()
|
||||
select {
|
||||
case <-ws.Context().Done():
|
||||
util.Close(stream)
|
||||
case <-done:
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sync syncs an application to its target state
|
||||
func (s *Server) Sync(ctx context.Context, syncReq *ApplicationSyncRequest) (*ApplicationSyncResult, error) {
|
||||
return s.deployAndPersistDeploymentInfo(ctx, syncReq.Name, syncReq.Revision, nil, syncReq.DryRun, syncReq.Prune)
|
||||
}
|
||||
|
||||
func (s *Server) Rollback(ctx context.Context, rollbackReq *ApplicationRollbackRequest) (*ApplicationSyncResult, error) {
|
||||
app, err := s.Get(ctx, &ApplicationQuery{Name: rollbackReq.Name})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var deploymentInfo *appv1.DeploymentInfo
|
||||
for _, info := range app.Status.RecentDeployments {
|
||||
if info.ID == rollbackReq.ID {
|
||||
deploymentInfo = &info
|
||||
break
|
||||
}
|
||||
}
|
||||
if deploymentInfo == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "application %s does not have deployment with id %v", rollbackReq.Name, rollbackReq.ID)
|
||||
}
|
||||
return s.deployAndPersistDeploymentInfo(ctx, rollbackReq.Name, deploymentInfo.Revision, &deploymentInfo.ComponentParameterOverrides, rollbackReq.DryRun, rollbackReq.Prune)
|
||||
}
|
||||
|
||||
func (s *Server) deployAndPersistDeploymentInfo(
|
||||
ctx context.Context, appName string, revision string, overrides *[]appv1.ComponentParameter, dryRun bool, prune bool) (*ApplicationSyncResult, error) {
|
||||
|
||||
log.Infof("Syncing application %s", appName)
|
||||
app, err := s.Get(ctx, &ApplicationQuery{Name: appName})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if revision != "" {
|
||||
app.Spec.Source.TargetRevision = revision
|
||||
}
|
||||
|
||||
if overrides != nil {
|
||||
app.Spec.Source.ComponentParameterOverrides = *overrides
|
||||
}
|
||||
|
||||
res, manifest, err := s.deploy(ctx, app, dryRun, prune)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !dryRun {
|
||||
err = s.persistDeploymentInfo(ctx, appName, manifest.Revision, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (s *Server) persistDeploymentInfo(ctx context.Context, appName string, revision string, overrides *[]appv1.ComponentParameter) error {
|
||||
app, err := s.Get(ctx, &ApplicationQuery{Name: appName})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
repo := s.getRepo(ctx, app.Spec.Source.RepoURL)
|
||||
conn, repoClient, err := s.repoClientset.NewRepositoryClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
defer util.Close(conn)
|
||||
// set fields in v1alpha/types.go
|
||||
log.Infof("Retrieving deployment params for application %s", syncReq.Name)
|
||||
|
||||
log.Infof("Retrieving deployment params for application %s", appName)
|
||||
envParams, err := repoClient.GetEnvParams(ctx, &repository.EnvParamsRequest{
|
||||
Repo: repo,
|
||||
Environment: app.Spec.Source.Environment,
|
||||
@@ -163,37 +411,65 @@ func (s *Server) Sync(ctx context.Context, syncReq *ApplicationSyncRequest) (*Ap
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
log.Infof("Received deployment params: %s", envParams.Params)
|
||||
|
||||
res, manifest, err := s.deploy(ctx, app.Spec.Source, app.Spec.Destination, app.Name, syncReq.DryRun)
|
||||
if err == nil {
|
||||
// Persist app deployment info
|
||||
params := make([]appv1.ComponentParameter, len(envParams.Params))
|
||||
for i := range envParams.Params {
|
||||
param := *envParams.Params[i]
|
||||
params[i] = param
|
||||
}
|
||||
app, err = s.Get(ctx, &ApplicationQuery{Name: syncReq.Name})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
app.Status.RecentDeployments = append(app.Status.RecentDeployments, appv1.DeploymentInfo{
|
||||
ComponentParameterOverrides: app.Spec.Source.ComponentParameterOverrides,
|
||||
Revision: manifest.Revision,
|
||||
Params: params,
|
||||
DeployedAt: metav1.NewTime(time.Now()),
|
||||
})
|
||||
if len(app.Status.RecentDeployments) > maxRecentDeploymentsCnt {
|
||||
app.Status.RecentDeployments = app.Status.RecentDeployments[:maxRecentDeploymentsCnt]
|
||||
}
|
||||
_, err = s.Update(ctx, app)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params := make([]appv1.ComponentParameter, len(envParams.Params))
|
||||
for i := range envParams.Params {
|
||||
param := *envParams.Params[i]
|
||||
params[i] = param
|
||||
}
|
||||
var nextId int64 = 0
|
||||
if len(app.Status.RecentDeployments) > 0 {
|
||||
nextId = app.Status.RecentDeployments[len(app.Status.RecentDeployments)-1].ID + 1
|
||||
}
|
||||
recentDeployments := append(app.Status.RecentDeployments, appv1.DeploymentInfo{
|
||||
ComponentParameterOverrides: app.Spec.Source.ComponentParameterOverrides,
|
||||
Revision: revision,
|
||||
Params: params,
|
||||
DeployedAt: metav1.NewTime(time.Now()),
|
||||
ID: nextId,
|
||||
})
|
||||
if len(recentDeployments) > maxRecentDeploymentsCnt {
|
||||
recentDeployments = recentDeployments[1 : maxRecentDeploymentsCnt+1]
|
||||
}
|
||||
|
||||
patch, err := json.Marshal(map[string]map[string][]appv1.DeploymentInfo{
|
||||
"status": {
|
||||
"recentDeployments": recentDeployments,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Patch(app.Name, types.MergePatchType, patch)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Server) getApplicationDestination(ctx context.Context, name string) (string, string, error) {
|
||||
app, err := s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
} else {
|
||||
repo := s.getRepo(ctx, app.Spec.Source.RepoURL)
|
||||
conn, repoClient, err := s.repoClientset.NewRepositoryClient()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
defer util.Close(conn)
|
||||
manifestInfo, err := repoClient.GenerateManifest(ctx, &repository.ManifestRequest{
|
||||
Repo: repo,
|
||||
Environment: app.Spec.Source.Environment,
|
||||
Path: app.Spec.Source.Path,
|
||||
Revision: app.Spec.Source.TargetRevision,
|
||||
AppLabel: app.Name,
|
||||
})
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
server, namespace := argoutil.ResolveServerNamespace(app.Spec.Destination, manifestInfo)
|
||||
return server, namespace, nil
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (s *Server) getRepo(ctx context.Context, repoURL string) *appv1.Repository {
|
||||
@@ -207,43 +483,35 @@ func (s *Server) getRepo(ctx context.Context, repoURL string) *appv1.Repository
|
||||
|
||||
func (s *Server) deploy(
|
||||
ctx context.Context,
|
||||
source appv1.ApplicationSource,
|
||||
destination *appv1.ApplicationDestination,
|
||||
appLabel string,
|
||||
dryRun bool) (*ApplicationSyncResult, *repository.ManifestResponse, error) {
|
||||
app *appv1.Application,
|
||||
dryRun bool,
|
||||
prune bool) (*ApplicationSyncResult, *repository.ManifestResponse, error) {
|
||||
|
||||
repo := s.getRepo(ctx, source.RepoURL)
|
||||
repo := s.getRepo(ctx, app.Spec.Source.RepoURL)
|
||||
conn, repoClient, err := s.repoClientset.NewRepositoryClient()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer util.Close(conn)
|
||||
overrides := make([]*appv1.ComponentParameter, len(source.ComponentParameterOverrides))
|
||||
if source.ComponentParameterOverrides != nil {
|
||||
for i := range source.ComponentParameterOverrides {
|
||||
item := source.ComponentParameterOverrides[i]
|
||||
overrides := make([]*appv1.ComponentParameter, len(app.Spec.Source.ComponentParameterOverrides))
|
||||
if app.Spec.Source.ComponentParameterOverrides != nil {
|
||||
for i := range app.Spec.Source.ComponentParameterOverrides {
|
||||
item := app.Spec.Source.ComponentParameterOverrides[i]
|
||||
overrides[i] = &item
|
||||
}
|
||||
}
|
||||
|
||||
manifestInfo, err := repoClient.GenerateManifest(ctx, &repository.ManifestRequest{
|
||||
Repo: repo,
|
||||
Environment: source.Environment,
|
||||
Path: source.Path,
|
||||
Revision: source.TargetRevision,
|
||||
Environment: app.Spec.Source.Environment,
|
||||
Path: app.Spec.Source.Path,
|
||||
Revision: app.Spec.Source.TargetRevision,
|
||||
ComponentParameterOverrides: overrides,
|
||||
AppLabel: appLabel,
|
||||
AppLabel: app.Name,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
server, namespace := argoutil.ResolveServerNamespace(destination, manifestInfo)
|
||||
|
||||
clst, err := s.clusterService.Get(ctx, &cluster.ClusterQuery{Server: server})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
config := clst.RESTConfig()
|
||||
|
||||
targetObjs := make([]*unstructured.Unstructured, len(manifestInfo.Manifests))
|
||||
for i, manifest := range manifestInfo.Manifests {
|
||||
@@ -254,40 +522,89 @@ func (s *Server) deploy(
|
||||
targetObjs[i] = obj
|
||||
}
|
||||
|
||||
liveObjs, err := kube.GetLiveResources(config, targetObjs, namespace)
|
||||
server, namespace := argoutil.ResolveServerNamespace(app.Spec.Destination, manifestInfo)
|
||||
|
||||
comparison, err := s.appComparator.CompareAppState(server, namespace, targetObjs, app)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
diffResList, err := diff.DiffArray(targetObjs, liveObjs)
|
||||
|
||||
clst, err := s.clusterService.Get(ctx, &cluster.ClusterQuery{Server: server})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
config := clst.RESTConfig()
|
||||
|
||||
var syncRes ApplicationSyncResult
|
||||
syncRes.Resources = make([]*ResourceDetails, 0)
|
||||
for i, diffRes := range diffResList.Diffs {
|
||||
for _, resourceState := range comparison.Resources {
|
||||
var liveObj, targetObj *unstructured.Unstructured
|
||||
|
||||
if resourceState.LiveState != "null" {
|
||||
liveObj = &unstructured.Unstructured{}
|
||||
err = json.Unmarshal([]byte(resourceState.LiveState), liveObj)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if resourceState.TargetState != "null" {
|
||||
targetObj = &unstructured.Unstructured{}
|
||||
err = json.Unmarshal([]byte(resourceState.TargetState), targetObj)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
needsCreate := liveObj == nil
|
||||
needsDelete := targetObj == nil
|
||||
|
||||
obj := targetObj
|
||||
if obj == nil {
|
||||
obj = liveObj
|
||||
}
|
||||
resDetails := ResourceDetails{
|
||||
Name: targetObjs[i].GetName(),
|
||||
Kind: targetObjs[i].GetKind(),
|
||||
Name: obj.GetName(),
|
||||
Kind: obj.GetKind(),
|
||||
Namespace: namespace,
|
||||
}
|
||||
needsCreate := bool(liveObjs[i] == nil)
|
||||
if !diffRes.Modified {
|
||||
|
||||
if resourceState.Status == appv1.ComparisonStatusSynced {
|
||||
resDetails.Message = fmt.Sprintf("already synced")
|
||||
} else if dryRun {
|
||||
if needsCreate {
|
||||
resDetails.Message = fmt.Sprintf("will create")
|
||||
} else if needsDelete {
|
||||
if prune {
|
||||
resDetails.Message = fmt.Sprintf("will delete")
|
||||
} else {
|
||||
resDetails.Message = fmt.Sprintf("will be ignored (should be deleted)")
|
||||
}
|
||||
} else {
|
||||
resDetails.Message = fmt.Sprintf("will update")
|
||||
}
|
||||
} else {
|
||||
_, err := kube.ApplyResource(config, targetObjs[i], namespace)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if needsCreate {
|
||||
resDetails.Message = fmt.Sprintf("created")
|
||||
if needsDelete {
|
||||
if prune {
|
||||
err = kube.DeleteResource(config, liveObj, namespace)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
resDetails.Message = fmt.Sprintf("deleted")
|
||||
} else {
|
||||
resDetails.Message = fmt.Sprintf("ignored (should be deleted)")
|
||||
}
|
||||
} else {
|
||||
resDetails.Message = fmt.Sprintf("updated")
|
||||
_, err := kube.ApplyResource(config, targetObj, namespace)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if needsCreate {
|
||||
resDetails.Message = fmt.Sprintf("created")
|
||||
} else {
|
||||
resDetails.Message = fmt.Sprintf("updated")
|
||||
}
|
||||
}
|
||||
}
|
||||
syncRes.Resources = append(syncRes.Resources, &resDetails)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -177,33 +177,6 @@ func request_ApplicationService_Delete_0(ctx context.Context, marshaler runtime.
|
||||
|
||||
}
|
||||
|
||||
func request_ApplicationService_ListPods_0(ctx context.Context, marshaler runtime.Marshaler, client ApplicationServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq ApplicationQuery
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
var (
|
||||
val string
|
||||
ok bool
|
||||
err error
|
||||
_ = err
|
||||
)
|
||||
|
||||
val, ok = pathParams["name"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name")
|
||||
}
|
||||
|
||||
protoReq.Name, err = runtime.String(val)
|
||||
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err)
|
||||
}
|
||||
|
||||
msg, err := client.ListPods(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func request_ApplicationService_Sync_0(ctx context.Context, marshaler runtime.Marshaler, client ApplicationServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq ApplicationSyncRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
@@ -235,6 +208,129 @@ func request_ApplicationService_Sync_0(ctx context.Context, marshaler runtime.Ma
|
||||
|
||||
}
|
||||
|
||||
func request_ApplicationService_Rollback_0(ctx context.Context, marshaler runtime.Marshaler, client ApplicationServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq ApplicationRollbackRequest
|
||||
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["name"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name")
|
||||
}
|
||||
|
||||
protoReq.Name, err = runtime.String(val)
|
||||
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err)
|
||||
}
|
||||
|
||||
msg, err := client.Rollback(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func request_ApplicationService_DeletePod_0(ctx context.Context, marshaler runtime.Marshaler, client ApplicationServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq DeletePodQuery
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
var (
|
||||
val string
|
||||
ok bool
|
||||
err error
|
||||
_ = err
|
||||
)
|
||||
|
||||
val, ok = pathParams["applicationName"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "applicationName")
|
||||
}
|
||||
|
||||
protoReq.ApplicationName, err = runtime.String(val)
|
||||
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "applicationName", err)
|
||||
}
|
||||
|
||||
val, ok = pathParams["podName"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "podName")
|
||||
}
|
||||
|
||||
protoReq.PodName, err = runtime.String(val)
|
||||
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "podName", err)
|
||||
}
|
||||
|
||||
msg, err := client.DeletePod(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
var (
|
||||
filter_ApplicationService_PodLogs_0 = &utilities.DoubleArray{Encoding: map[string]int{"applicationName": 0, "podName": 1}, Base: []int{1, 1, 2, 0, 0}, Check: []int{0, 1, 1, 2, 3}}
|
||||
)
|
||||
|
||||
func request_ApplicationService_PodLogs_0(ctx context.Context, marshaler runtime.Marshaler, client ApplicationServiceClient, req *http.Request, pathParams map[string]string) (ApplicationService_PodLogsClient, runtime.ServerMetadata, error) {
|
||||
var protoReq PodLogsQuery
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
var (
|
||||
val string
|
||||
ok bool
|
||||
err error
|
||||
_ = err
|
||||
)
|
||||
|
||||
val, ok = pathParams["applicationName"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "applicationName")
|
||||
}
|
||||
|
||||
protoReq.ApplicationName, err = runtime.String(val)
|
||||
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "applicationName", err)
|
||||
}
|
||||
|
||||
val, ok = pathParams["podName"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "podName")
|
||||
}
|
||||
|
||||
protoReq.PodName, err = runtime.String(val)
|
||||
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "podName", err)
|
||||
}
|
||||
|
||||
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_ApplicationService_PodLogs_0); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
stream, err := client.PodLogs(ctx, &protoReq)
|
||||
if err != nil {
|
||||
return nil, metadata, err
|
||||
}
|
||||
header, err := stream.Header()
|
||||
if err != nil {
|
||||
return nil, metadata, err
|
||||
}
|
||||
metadata.HeaderMD = header
|
||||
return stream, metadata, nil
|
||||
|
||||
}
|
||||
|
||||
// RegisterApplicationServiceHandlerFromEndpoint is same as RegisterApplicationServiceHandler but
|
||||
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
|
||||
func RegisterApplicationServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
|
||||
@@ -447,35 +543,6 @@ func RegisterApplicationServiceHandlerClient(ctx context.Context, mux *runtime.S
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_ApplicationService_ListPods_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_ListPods_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_ListPods_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_ApplicationService_Sync_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
@@ -505,6 +572,93 @@ func RegisterApplicationServiceHandlerClient(ctx context.Context, mux *runtime.S
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_ApplicationService_Rollback_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_Rollback_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_Rollback_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("DELETE", pattern_ApplicationService_DeletePod_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_DeletePod_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_DeletePod_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_ApplicationService_PodLogs_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_PodLogs_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_PodLogs_0(ctx, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -521,9 +675,13 @@ var (
|
||||
|
||||
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_ListPods_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", "pods"}, ""))
|
||||
|
||||
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"}, ""))
|
||||
|
||||
pattern_ApplicationService_Rollback_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", "rollback"}, ""))
|
||||
|
||||
pattern_ApplicationService_DeletePod_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"api", "v1", "applications", "applicationName", "pods", "podName"}, ""))
|
||||
|
||||
pattern_ApplicationService_PodLogs_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4, 1, 0, 4, 1, 5, 5, 2, 6}, []string{"api", "v1", "applications", "applicationName", "pods", "podName", "logs"}, ""))
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -539,7 +697,11 @@ var (
|
||||
|
||||
forward_ApplicationService_Delete_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_ApplicationService_ListPods_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_ApplicationService_Sync_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_ApplicationService_Rollback_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_ApplicationService_DeletePod_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_ApplicationService_PodLogs_0 = runtime.ForwardResponseStream
|
||||
)
|
||||
|
||||
@@ -9,6 +9,7 @@ package application;
|
||||
import "gogoproto/gogo.proto";
|
||||
import "google/api/annotations.proto";
|
||||
import "k8s.io/api/core/v1/generated.proto";
|
||||
import "k8s.io/apimachinery/pkg/apis/meta/v1/generated.proto";
|
||||
import "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1/generated.proto";
|
||||
|
||||
|
||||
@@ -23,6 +24,7 @@ message DeleteApplicationRequest {
|
||||
string name = 1;
|
||||
string namespace = 2;
|
||||
string server = 3;
|
||||
bool force = 4;
|
||||
}
|
||||
|
||||
// ApplicationSyncRequest is a request to apply the config state to live state
|
||||
@@ -30,6 +32,7 @@ message ApplicationSyncRequest {
|
||||
string name = 1;
|
||||
string revision = 2;
|
||||
bool dryRun = 3;
|
||||
bool prune = 4;
|
||||
}
|
||||
|
||||
// ApplicationSyncResult is a result of a sync requeswt
|
||||
@@ -38,6 +41,13 @@ message ApplicationSyncResult {
|
||||
repeated ResourceDetails resources = 2;
|
||||
}
|
||||
|
||||
message ApplicationRollbackRequest {
|
||||
string name = 1;
|
||||
int64 id = 2 [(gogoproto.customname) = "ID"];
|
||||
bool dryRun = 3;
|
||||
bool prune = 4;
|
||||
}
|
||||
|
||||
message ResourceDetails {
|
||||
string name = 1;
|
||||
string kind = 2;
|
||||
@@ -45,6 +55,26 @@ message ResourceDetails {
|
||||
string message = 4;
|
||||
}
|
||||
|
||||
message DeletePodQuery {
|
||||
string applicationName = 1;
|
||||
string podName = 2;
|
||||
}
|
||||
|
||||
message PodLogsQuery {
|
||||
string applicationName = 1;
|
||||
string podName = 2;
|
||||
string container = 3;
|
||||
int64 sinceSeconds = 4;
|
||||
k8s.io.apimachinery.pkg.apis.meta.v1.Time sinceTime = 5;
|
||||
int64 tailLines = 6;
|
||||
bool follow = 7;
|
||||
}
|
||||
|
||||
message LogEntry {
|
||||
string content = 1;
|
||||
k8s.io.apimachinery.pkg.apis.meta.v1.Time timeStamp = 2;
|
||||
}
|
||||
|
||||
// ApplicationService
|
||||
service ApplicationService {
|
||||
|
||||
@@ -84,11 +114,6 @@ service ApplicationService {
|
||||
option (google.api.http).delete = "/api/v1/applications/{name}";
|
||||
}
|
||||
|
||||
// ListPods returns pods in an application
|
||||
rpc ListPods(ApplicationQuery) returns (k8s.io.api.core.v1.PodList) {
|
||||
option (google.api.http).get = "/api/v1/applications/{name}/pods";
|
||||
}
|
||||
|
||||
// Sync syncs an application to its target state
|
||||
rpc Sync(ApplicationSyncRequest) returns (ApplicationSyncResult) {
|
||||
option (google.api.http) = {
|
||||
@@ -97,4 +122,21 @@ service ApplicationService {
|
||||
};
|
||||
}
|
||||
|
||||
// Sync syncs an application to its target state
|
||||
rpc Rollback(ApplicationRollbackRequest) returns (ApplicationSyncResult) {
|
||||
option (google.api.http) = {
|
||||
post: "/api/v1/applications/{name}/rollback"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// PodLogs returns stream of log entries for the specified pod. Pod
|
||||
rpc DeletePod(DeletePodQuery) returns (ApplicationResponse) {
|
||||
option (google.api.http).delete = "/api/v1/applications/{applicationName}/pods/{podName}";
|
||||
}
|
||||
|
||||
// PodLogs returns stream of log entries for the specified pod. Pod
|
||||
rpc PodLogs(PodLogsQuery) returns (stream LogEntry) {
|
||||
option (google.api.http).get = "/api/v1/applications/{applicationName}/pods/{podName}/logs";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ var (
|
||||
)
|
||||
|
||||
func init() {
|
||||
forward_ApplicationService_Watch_0 = func(ctx context.Context, mux *runtime.ServeMux, marshaler runtime.Marshaler, w http.ResponseWriter, req *http.Request, recv func() (proto.Message, error), opts ...func(context.Context, http.ResponseWriter, proto.Message) error) {
|
||||
sseOverrider := func(ctx context.Context, mux *runtime.ServeMux, marshaler runtime.Marshaler, w http.ResponseWriter, req *http.Request, recv func() (proto.Message, error), opts ...func(context.Context, http.ResponseWriter, proto.Message) error) {
|
||||
if req.Header.Get("Accept") == "text/event-stream" {
|
||||
w.Header().Set("Content-Type", "text/event-stream")
|
||||
w.Header().Set("Transfer-Encoding", "chunked")
|
||||
@@ -55,4 +55,6 @@ func init() {
|
||||
runtime.ForwardResponseStream(ctx, mux, marshaler, w, req, recv, opts...)
|
||||
}
|
||||
}
|
||||
forward_ApplicationService_Watch_0 = sseOverrider
|
||||
forward_ApplicationService_PodLogs_0 = sseOverrider
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/selection"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
)
|
||||
|
||||
@@ -30,7 +31,7 @@ type Server struct {
|
||||
}
|
||||
|
||||
// NewServer returns a new instance of the Cluster service
|
||||
func NewServer(namespace string, kubeclientset kubernetes.Interface, appclientset appclientset.Interface) ClusterServiceServer {
|
||||
func NewServer(namespace string, kubeclientset kubernetes.Interface, appclientset appclientset.Interface) *Server {
|
||||
return &Server{
|
||||
ns: namespace,
|
||||
appclientset: appclientset,
|
||||
@@ -93,6 +94,41 @@ func (s *Server) Create(ctx context.Context, c *appv1.Cluster) (*appv1.Cluster,
|
||||
return secretToCluster(clusterSecret), nil
|
||||
}
|
||||
|
||||
// ClusterEvent contains information about cluster event
|
||||
type ClusterEvent struct {
|
||||
Type watch.EventType
|
||||
Cluster *appv1.Cluster
|
||||
}
|
||||
|
||||
// WatchClusters allow watching for cluster events
|
||||
func (s *Server) WatchClusters(ctx context.Context, callback func(*ClusterEvent)) error {
|
||||
listOpts := metav1.ListOptions{}
|
||||
labelSelector := labels.NewSelector()
|
||||
req, err := labels.NewRequirement(common.LabelKeySecretType, selection.Equals, []string{common.SecretTypeCluster})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
labelSelector = labelSelector.Add(*req)
|
||||
listOpts.LabelSelector = labelSelector.String()
|
||||
w, err := s.kubeclientset.CoreV1().Secrets(s.ns).Watch(listOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
w.Stop()
|
||||
}()
|
||||
for next := range w.ResultChan() {
|
||||
secret := next.Object.(*apiv1.Secret)
|
||||
cluster := secretToCluster(secret)
|
||||
callback(&ClusterEvent{
|
||||
Type: next.Type,
|
||||
Cluster: cluster,
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) getClusterSecret(server string) (*apiv1.Secret, error) {
|
||||
secName := serverToSecretName(server)
|
||||
clusterSecret, err := s.kubeclientset.CoreV1().Secrets(s.ns).Get(secName, metav1.GetOptions{})
|
||||
@@ -153,7 +189,7 @@ func serverToSecretName(server string) string {
|
||||
}
|
||||
h := fnv.New32a()
|
||||
_, _ = h.Write([]byte(server))
|
||||
host := strings.Split(serverURL.Host, ":")[0]
|
||||
host := strings.ToLower(strings.Split(serverURL.Host, ":")[0])
|
||||
return fmt.Sprintf("cluster-%s-%v", host, h.Sum32())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||
// source: server/cluster/cluster.proto
|
||||
|
||||
/*
|
||||
Package cluster is a generated protocol buffer package.
|
||||
Package cluster is a generated protocol buffer package.
|
||||
|
||||
Cluster Service
|
||||
Cluster Service
|
||||
|
||||
Cluster Service API performs CRUD actions against cluster resources
|
||||
Cluster Service API performs CRUD actions against cluster resources
|
||||
|
||||
It is generated from these files:
|
||||
server/cluster/cluster.proto
|
||||
It is generated from these files:
|
||||
server/cluster/cluster.proto
|
||||
|
||||
It has these top-level messages:
|
||||
ClusterQuery
|
||||
ClusterResponse
|
||||
ClusterUpdateRequest
|
||||
It has these top-level messages:
|
||||
ClusterQuery
|
||||
ClusterResponse
|
||||
ClusterUpdateRequest
|
||||
*/
|
||||
package cluster
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import proto "github.com/gogo/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
import _ "github.com/gogo/protobuf/gogoproto"
|
||||
@@ -26,10 +26,10 @@ import _ "google.golang.org/genproto/googleapis/api/annotations"
|
||||
import k8s_io_api_core_v1 "k8s.io/api/core/v1"
|
||||
import github_com_argoproj_argo_cd_pkg_apis_application_v1alpha1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
|
||||
import (
|
||||
context "golang.org/x/net/context"
|
||||
grpc "google.golang.org/grpc"
|
||||
)
|
||||
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
|
||||
@@ -40,17 +40,17 @@ var _ = math.Inf
|
||||
// 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.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
// ClusterQuery is a query for cluster resources
|
||||
type ClusterQuery struct {
|
||||
Server string `protobuf:"bytes,1,opt,name=server" json:"server,omitempty"`
|
||||
Server string `protobuf:"bytes,1,opt,name=server,proto3" json:"server,omitempty"`
|
||||
}
|
||||
|
||||
func (m *ClusterQuery) Reset() { *m = ClusterQuery{} }
|
||||
func (m *ClusterQuery) String() string { return proto.CompactTextString(m) }
|
||||
func (*ClusterQuery) ProtoMessage() {}
|
||||
func (*ClusterQuery) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||
func (*ClusterQuery) Descriptor() ([]byte, []int) { return fileDescriptorCluster, []int{0} }
|
||||
|
||||
func (m *ClusterQuery) GetServer() string {
|
||||
if m != nil {
|
||||
@@ -65,17 +65,17 @@ type ClusterResponse struct {
|
||||
func (m *ClusterResponse) Reset() { *m = ClusterResponse{} }
|
||||
func (m *ClusterResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*ClusterResponse) ProtoMessage() {}
|
||||
func (*ClusterResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
||||
func (*ClusterResponse) Descriptor() ([]byte, []int) { return fileDescriptorCluster, []int{1} }
|
||||
|
||||
type ClusterUpdateRequest struct {
|
||||
Server string `protobuf:"bytes,1,opt,name=server" json:"server,omitempty"`
|
||||
Server string `protobuf:"bytes,1,opt,name=server,proto3" json:"server,omitempty"`
|
||||
Cluster *github_com_argoproj_argo_cd_pkg_apis_application_v1alpha1.Cluster `protobuf:"bytes,2,opt,name=cluster" json:"cluster,omitempty"`
|
||||
}
|
||||
|
||||
func (m *ClusterUpdateRequest) Reset() { *m = ClusterUpdateRequest{} }
|
||||
func (m *ClusterUpdateRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*ClusterUpdateRequest) ProtoMessage() {}
|
||||
func (*ClusterUpdateRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
|
||||
func (*ClusterUpdateRequest) Descriptor() ([]byte, []int) { return fileDescriptorCluster, []int{2} }
|
||||
|
||||
func (m *ClusterUpdateRequest) GetServer() string {
|
||||
if m != nil {
|
||||
@@ -381,39 +381,514 @@ var _ClusterService_serviceDesc = grpc.ServiceDesc{
|
||||
Metadata: "server/cluster/cluster.proto",
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("server/cluster/cluster.proto", fileDescriptor0) }
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 496 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x54, 0xcd, 0x6e, 0xd4, 0x30,
|
||||
0x10, 0xc6, 0x05, 0xa5, 0x60, 0x10, 0x3f, 0xa3, 0x16, 0x2d, 0xd9, 0x82, 0x96, 0x08, 0x55, 0xd5,
|
||||
0x0a, 0x6c, 0x6d, 0xb9, 0xa0, 0x1e, 0x5b, 0x7e, 0x84, 0xc4, 0xa1, 0x6c, 0xe1, 0x82, 0x2a, 0x21,
|
||||
0x37, 0x19, 0xd2, 0xb0, 0x21, 0x36, 0xb6, 0x37, 0x12, 0x42, 0x08, 0x09, 0xae, 0x9c, 0xe0, 0xc8,
|
||||
0x33, 0xf0, 0x0e, 0xbc, 0x03, 0xaf, 0xc0, 0x83, 0xa0, 0x38, 0x36, 0x6d, 0xb7, 0x4a, 0x2f, 0xec,
|
||||
0x81, 0x53, 0xec, 0xb1, 0xf3, 0x7d, 0xf3, 0xcd, 0x7c, 0x1e, 0xba, 0x62, 0x50, 0xd7, 0xa8, 0x79,
|
||||
0x5a, 0x4e, 0x8d, 0x3d, 0xf8, 0x32, 0xa5, 0xa5, 0x95, 0xb0, 0xe8, 0xb7, 0xf1, 0x52, 0x2e, 0x73,
|
||||
0xe9, 0x62, 0xbc, 0x59, 0xb5, 0xc7, 0xf1, 0x4a, 0x2e, 0x65, 0x5e, 0x22, 0x17, 0xaa, 0xe0, 0xa2,
|
||||
0xaa, 0xa4, 0x15, 0xb6, 0x90, 0x95, 0xf1, 0xa7, 0xc9, 0xe4, 0x9e, 0x61, 0x85, 0x74, 0xa7, 0xa9,
|
||||
0xd4, 0xc8, 0xeb, 0x11, 0xcf, 0xb1, 0x42, 0x2d, 0x2c, 0x66, 0xfe, 0xce, 0xe3, 0xbc, 0xb0, 0xfb,
|
||||
0xd3, 0x3d, 0x96, 0xca, 0x37, 0x5c, 0x68, 0x47, 0xf1, 0xda, 0x2d, 0xee, 0xa4, 0x19, 0x57, 0x93,
|
||||
0xbc, 0xf9, 0xd9, 0x70, 0xa1, 0x54, 0x59, 0xa4, 0x0e, 0x9c, 0xd7, 0x23, 0x51, 0xaa, 0x7d, 0x71,
|
||||
0x0c, 0x2a, 0x59, 0xa5, 0x17, 0xb6, 0xda, 0x6c, 0x9f, 0x4e, 0x51, 0xbf, 0x83, 0xab, 0x34, 0x6a,
|
||||
0xb5, 0xf5, 0xc8, 0x80, 0xac, 0x9d, 0x1b, 0xfb, 0x5d, 0x72, 0x85, 0x5e, 0xf2, 0xf7, 0xc6, 0x68,
|
||||
0x94, 0xac, 0x0c, 0x26, 0x5f, 0x08, 0x5d, 0xf2, 0xb1, 0xe7, 0x2a, 0x13, 0x16, 0xc7, 0xf8, 0x76,
|
||||
0x8a, 0xc6, 0x76, 0x61, 0xc0, 0x2e, 0x0d, 0x95, 0xe9, 0x2d, 0x0c, 0xc8, 0xda, 0xf9, 0xf5, 0x4d,
|
||||
0x76, 0x20, 0x84, 0x05, 0x21, 0x6e, 0xf1, 0x32, 0xcd, 0x98, 0x9a, 0xe4, 0xac, 0x11, 0xc2, 0x0e,
|
||||
0x09, 0x61, 0x41, 0x08, 0x0b, 0xd9, 0x04, 0xc8, 0xf5, 0x9f, 0x8b, 0xf4, 0xa2, 0x0f, 0xee, 0xa0,
|
||||
0xae, 0x8b, 0x14, 0xe1, 0x23, 0x3d, 0xf3, 0xa4, 0x30, 0x16, 0x96, 0x59, 0x68, 0xd0, 0x61, 0xad,
|
||||
0xf1, 0xc3, 0x7f, 0xa7, 0x6f, 0xe0, 0x93, 0xde, 0xa7, 0x5f, 0xbf, 0xbf, 0x2d, 0x00, 0x5c, 0x76,
|
||||
0x4d, 0xab, 0x47, 0xc1, 0x0e, 0x06, 0x7e, 0x10, 0x1a, 0x6d, 0x69, 0x14, 0x16, 0x61, 0x0e, 0x5a,
|
||||
0xe3, 0x39, 0x60, 0x24, 0x7d, 0x97, 0xec, 0x72, 0x72, 0x2c, 0xd9, 0x0d, 0x32, 0x84, 0xcf, 0x84,
|
||||
0x9e, 0x7e, 0x84, 0x9d, 0x05, 0x9b, 0x07, 0xff, 0x4d, 0xc7, 0xdf, 0x87, 0x6b, 0xb3, 0xfc, 0xfc,
|
||||
0x7d, 0x6b, 0x93, 0x0f, 0xf0, 0x95, 0xd0, 0xa8, 0x75, 0xd4, 0x7f, 0x53, 0xb5, 0x53, 0xf0, 0x9d,
|
||||
0x50, 0xea, 0x6d, 0xfe, 0x60, 0xe7, 0x19, 0x5c, 0x9f, 0xad, 0xd0, 0x91, 0x27, 0x30, 0x17, 0xce,
|
||||
0xa1, 0xab, 0xd4, 0xad, 0xb8, 0xbb, 0x52, 0x1b, 0xc1, 0xfc, 0xb0, 0x4b, 0xa3, 0xfb, 0x58, 0xa2,
|
||||
0xc5, 0xae, 0xd6, 0xf5, 0x66, 0xc3, 0x7f, 0x9f, 0xb1, 0x6f, 0xc8, 0xf0, 0x84, 0x86, 0xbc, 0xa2,
|
||||
0x67, 0x1b, 0xa3, 0x6f, 0xcb, 0xcc, 0x74, 0xe1, 0xf7, 0x59, 0x3b, 0xb7, 0x1a, 0x5d, 0xac, 0x99,
|
||||
0x5b, 0xac, 0x1e, 0xb1, 0x6d, 0x99, 0xb9, 0x07, 0xb2, 0xea, 0x28, 0x06, 0x70, 0xa3, 0x93, 0x82,
|
||||
0x2b, 0x99, 0x99, 0xcd, 0xdb, 0x2f, 0x86, 0x27, 0x4d, 0xb6, 0xa3, 0x43, 0x77, 0x2f, 0x72, 0x13,
|
||||
0xec, 0xee, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x3b, 0x95, 0xa3, 0x36, 0x8d, 0x05, 0x00, 0x00,
|
||||
func (m *ClusterQuery) 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 *ClusterQuery) MarshalTo(dAtA []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.Server) > 0 {
|
||||
dAtA[i] = 0xa
|
||||
i++
|
||||
i = encodeVarintCluster(dAtA, i, uint64(len(m.Server)))
|
||||
i += copy(dAtA[i:], m.Server)
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (m *ClusterResponse) 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 *ClusterResponse) MarshalTo(dAtA []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (m *ClusterUpdateRequest) 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 *ClusterUpdateRequest) MarshalTo(dAtA []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.Server) > 0 {
|
||||
dAtA[i] = 0xa
|
||||
i++
|
||||
i = encodeVarintCluster(dAtA, i, uint64(len(m.Server)))
|
||||
i += copy(dAtA[i:], m.Server)
|
||||
}
|
||||
if m.Cluster != nil {
|
||||
dAtA[i] = 0x12
|
||||
i++
|
||||
i = encodeVarintCluster(dAtA, i, uint64(m.Cluster.Size()))
|
||||
n1, err := m.Cluster.MarshalTo(dAtA[i:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i += n1
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func encodeVarintCluster(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 *ClusterQuery) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
l = len(m.Server)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovCluster(uint64(l))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *ClusterResponse) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *ClusterUpdateRequest) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
l = len(m.Server)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovCluster(uint64(l))
|
||||
}
|
||||
if m.Cluster != nil {
|
||||
l = m.Cluster.Size()
|
||||
n += 1 + l + sovCluster(uint64(l))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func sovCluster(x uint64) (n int) {
|
||||
for {
|
||||
n++
|
||||
x >>= 7
|
||||
if x == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
func sozCluster(x uint64) (n int) {
|
||||
return sovCluster(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||
}
|
||||
func (m *ClusterQuery) 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 ErrIntOverflowCluster
|
||||
}
|
||||
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: ClusterQuery: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: ClusterQuery: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Server", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowCluster
|
||||
}
|
||||
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 ErrInvalidLengthCluster
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Server = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipCluster(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthCluster
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *ClusterResponse) 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 ErrIntOverflowCluster
|
||||
}
|
||||
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: ClusterResponse: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: ClusterResponse: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipCluster(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthCluster
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *ClusterUpdateRequest) 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 ErrIntOverflowCluster
|
||||
}
|
||||
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: ClusterUpdateRequest: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: ClusterUpdateRequest: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Server", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowCluster
|
||||
}
|
||||
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 ErrInvalidLengthCluster
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Server = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Cluster", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowCluster
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthCluster
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if m.Cluster == nil {
|
||||
m.Cluster = &github_com_argoproj_argo_cd_pkg_apis_application_v1alpha1.Cluster{}
|
||||
}
|
||||
if err := m.Cluster.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipCluster(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthCluster
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func skipCluster(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, ErrIntOverflowCluster
|
||||
}
|
||||
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, ErrIntOverflowCluster
|
||||
}
|
||||
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, ErrIntOverflowCluster
|
||||
}
|
||||
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, ErrInvalidLengthCluster
|
||||
}
|
||||
return iNdEx, nil
|
||||
case 3:
|
||||
for {
|
||||
var innerWire uint64
|
||||
var start int = iNdEx
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowCluster
|
||||
}
|
||||
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 := skipCluster(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 (
|
||||
ErrInvalidLengthCluster = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowCluster = fmt.Errorf("proto: integer overflow")
|
||||
)
|
||||
|
||||
func init() { proto.RegisterFile("server/cluster/cluster.proto", fileDescriptorCluster) }
|
||||
|
||||
var fileDescriptorCluster = []byte{
|
||||
// 510 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x54, 0x4f, 0x6b, 0x14, 0x31,
|
||||
0x14, 0x37, 0x55, 0xa6, 0x1a, 0xc5, 0x3f, 0xa1, 0x95, 0x75, 0xb6, 0x2e, 0xeb, 0x20, 0xa5, 0x2c,
|
||||
0x98, 0xb0, 0xf5, 0x52, 0x7a, 0x6c, 0xfd, 0x83, 0xe0, 0xa1, 0x6e, 0xf5, 0x22, 0x05, 0x49, 0x67,
|
||||
0x9e, 0xd3, 0x71, 0xc7, 0x49, 0x4c, 0xb2, 0x03, 0x22, 0x22, 0xe8, 0xd5, 0x93, 0x1e, 0xfd, 0x0c,
|
||||
0x7e, 0x07, 0x8f, 0x1e, 0x05, 0xbf, 0x80, 0x2c, 0x7e, 0x10, 0x99, 0x4c, 0x62, 0xdb, 0x2d, 0xb3,
|
||||
0x17, 0xe7, 0xe0, 0x69, 0x92, 0x97, 0xcc, 0xef, 0xf7, 0x7e, 0xef, 0xfd, 0xf2, 0xf0, 0x8a, 0x06,
|
||||
0x55, 0x82, 0x62, 0x71, 0x3e, 0xd1, 0xe6, 0xf0, 0x4b, 0xa5, 0x12, 0x46, 0x90, 0x45, 0xb7, 0x0d,
|
||||
0x97, 0x52, 0x91, 0x0a, 0x1b, 0x63, 0xd5, 0xaa, 0x3e, 0x0e, 0x57, 0x52, 0x21, 0xd2, 0x1c, 0x18,
|
||||
0x97, 0x19, 0xe3, 0x45, 0x21, 0x0c, 0x37, 0x99, 0x28, 0xb4, 0x3b, 0x8d, 0xc6, 0x1b, 0x9a, 0x66,
|
||||
0xc2, 0x9e, 0xc6, 0x42, 0x01, 0x2b, 0x87, 0x2c, 0x85, 0x02, 0x14, 0x37, 0x90, 0xb8, 0x3b, 0x0f,
|
||||
0xd2, 0xcc, 0x1c, 0x4c, 0xf6, 0x69, 0x2c, 0x5e, 0x32, 0xae, 0x2c, 0xc5, 0x0b, 0xbb, 0xb8, 0x15,
|
||||
0x27, 0x4c, 0x8e, 0xd3, 0xea, 0x67, 0xcd, 0xb8, 0x94, 0x79, 0x16, 0x5b, 0x70, 0x56, 0x0e, 0x79,
|
||||
0x2e, 0x0f, 0xf8, 0x09, 0xa8, 0x68, 0x15, 0x5f, 0xd8, 0xae, 0xb3, 0x7d, 0x34, 0x01, 0xf5, 0x9a,
|
||||
0x5c, 0xc5, 0x41, 0xad, 0xad, 0x83, 0xfa, 0x68, 0xed, 0xdc, 0xc8, 0xed, 0xa2, 0x2b, 0xf8, 0x92,
|
||||
0xbb, 0x37, 0x02, 0x2d, 0x45, 0xa1, 0x21, 0xfa, 0x88, 0xf0, 0x92, 0x8b, 0x3d, 0x91, 0x09, 0x37,
|
||||
0x30, 0x82, 0x57, 0x13, 0xd0, 0xa6, 0x09, 0x83, 0xec, 0x61, 0x5f, 0x99, 0xce, 0x42, 0x1f, 0xad,
|
||||
0x9d, 0x5f, 0xdf, 0xa2, 0x87, 0x42, 0xa8, 0x17, 0x62, 0x17, 0xcf, 0xe2, 0x84, 0xca, 0x71, 0x4a,
|
||||
0x2b, 0x21, 0xf4, 0x88, 0x10, 0xea, 0x85, 0x50, 0x9f, 0x8d, 0x87, 0x5c, 0xff, 0xb6, 0x88, 0x2f,
|
||||
0xba, 0xe0, 0x2e, 0xa8, 0x32, 0x8b, 0x81, 0xbc, 0xc3, 0x67, 0x1e, 0x66, 0xda, 0x90, 0x65, 0xea,
|
||||
0x1b, 0x74, 0x54, 0x6b, 0x78, 0xef, 0xdf, 0xe9, 0x2b, 0xf8, 0xa8, 0xf3, 0xfe, 0xe7, 0xef, 0xcf,
|
||||
0x0b, 0x84, 0x5c, 0xb6, 0x4d, 0x2b, 0x87, 0xde, 0x0e, 0x9a, 0x7c, 0x45, 0x38, 0xd8, 0x56, 0xc0,
|
||||
0x0d, 0x90, 0x16, 0xb4, 0x86, 0x2d, 0x60, 0x44, 0x5d, 0x9b, 0xec, 0x72, 0x74, 0x22, 0xd9, 0x4d,
|
||||
0x34, 0x20, 0x1f, 0x10, 0x3e, 0x7d, 0x1f, 0x1a, 0x0b, 0xd6, 0x06, 0xff, 0x0d, 0xcb, 0xdf, 0x25,
|
||||
0xd7, 0x66, 0xf9, 0xd9, 0x9b, 0xda, 0x26, 0x6f, 0xc9, 0x27, 0x84, 0x83, 0xda, 0x51, 0xff, 0x4d,
|
||||
0xd5, 0x4e, 0x91, 0x2f, 0x08, 0x63, 0x67, 0xf3, 0xbb, 0xbb, 0x8f, 0xc9, 0xf5, 0xd9, 0x0a, 0x1d,
|
||||
0x7b, 0x02, 0xad, 0x70, 0x0e, 0x6c, 0xa5, 0x6e, 0x86, 0xcd, 0x95, 0xda, 0xf4, 0xe6, 0x27, 0x7b,
|
||||
0x38, 0xb8, 0x03, 0x39, 0x18, 0x68, 0x6a, 0x5d, 0x67, 0x36, 0xfc, 0xf7, 0x19, 0xbb, 0x86, 0x0c,
|
||||
0xe6, 0x34, 0xe4, 0x39, 0x3e, 0x5b, 0x19, 0x7d, 0x47, 0x24, 0xba, 0x09, 0xbf, 0x4b, 0xeb, 0xb9,
|
||||
0x55, 0xe9, 0xa2, 0xd5, 0xdc, 0xa2, 0xe5, 0x90, 0xee, 0x88, 0xc4, 0x3e, 0x90, 0x55, 0x4b, 0xd1,
|
||||
0x27, 0xbd, 0x46, 0x0a, 0x26, 0x45, 0xa2, 0xb7, 0x36, 0xbe, 0x4f, 0x7b, 0xe8, 0xc7, 0xb4, 0x87,
|
||||
0x7e, 0x4d, 0x7b, 0xe8, 0xe9, 0x60, 0xde, 0x94, 0x3b, 0x3e, 0x80, 0xf7, 0x03, 0x3b, 0xcd, 0x6e,
|
||||
0xff, 0x09, 0x00, 0x00, 0xff, 0xff, 0x25, 0x28, 0x85, 0xfb, 0x99, 0x05, 0x00, 0x00,
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ func repoURLToSecretName(repo string) string {
|
||||
h := fnv.New32a()
|
||||
_, _ = h.Write([]byte(repo))
|
||||
parts := strings.Split(strings.TrimSuffix(repo, ".git"), "/")
|
||||
return fmt.Sprintf("repo-%s-%v", parts[len(parts)-1], h.Sum32())
|
||||
return fmt.Sprintf("repo-%s-%v", strings.ToLower(parts[len(parts)-1]), h.Sum32())
|
||||
}
|
||||
|
||||
// repoToStringData converts a repository object to string data for serialization to a secret
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||
// source: server/repository/repository.proto
|
||||
|
||||
/*
|
||||
Package repository is a generated protocol buffer package.
|
||||
Package repository is a generated protocol buffer package.
|
||||
|
||||
Repository Service
|
||||
Repository Service
|
||||
|
||||
Repository Service API performs CRUD actions against repository resources
|
||||
Repository Service API performs CRUD actions against repository resources
|
||||
|
||||
It is generated from these files:
|
||||
server/repository/repository.proto
|
||||
It is generated from these files:
|
||||
server/repository/repository.proto
|
||||
|
||||
It has these top-level messages:
|
||||
RepoQuery
|
||||
RepoResponse
|
||||
RepoUpdateRequest
|
||||
It has these top-level messages:
|
||||
RepoQuery
|
||||
RepoResponse
|
||||
RepoUpdateRequest
|
||||
*/
|
||||
package repository
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import proto "github.com/gogo/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
import _ "github.com/gogo/protobuf/gogoproto"
|
||||
@@ -26,10 +26,10 @@ import _ "google.golang.org/genproto/googleapis/api/annotations"
|
||||
import _ "k8s.io/api/core/v1"
|
||||
import github_com_argoproj_argo_cd_pkg_apis_application_v1alpha1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
|
||||
import (
|
||||
context "golang.org/x/net/context"
|
||||
grpc "google.golang.org/grpc"
|
||||
)
|
||||
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
|
||||
@@ -40,17 +40,17 @@ var _ = math.Inf
|
||||
// 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.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
// RepoQuery is a query for Repository resources
|
||||
type RepoQuery struct {
|
||||
Repo string `protobuf:"bytes,1,opt,name=repo" json:"repo,omitempty"`
|
||||
Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
|
||||
}
|
||||
|
||||
func (m *RepoQuery) Reset() { *m = RepoQuery{} }
|
||||
func (m *RepoQuery) String() string { return proto.CompactTextString(m) }
|
||||
func (*RepoQuery) ProtoMessage() {}
|
||||
func (*RepoQuery) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||
func (*RepoQuery) Descriptor() ([]byte, []int) { return fileDescriptorRepository, []int{0} }
|
||||
|
||||
func (m *RepoQuery) GetRepo() string {
|
||||
if m != nil {
|
||||
@@ -65,17 +65,17 @@ type RepoResponse struct {
|
||||
func (m *RepoResponse) Reset() { *m = RepoResponse{} }
|
||||
func (m *RepoResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*RepoResponse) ProtoMessage() {}
|
||||
func (*RepoResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
||||
func (*RepoResponse) Descriptor() ([]byte, []int) { return fileDescriptorRepository, []int{1} }
|
||||
|
||||
type RepoUpdateRequest struct {
|
||||
Url string `protobuf:"bytes,1,opt,name=url" json:"url,omitempty"`
|
||||
Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"`
|
||||
Repo *github_com_argoproj_argo_cd_pkg_apis_application_v1alpha1.Repository `protobuf:"bytes,2,opt,name=repo" json:"repo,omitempty"`
|
||||
}
|
||||
|
||||
func (m *RepoUpdateRequest) Reset() { *m = RepoUpdateRequest{} }
|
||||
func (m *RepoUpdateRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*RepoUpdateRequest) ProtoMessage() {}
|
||||
func (*RepoUpdateRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
|
||||
func (*RepoUpdateRequest) Descriptor() ([]byte, []int) { return fileDescriptorRepository, []int{2} }
|
||||
|
||||
func (m *RepoUpdateRequest) GetUrl() string {
|
||||
if m != nil {
|
||||
@@ -346,38 +346,513 @@ var _RepositoryService_serviceDesc = grpc.ServiceDesc{
|
||||
Metadata: "server/repository/repository.proto",
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("server/repository/repository.proto", fileDescriptor0) }
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 470 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x94, 0x41, 0x6b, 0x14, 0x31,
|
||||
0x14, 0xc7, 0x4d, 0x5b, 0x06, 0x1a, 0x45, 0xf4, 0x51, 0xa5, 0x8e, 0x2d, 0x2d, 0xf1, 0xb2, 0x14,
|
||||
0x9a, 0xb0, 0xf5, 0x22, 0x3d, 0xaa, 0x45, 0x0a, 0x5e, 0x9c, 0xea, 0x41, 0x0f, 0x4a, 0x3a, 0xfb,
|
||||
0x98, 0xc6, 0x1d, 0x27, 0x31, 0xc9, 0x0c, 0x14, 0x29, 0x88, 0x07, 0xf1, 0xee, 0xc5, 0x83, 0xdf,
|
||||
0xc3, 0x0f, 0xe2, 0x57, 0xf0, 0x5b, 0x78, 0x91, 0x64, 0xa6, 0xbb, 0x6b, 0xbb, 0xed, 0x69, 0x0e,
|
||||
0xbd, 0xfd, 0xe7, 0x4d, 0xf2, 0xf2, 0xcb, 0xff, 0xe5, 0x3d, 0xca, 0x1c, 0xda, 0x06, 0xad, 0xb0,
|
||||
0x68, 0xb4, 0x53, 0x5e, 0xdb, 0xe3, 0x19, 0xc9, 0x8d, 0xd5, 0x5e, 0x03, 0x9d, 0x46, 0xd2, 0x95,
|
||||
0x42, 0x17, 0x3a, 0x86, 0x45, 0x50, 0xed, 0x8a, 0x74, 0xad, 0xd0, 0xba, 0x28, 0x51, 0x48, 0xa3,
|
||||
0x84, 0xac, 0x2a, 0xed, 0xa5, 0x57, 0xba, 0x72, 0xdd, 0x5f, 0x36, 0x7e, 0xe4, 0xb8, 0xd2, 0xf1,
|
||||
0x6f, 0xae, 0x2d, 0x8a, 0x66, 0x28, 0x0a, 0xac, 0xd0, 0x4a, 0x8f, 0xa3, 0x6e, 0xcd, 0x7e, 0xa1,
|
||||
0xfc, 0x51, 0x7d, 0xc8, 0x73, 0xfd, 0x41, 0x48, 0x1b, 0x8f, 0x78, 0x1f, 0xc5, 0x76, 0x3e, 0x12,
|
||||
0x66, 0x5c, 0x84, 0xcd, 0x4e, 0x48, 0x63, 0x4a, 0x95, 0xc7, 0xe4, 0xa2, 0x19, 0xca, 0xd2, 0x1c,
|
||||
0xc9, 0x73, 0xa9, 0xd8, 0x06, 0x5d, 0xce, 0xd0, 0xe8, 0x17, 0x35, 0xda, 0x63, 0x00, 0xba, 0x14,
|
||||
0xe8, 0x57, 0xc9, 0x26, 0x19, 0x2c, 0x67, 0x51, 0xb3, 0x9b, 0xf4, 0x46, 0x58, 0x90, 0xa1, 0x33,
|
||||
0xba, 0x72, 0xc8, 0x3e, 0x13, 0x7a, 0x3b, 0x04, 0x5e, 0x99, 0x91, 0xf4, 0x98, 0xe1, 0xc7, 0x1a,
|
||||
0x9d, 0x87, 0x5b, 0x74, 0xb1, 0xb6, 0x65, 0xb7, 0x31, 0x48, 0x78, 0xdd, 0xe5, 0x5a, 0xd8, 0x24,
|
||||
0x83, 0xeb, 0x3b, 0x7b, 0x7c, 0x8a, 0xcc, 0x4f, 0x91, 0xa3, 0x78, 0x97, 0x8f, 0xb8, 0x19, 0x17,
|
||||
0x3c, 0x20, 0xf3, 0x19, 0x64, 0x7e, 0x8a, 0xcc, 0xb3, 0x89, 0xa1, 0x2d, 0xd2, 0xce, 0xdf, 0xa4,
|
||||
0x45, 0x68, 0x83, 0x07, 0x68, 0x1b, 0x95, 0x23, 0x7c, 0x25, 0x74, 0xe9, 0xb9, 0x72, 0x1e, 0xee,
|
||||
0xf0, 0x99, 0xa2, 0x4c, 0x2e, 0x97, 0xee, 0xf7, 0x82, 0x10, 0x4e, 0x60, 0x6b, 0x5f, 0x7e, 0xff,
|
||||
0xf9, 0xbe, 0x70, 0x17, 0x56, 0x62, 0x95, 0x9a, 0xe1, 0xf4, 0x15, 0x28, 0x74, 0xf0, 0x8b, 0xd0,
|
||||
0xe4, 0x89, 0x45, 0xe9, 0x11, 0xfa, 0xb9, 0x76, 0xda, 0x4f, 0x1a, 0xb6, 0x11, 0xb1, 0xef, 0xb1,
|
||||
0xb9, 0xd8, 0xbb, 0x64, 0x0b, 0xbe, 0x11, 0xba, 0xf8, 0x0c, 0x2f, 0x74, 0xb0, 0x27, 0x8c, 0x07,
|
||||
0x11, 0x63, 0x1d, 0xee, 0xcf, 0xc3, 0x10, 0x9f, 0xc2, 0xd7, 0x09, 0xfc, 0x20, 0x34, 0x69, 0x9f,
|
||||
0xd8, 0x15, 0x33, 0xf1, 0x1a, 0xfc, 0x24, 0x94, 0x76, 0xaf, 0x7f, 0xef, 0xe0, 0x25, 0xac, 0x9f,
|
||||
0x35, 0xeb, 0xbf, 0xce, 0xe8, 0xeb, 0xd8, 0x41, 0x34, 0x8d, 0xa5, 0xe9, 0x7c, 0xd3, 0x6a, 0x5b,
|
||||
0x9e, 0xec, 0xc6, 0xee, 0x80, 0xb7, 0x34, 0x79, 0x8a, 0x25, 0x7a, 0xbc, 0xa8, 0x8c, 0xab, 0x67,
|
||||
0xc3, 0x93, 0xde, 0xee, 0x2a, 0xb3, 0x75, 0x59, 0x65, 0x1e, 0x8b, 0x37, 0xdb, 0x97, 0x8d, 0x9f,
|
||||
0x73, 0x23, 0xf2, 0x30, 0x89, 0x93, 0xe6, 0xe1, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x9e, 0x0a,
|
||||
0x51, 0x7f, 0x3e, 0x05, 0x00, 0x00,
|
||||
func (m *RepoQuery) 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 *RepoQuery) MarshalTo(dAtA []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.Repo) > 0 {
|
||||
dAtA[i] = 0xa
|
||||
i++
|
||||
i = encodeVarintRepository(dAtA, i, uint64(len(m.Repo)))
|
||||
i += copy(dAtA[i:], m.Repo)
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (m *RepoResponse) 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 *RepoResponse) MarshalTo(dAtA []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (m *RepoUpdateRequest) 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 *RepoUpdateRequest) MarshalTo(dAtA []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.Url) > 0 {
|
||||
dAtA[i] = 0xa
|
||||
i++
|
||||
i = encodeVarintRepository(dAtA, i, uint64(len(m.Url)))
|
||||
i += copy(dAtA[i:], m.Url)
|
||||
}
|
||||
if m.Repo != nil {
|
||||
dAtA[i] = 0x12
|
||||
i++
|
||||
i = encodeVarintRepository(dAtA, i, uint64(m.Repo.Size()))
|
||||
n1, err := m.Repo.MarshalTo(dAtA[i:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i += n1
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func encodeVarintRepository(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 *RepoQuery) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
l = len(m.Repo)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovRepository(uint64(l))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *RepoResponse) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *RepoUpdateRequest) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
l = len(m.Url)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovRepository(uint64(l))
|
||||
}
|
||||
if m.Repo != nil {
|
||||
l = m.Repo.Size()
|
||||
n += 1 + l + sovRepository(uint64(l))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func sovRepository(x uint64) (n int) {
|
||||
for {
|
||||
n++
|
||||
x >>= 7
|
||||
if x == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
func sozRepository(x uint64) (n int) {
|
||||
return sovRepository(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||
}
|
||||
func (m *RepoQuery) 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 ErrIntOverflowRepository
|
||||
}
|
||||
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: RepoQuery: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: RepoQuery: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Repo", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowRepository
|
||||
}
|
||||
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 ErrInvalidLengthRepository
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Repo = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipRepository(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthRepository
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *RepoResponse) 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 ErrIntOverflowRepository
|
||||
}
|
||||
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: RepoResponse: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: RepoResponse: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipRepository(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthRepository
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *RepoUpdateRequest) 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 ErrIntOverflowRepository
|
||||
}
|
||||
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: RepoUpdateRequest: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: RepoUpdateRequest: 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 ErrIntOverflowRepository
|
||||
}
|
||||
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 ErrInvalidLengthRepository
|
||||
}
|
||||
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 Repo", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowRepository
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthRepository
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if m.Repo == nil {
|
||||
m.Repo = &github_com_argoproj_argo_cd_pkg_apis_application_v1alpha1.Repository{}
|
||||
}
|
||||
if err := m.Repo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipRepository(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthRepository
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func skipRepository(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, ErrIntOverflowRepository
|
||||
}
|
||||
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, ErrIntOverflowRepository
|
||||
}
|
||||
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, ErrIntOverflowRepository
|
||||
}
|
||||
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, ErrInvalidLengthRepository
|
||||
}
|
||||
return iNdEx, nil
|
||||
case 3:
|
||||
for {
|
||||
var innerWire uint64
|
||||
var start int = iNdEx
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowRepository
|
||||
}
|
||||
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 := skipRepository(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 (
|
||||
ErrInvalidLengthRepository = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowRepository = fmt.Errorf("proto: integer overflow")
|
||||
)
|
||||
|
||||
func init() { proto.RegisterFile("server/repository/repository.proto", fileDescriptorRepository) }
|
||||
|
||||
var fileDescriptorRepository = []byte{
|
||||
// 486 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x94, 0x41, 0x6b, 0x14, 0x31,
|
||||
0x14, 0xc7, 0x4d, 0x5b, 0x06, 0x1a, 0x45, 0x34, 0x54, 0xa9, 0x63, 0xbb, 0x2d, 0xf1, 0xb2, 0x14,
|
||||
0x9a, 0xb0, 0xf5, 0x22, 0xf5, 0xa6, 0x16, 0x29, 0x78, 0x71, 0xaa, 0x07, 0x3d, 0x28, 0xe9, 0xcc,
|
||||
0x63, 0x1a, 0x77, 0x9c, 0xc4, 0x24, 0x33, 0x50, 0xa4, 0x20, 0x1e, 0xc4, 0xbb, 0x17, 0x0f, 0x7e,
|
||||
0x0f, 0xbf, 0x82, 0x47, 0xc1, 0x2f, 0x20, 0x8b, 0xdf, 0xc2, 0x8b, 0x24, 0x33, 0xdd, 0x5d, 0xdb,
|
||||
0x6d, 0x4f, 0x73, 0xe8, 0xed, 0x3f, 0x6f, 0x92, 0x97, 0x5f, 0xfe, 0x2f, 0xef, 0x61, 0x6a, 0xc1,
|
||||
0xd4, 0x60, 0xb8, 0x01, 0xad, 0xac, 0x74, 0xca, 0x1c, 0x4e, 0x49, 0xa6, 0x8d, 0x72, 0x8a, 0xe0,
|
||||
0x49, 0x24, 0x5e, 0xca, 0x55, 0xae, 0x42, 0x98, 0x7b, 0xd5, 0xac, 0x88, 0x57, 0x72, 0xa5, 0xf2,
|
||||
0x02, 0xb8, 0xd0, 0x92, 0x8b, 0xb2, 0x54, 0x4e, 0x38, 0xa9, 0x4a, 0xdb, 0xfe, 0xa5, 0xc3, 0x7b,
|
||||
0x96, 0x49, 0x15, 0xfe, 0xa6, 0xca, 0x00, 0xaf, 0x07, 0x3c, 0x87, 0x12, 0x8c, 0x70, 0x90, 0xb5,
|
||||
0x6b, 0x76, 0x73, 0xe9, 0x0e, 0xaa, 0x7d, 0x96, 0xaa, 0xb7, 0x5c, 0x98, 0x70, 0xc4, 0x9b, 0x20,
|
||||
0x36, 0xd3, 0x8c, 0xeb, 0x61, 0xee, 0x37, 0x5b, 0x2e, 0xb4, 0x2e, 0x64, 0x1a, 0x92, 0xf3, 0x7a,
|
||||
0x20, 0x0a, 0x7d, 0x20, 0x4e, 0xa5, 0xa2, 0x6b, 0x78, 0x31, 0x01, 0xad, 0x9e, 0x56, 0x60, 0x0e,
|
||||
0x09, 0xc1, 0x0b, 0x9e, 0x7e, 0x19, 0xad, 0xa3, 0xfe, 0x62, 0x12, 0x34, 0xbd, 0x8a, 0xaf, 0xf8,
|
||||
0x05, 0x09, 0x58, 0xad, 0x4a, 0x0b, 0xf4, 0x03, 0xc2, 0xd7, 0x7d, 0xe0, 0xb9, 0xce, 0x84, 0x83,
|
||||
0x04, 0xde, 0x55, 0x60, 0x1d, 0xb9, 0x86, 0xe7, 0x2b, 0x53, 0xb4, 0x1b, 0xbd, 0x24, 0x2f, 0xda,
|
||||
0x5c, 0x73, 0xeb, 0xa8, 0x7f, 0x79, 0x6b, 0x87, 0x4d, 0x90, 0xd9, 0x31, 0x72, 0x10, 0xaf, 0xd3,
|
||||
0x8c, 0xe9, 0x61, 0xce, 0x3c, 0x32, 0x9b, 0x42, 0x66, 0xc7, 0xc8, 0x2c, 0x19, 0x1b, 0xda, 0x20,
|
||||
0x6d, 0xfd, 0x8d, 0x1a, 0x84, 0x26, 0xb8, 0x07, 0xa6, 0x96, 0x29, 0x90, 0x4f, 0x08, 0x2f, 0x3c,
|
||||
0x91, 0xd6, 0x91, 0x1b, 0x6c, 0xaa, 0x28, 0xe3, 0xcb, 0xc5, 0xbb, 0x9d, 0x20, 0xf8, 0x13, 0xe8,
|
||||
0xca, 0xc7, 0x5f, 0x7f, 0xbe, 0xcc, 0xdd, 0x24, 0x4b, 0xa1, 0x4a, 0xf5, 0x60, 0xf2, 0x0a, 0x24,
|
||||
0x58, 0xf2, 0x1d, 0xe1, 0xe8, 0xa1, 0x01, 0xe1, 0x80, 0x74, 0x73, 0xed, 0xb8, 0x9b, 0x34, 0x74,
|
||||
0x2d, 0x60, 0xdf, 0xa2, 0x33, 0xb1, 0xb7, 0xd1, 0x06, 0xf9, 0x8c, 0xf0, 0xfc, 0x63, 0x38, 0xd3,
|
||||
0xc1, 0x8e, 0x30, 0xee, 0x04, 0x8c, 0x55, 0x72, 0x7b, 0x16, 0x06, 0x7f, 0xef, 0xbf, 0x8e, 0xc8,
|
||||
0x57, 0x84, 0xa3, 0xe6, 0x89, 0x5d, 0x30, 0x13, 0x2f, 0x91, 0x6f, 0x08, 0xe3, 0xf6, 0xf5, 0xef,
|
||||
0xec, 0x3d, 0x23, 0xab, 0x27, 0xcd, 0xfa, 0xaf, 0x33, 0xba, 0x3a, 0xb6, 0x1f, 0x4c, 0xa3, 0x71,
|
||||
0x3c, 0xdb, 0xb4, 0xca, 0x14, 0x47, 0xdb, 0xa1, 0x3b, 0xc8, 0x2b, 0x1c, 0x3d, 0x82, 0x02, 0x1c,
|
||||
0x9c, 0x55, 0xc6, 0xe5, 0x93, 0xe1, 0x71, 0x6f, 0xb7, 0x95, 0xd9, 0x38, 0xaf, 0x32, 0x0f, 0xee,
|
||||
0xff, 0x18, 0xf5, 0xd0, 0xcf, 0x51, 0x0f, 0xfd, 0x1e, 0xf5, 0xd0, 0xcb, 0xcd, 0xf3, 0x46, 0xd1,
|
||||
0xa9, 0x71, 0xb9, 0x1f, 0x85, 0xa9, 0x73, 0xf7, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd4, 0xa0,
|
||||
0x05, 0x80, 0x4a, 0x05, 0x00, 0x00,
|
||||
}
|
||||
|
||||
18
server/repository/repository_test.go
Normal file
18
server/repository/repository_test.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package repository
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestRepoURLToSecretName(t *testing.T) {
|
||||
tables := map[string]string{
|
||||
"git://git@github.com:argoproj/ARGO-cd.git": "repo-argo-cd-83273445",
|
||||
"https://github.com/argoproj/ARGO-cd": "repo-argo-cd-1890113693",
|
||||
"https://github.com/argoproj/argo-cd": "repo-argo-cd-42374749",
|
||||
"ssh://git@github.com/argoproj/argo-cd.git": "repo-argo-cd-3569564120",
|
||||
}
|
||||
|
||||
for k, v := range tables {
|
||||
if sn := repoURLToSecretName(k); sn != v {
|
||||
t.Errorf("Expected secret name %q for repo %q; instead, got %q", v, k, sn)
|
||||
}
|
||||
}
|
||||
}
|
||||
290
server/server.go
290
server/server.go
@@ -2,33 +2,46 @@ package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"strings"
|
||||
|
||||
argocd "github.com/argoproj/argo-cd"
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
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/version"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
"github.com/argoproj/argo-cd/util/config"
|
||||
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"
|
||||
tlsutil "github.com/argoproj/argo-cd/util/tls"
|
||||
golang_proto "github.com/golang/protobuf/proto"
|
||||
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
|
||||
grpc_logrus "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus"
|
||||
"github.com/grpc-ecosystem/grpc-gateway/runtime"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/soheilhy/cmux"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/reflection"
|
||||
"google.golang.org/grpc/status"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
)
|
||||
|
||||
const (
|
||||
port = 8080
|
||||
port = 8080
|
||||
authCookieName = "argocd.argoproj.io/auth-token"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -37,31 +50,33 @@ var (
|
||||
|
||||
// ArgoCDServer is the API server for ArgoCD
|
||||
type ArgoCDServer struct {
|
||||
ns string
|
||||
staticAssetsDir string
|
||||
kubeclientset kubernetes.Interface
|
||||
appclientset appclientset.Interface
|
||||
repoclientset reposerver.Clientset
|
||||
settings util.ArgoCDSettings
|
||||
log *log.Entry
|
||||
ArgoCDServerOpts
|
||||
|
||||
settings config.ArgoCDSettings
|
||||
log *log.Entry
|
||||
}
|
||||
|
||||
type ArgoCDServerOpts struct {
|
||||
DisableAuth bool
|
||||
Insecure bool
|
||||
Namespace string
|
||||
StaticAssetsDir string
|
||||
KubeClientset kubernetes.Interface
|
||||
AppClientset appclientset.Interface
|
||||
RepoClientset reposerver.Clientset
|
||||
}
|
||||
|
||||
// NewServer returns a new instance of the ArgoCD API server
|
||||
func NewServer(
|
||||
kubeclientset kubernetes.Interface, appclientset appclientset.Interface, repoclientset reposerver.Clientset, namespace, staticAssetsDir, configMapName string) *ArgoCDServer {
|
||||
configManager := util.NewConfigManager(kubeclientset, namespace, configMapName)
|
||||
func NewServer(opts ArgoCDServerOpts) *ArgoCDServer {
|
||||
configManager := config.NewConfigManager(opts.KubeClientset, opts.Namespace)
|
||||
settings, err := configManager.GetSettings()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return &ArgoCDServer{
|
||||
ns: namespace,
|
||||
kubeclientset: kubeclientset,
|
||||
appclientset: appclientset,
|
||||
repoclientset: repoclientset,
|
||||
log: log.NewEntry(log.New()),
|
||||
staticAssetsDir: staticAssetsDir,
|
||||
settings: settings,
|
||||
ArgoCDServerOpts: opts,
|
||||
log: log.NewEntry(log.New()),
|
||||
settings: *settings,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,82 +89,196 @@ func (a *ArgoCDServer) Run() {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
conn, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
grpcS := a.newGRPCServer()
|
||||
var httpS *http.Server
|
||||
var httpsS *http.Server
|
||||
if a.useTLS() {
|
||||
httpS = newRedirectServer()
|
||||
httpsS = a.newHTTPServer(ctx)
|
||||
} else {
|
||||
httpS = a.newHTTPServer(ctx)
|
||||
}
|
||||
|
||||
// Cmux is used to support servicing gRPC and HTTP1.1+JSON on the same port
|
||||
m := cmux.New(conn)
|
||||
grpcL := m.Match(cmux.HTTP2HeaderField("content-type", "application/grpc"))
|
||||
httpL := m.Match(cmux.HTTP1Fast())
|
||||
conn, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
|
||||
errors.CheckError(err)
|
||||
|
||||
// gRPC Server
|
||||
grpcS := grpc.NewServer(
|
||||
grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(
|
||||
grpc_logrus.StreamServerInterceptor(a.log),
|
||||
grpc_util.PanicLoggerStreamServerInterceptor(a.log),
|
||||
)),
|
||||
grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
|
||||
grpc_logrus.UnaryServerInterceptor(a.log),
|
||||
grpc_util.PanicLoggerUnaryServerInterceptor(a.log),
|
||||
)),
|
||||
)
|
||||
tcpm := cmux.New(conn)
|
||||
var tlsm cmux.CMux
|
||||
var grpcL net.Listener
|
||||
var httpL net.Listener
|
||||
var httpsL net.Listener
|
||||
if !a.useTLS() {
|
||||
httpL = tcpm.Match(cmux.HTTP1Fast())
|
||||
grpcL = tcpm.Match(cmux.HTTP2HeaderField("content-type", "application/grpc"))
|
||||
} else {
|
||||
// We first match on HTTP 1.1 methods.
|
||||
httpL = tcpm.Match(cmux.HTTP1Fast())
|
||||
|
||||
// If not matched, we assume that its TLS.
|
||||
tlsl := tcpm.Match(cmux.Any())
|
||||
tlsConfig := tls.Config{
|
||||
Certificates: []tls.Certificate{*a.settings.Certificate},
|
||||
}
|
||||
tlsl = tls.NewListener(tlsl, &tlsConfig)
|
||||
|
||||
// Now, we build another mux recursively to match HTTPS and GoRPC.
|
||||
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)
|
||||
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()) }()
|
||||
}
|
||||
err = tcpm.Serve()
|
||||
errors.CheckError(err)
|
||||
}
|
||||
|
||||
func (a *ArgoCDServer) useTLS() bool {
|
||||
if a.Insecure || a.settings.Certificate == nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (a *ArgoCDServer) newGRPCServer() *grpc.Server {
|
||||
var sOpts []grpc.ServerOption
|
||||
// NOTE: notice we do not configure the gRPC server here with TLS (e.g. grpc.Creds(creds))
|
||||
// This is because TLS handshaking occurs in cmux handling
|
||||
sOpts = append(sOpts, grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(
|
||||
grpc_logrus.StreamServerInterceptor(a.log),
|
||||
grpc_auth.StreamServerInterceptor(a.authenticate),
|
||||
grpc_util.ErrorCodeStreamServerInterceptor(),
|
||||
grpc_util.PanicLoggerStreamServerInterceptor(a.log),
|
||||
)))
|
||||
sOpts = append(sOpts, grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
|
||||
grpc_logrus.UnaryServerInterceptor(a.log),
|
||||
grpc_auth.UnaryServerInterceptor(a.authenticate),
|
||||
grpc_util.ErrorCodeUnaryServerInterceptor(),
|
||||
grpc_util.PanicLoggerUnaryServerInterceptor(a.log),
|
||||
)))
|
||||
|
||||
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)
|
||||
applicationService := application.NewServer(a.Namespace, a.KubeClientset, a.AppClientset, a.RepoClientset, repoService, clusterService)
|
||||
version.RegisterVersionServiceServer(grpcS, &version.Server{})
|
||||
clusterService := cluster.NewServer(a.ns, a.kubeclientset, a.appclientset)
|
||||
repoService := repository.NewServer(a.ns, a.kubeclientset, a.appclientset)
|
||||
cluster.RegisterClusterServiceServer(grpcS, clusterService)
|
||||
application.RegisterApplicationServiceServer(grpcS, application.NewServer(a.ns, a.kubeclientset, a.appclientset, a.repoclientset, repoService, clusterService))
|
||||
application.RegisterApplicationServiceServer(grpcS, applicationService)
|
||||
repository.RegisterRepositoryServiceServer(grpcS, repoService)
|
||||
session.RegisterSessionServiceServer(grpcS, sessionService)
|
||||
|
||||
// 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=/")
|
||||
w.Header().Set("Set-Cookie", cookie)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// newHTTPServer returns the HTTP server to serve HTTP/HTTPS requests. This is implemented
|
||||
// using grpc-gateway as a proxy to the gRPC server.
|
||||
func (a *ArgoCDServer) newHTTPServer(ctx context.Context) *http.Server {
|
||||
mux := http.NewServeMux()
|
||||
httpS := http.Server{
|
||||
Addr: endpoint,
|
||||
Handler: mux,
|
||||
}
|
||||
var dOpts []grpc.DialOption
|
||||
if a.useTLS() {
|
||||
// The following sets up the dial Options for grpc-gateway to talk to gRPC server over TLS.
|
||||
// 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,
|
||||
})
|
||||
dOpts = append(dOpts, grpc.WithTransportCredentials(dCreds))
|
||||
} else {
|
||||
dOpts = append(dOpts, grpc.WithInsecure())
|
||||
}
|
||||
|
||||
// HTTP 1.1+JSON Server
|
||||
// grpc-ecosystem/grpc-gateway is used to proxy HTTP requests to the corresponding gRPC call
|
||||
mux := http.NewServeMux()
|
||||
// NOTE: if a marshaller option is not supplied, grpc-gateway will default to the jsonpb from
|
||||
// golang/protobuf. Which does not support types such as time.Time. gogo/protobuf does support
|
||||
// time.Time, but does not support custom UnmarshalJSON() and MarshalJSON() methods. Therefore
|
||||
// we use our own Marshaler
|
||||
gwMuxOpts := runtime.WithMarshalerOption(runtime.MIMEWildcard, new(jsonutil.JSONMarshaler))
|
||||
gwmux := runtime.NewServeMux(gwMuxOpts)
|
||||
gwCookieOpts := runtime.WithForwardResponseOption(a.translateGrpcCookieHeader)
|
||||
gwmux := runtime.NewServeMux(gwMuxOpts, gwCookieOpts)
|
||||
mux.Handle("/api/", gwmux)
|
||||
dOpts := []grpc.DialOption{grpc.WithInsecure()}
|
||||
mustRegisterGWHandler(version.RegisterVersionServiceHandlerFromEndpoint, ctx, gwmux, endpoint, dOpts)
|
||||
mustRegisterGWHandler(cluster.RegisterClusterServiceHandlerFromEndpoint, ctx, gwmux, endpoint, dOpts)
|
||||
mustRegisterGWHandler(application.RegisterApplicationServiceHandlerFromEndpoint, ctx, gwmux, endpoint, dOpts)
|
||||
mustRegisterGWHandler(repository.RegisterRepositoryServiceHandlerFromEndpoint, ctx, gwmux, endpoint, dOpts)
|
||||
mustRegisterGWHandler(session.RegisterSessionServiceHandlerFromEndpoint, ctx, gwmux, endpoint, dOpts)
|
||||
|
||||
if a.staticAssetsDir != "" {
|
||||
if a.StaticAssetsDir != "" {
|
||||
mux.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
|
||||
acceptHtml := false
|
||||
acceptHTML := false
|
||||
for _, acceptType := range strings.Split(request.Header.Get("Accept"), ",") {
|
||||
if acceptType == "text/html" || acceptType == "html" {
|
||||
acceptHtml = true
|
||||
acceptHTML = true
|
||||
break
|
||||
}
|
||||
}
|
||||
fileRequest := request.URL.Path != "/index.html" && strings.Contains(request.URL.Path, ".")
|
||||
|
||||
// serve index.html for non file requests to support HTML5 History API
|
||||
if acceptHtml && !fileRequest && (request.Method == "GET" || request.Method == "HEAD") {
|
||||
http.ServeFile(writer, request, a.staticAssetsDir+"/index.html")
|
||||
if acceptHTML && !fileRequest && (request.Method == "GET" || request.Method == "HEAD") {
|
||||
http.ServeFile(writer, request, a.StaticAssetsDir+"/index.html")
|
||||
} else {
|
||||
http.ServeFile(writer, request, a.staticAssetsDir+request.URL.Path)
|
||||
http.ServeFile(writer, request, a.StaticAssetsDir+request.URL.Path)
|
||||
}
|
||||
})
|
||||
}
|
||||
return &httpS
|
||||
}
|
||||
|
||||
httpS := &http.Server{
|
||||
Addr: endpoint,
|
||||
Handler: mux,
|
||||
}
|
||||
|
||||
// Start the muxed listeners for our servers
|
||||
log.Infof("argocd %s serving on port %d (namespace: %s)", argocd.GetVersion(), port, a.ns)
|
||||
go func() { _ = grpcS.Serve(grpcL) }()
|
||||
go func() { _ = httpS.Serve(httpL) }()
|
||||
err = m.Serve()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
// newRedirectServer returns an HTTP server which does a 307 redirect to the HTTPS server
|
||||
func newRedirectServer() *http.Server {
|
||||
return &http.Server{
|
||||
Addr: endpoint,
|
||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
target := "https://" + req.Host + req.URL.Path
|
||||
if len(req.URL.RawQuery) > 0 {
|
||||
target += "?" + req.URL.RawQuery
|
||||
}
|
||||
http.Redirect(w, req, target, http.StatusTemporaryRedirect)
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,3 +291,42 @@ func mustRegisterGWHandler(register registerFunc, ctx context.Context, mux *runt
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
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")
|
||||
}
|
||||
|
||||
return ctx, status.Errorf(codes.Unauthenticated, "empty metadata")
|
||||
}
|
||||
|
||||
72
server/session/mocks/SessionServiceClient.go
Normal file
72
server/session/mocks/SessionServiceClient.go
Normal file
@@ -0,0 +1,72 @@
|
||||
// Code generated by mockery v1.0.0
|
||||
package mocks
|
||||
|
||||
import context "context"
|
||||
import grpc "google.golang.org/grpc"
|
||||
import mock "github.com/stretchr/testify/mock"
|
||||
import session "github.com/argoproj/argo-cd/server/session"
|
||||
|
||||
// SessionServiceClient is an autogenerated mock type for the SessionServiceClient type
|
||||
type SessionServiceClient struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Create provides a mock function with given fields: ctx, in, opts
|
||||
func (_m *SessionServiceClient) Create(ctx context.Context, in *session.SessionCreateRequest, opts ...grpc.CallOption) (*session.SessionResponse, error) {
|
||||
_va := make([]interface{}, len(opts))
|
||||
for _i := range opts {
|
||||
_va[_i] = opts[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx, in)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
var r0 *session.SessionResponse
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *session.SessionCreateRequest, ...grpc.CallOption) *session.SessionResponse); ok {
|
||||
r0 = rf(ctx, in, opts...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*session.SessionResponse)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *session.SessionCreateRequest, ...grpc.CallOption) error); ok {
|
||||
r1 = rf(ctx, in, opts...)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Delete provides a mock function with given fields: ctx, in, opts
|
||||
func (_m *SessionServiceClient) Delete(ctx context.Context, in *session.SessionDeleteRequest, opts ...grpc.CallOption) (*session.SessionResponse, error) {
|
||||
_va := make([]interface{}, len(opts))
|
||||
for _i := range opts {
|
||||
_va[_i] = opts[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx, in)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
var r0 *session.SessionResponse
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *session.SessionDeleteRequest, ...grpc.CallOption) *session.SessionResponse); ok {
|
||||
r0 = rf(ctx, in, opts...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*session.SessionResponse)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *session.SessionDeleteRequest, ...grpc.CallOption) error); ok {
|
||||
r1 = rf(ctx, in, opts...)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
57
server/session/mocks/SessionServiceServer.go
Normal file
57
server/session/mocks/SessionServiceServer.go
Normal file
@@ -0,0 +1,57 @@
|
||||
// Code generated by mockery v1.0.0
|
||||
package mocks
|
||||
|
||||
import context "context"
|
||||
import mock "github.com/stretchr/testify/mock"
|
||||
import session "github.com/argoproj/argo-cd/server/session"
|
||||
|
||||
// SessionServiceServer is an autogenerated mock type for the SessionServiceServer type
|
||||
type SessionServiceServer struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Create provides a mock function with given fields: _a0, _a1
|
||||
func (_m *SessionServiceServer) Create(_a0 context.Context, _a1 *session.SessionCreateRequest) (*session.SessionResponse, error) {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
var r0 *session.SessionResponse
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *session.SessionCreateRequest) *session.SessionResponse); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*session.SessionResponse)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *session.SessionCreateRequest) error); ok {
|
||||
r1 = rf(_a0, _a1)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Delete provides a mock function with given fields: _a0, _a1
|
||||
func (_m *SessionServiceServer) Delete(_a0 context.Context, _a1 *session.SessionDeleteRequest) (*session.SessionResponse, error) {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
var r0 *session.SessionResponse
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *session.SessionDeleteRequest) *session.SessionResponse); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*session.SessionResponse)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *session.SessionDeleteRequest) error); ok {
|
||||
r1 = rf(_a0, _a1)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
81
server/session/session.go
Normal file
81
server/session/session.go
Normal file
@@ -0,0 +1,81 @@
|
||||
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"
|
||||
"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
|
||||
}
|
||||
|
||||
// NewServer returns a new instance of the Session service
|
||||
func NewServer(namespace string, kubeclientset kubernetes.Interface, appclientset appclientset.Interface, serversettings config.ArgoCDSettings) *Server {
|
||||
return &Server{
|
||||
ns: namespace,
|
||||
appclientset: appclientset,
|
||||
kubeclientset: kubeclientset,
|
||||
serversettings: serversettings,
|
||||
}
|
||||
}
|
||||
|
||||
// invalidLoginMessage, for security purposes, doesn't say whether the username or password was invalid. This does not mitigate the potential for timing attacks to determine which is which.
|
||||
const (
|
||||
invalidLoginError = "Invalid username or password"
|
||||
blankPasswordError = "Blank passwords are not allowed"
|
||||
)
|
||||
|
||||
// Create an authentication cookie for the client.
|
||||
func (s *Server) Create(ctx context.Context, q *SessionCreateRequest) (*SessionResponse, error) {
|
||||
if q.Password == "" {
|
||||
err := status.Errorf(codes.Unauthenticated, blankPasswordError)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
passwordHash, ok := s.serversettings.LocalUsers[q.Username]
|
||||
if !ok {
|
||||
// Username was not found in local user store.
|
||||
// Ensure we still send password to hashing algorithm for comparison.
|
||||
// This mitigates potential for timing attacks that benefit from short-circuiting,
|
||||
// provided the hashing library/algorithm in use doesn't itself short-circuit.
|
||||
passwordHash = ""
|
||||
}
|
||||
|
||||
valid, _ := password.VerifyPassword(q.Password, passwordHash)
|
||||
if !valid {
|
||||
err := status.Errorf(codes.Unauthenticated, invalidLoginError)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sessionManager := session.MakeSessionManager(s.serversettings.ServerSignature)
|
||||
token, err := sessionManager.Create(q.Username)
|
||||
if err != nil {
|
||||
token = ""
|
||||
}
|
||||
|
||||
return &SessionResponse{token}, err
|
||||
}
|
||||
|
||||
// Delete an authentication cookie from the client. This makes sense only for the Web client.
|
||||
func (s *Server) Delete(ctx context.Context, q *SessionDeleteRequest) (*SessionResponse, error) {
|
||||
return &SessionResponse{""}, nil
|
||||
}
|
||||
|
||||
// AuthFuncOverride overrides the authentication function and let us not require auth to receive auth.
|
||||
// Without this function here, ArgoCDServer.authenticate would be invoked and credentials checked.
|
||||
// Since this service is generally invoked when the user has _no_ credentials, that would create a
|
||||
// chicken-and-egg situation if we didn't place this here to allow traffic to pass through.
|
||||
func (s *Server) AuthFuncOverride(ctx context.Context, fullMethodName string) (context.Context, error) {
|
||||
return ctx, nil
|
||||
}
|
||||
703
server/session/session.pb.go
Normal file
703
server/session/session.pb.go
Normal file
@@ -0,0 +1,703 @@
|
||||
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||
// source: server/session/session.proto
|
||||
|
||||
/*
|
||||
Package session is a generated protocol buffer package.
|
||||
|
||||
Session Service
|
||||
|
||||
Session Service API performs CRUD actions against session resources
|
||||
|
||||
It is generated from these files:
|
||||
server/session/session.proto
|
||||
|
||||
It has these top-level messages:
|
||||
SessionCreateRequest
|
||||
SessionDeleteRequest
|
||||
SessionResponse
|
||||
*/
|
||||
package session
|
||||
|
||||
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 _ "k8s.io/api/core/v1"
|
||||
import _ "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
|
||||
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
|
||||
|
||||
// SessionCreateRequest is for logging in.
|
||||
type SessionCreateRequest struct {
|
||||
Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"`
|
||||
Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
|
||||
}
|
||||
|
||||
func (m *SessionCreateRequest) Reset() { *m = SessionCreateRequest{} }
|
||||
func (m *SessionCreateRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*SessionCreateRequest) ProtoMessage() {}
|
||||
func (*SessionCreateRequest) Descriptor() ([]byte, []int) { return fileDescriptorSession, []int{0} }
|
||||
|
||||
func (m *SessionCreateRequest) GetUsername() string {
|
||||
if m != nil {
|
||||
return m.Username
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *SessionCreateRequest) GetPassword() string {
|
||||
if m != nil {
|
||||
return m.Password
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// SessionDeleteRequest is for logging out.
|
||||
type SessionDeleteRequest struct {
|
||||
}
|
||||
|
||||
func (m *SessionDeleteRequest) Reset() { *m = SessionDeleteRequest{} }
|
||||
func (m *SessionDeleteRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*SessionDeleteRequest) ProtoMessage() {}
|
||||
func (*SessionDeleteRequest) Descriptor() ([]byte, []int) { return fileDescriptorSession, []int{1} }
|
||||
|
||||
// SessionResponse wraps the created token or returns an empty string if deleted.
|
||||
type SessionResponse struct {
|
||||
Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"`
|
||||
}
|
||||
|
||||
func (m *SessionResponse) Reset() { *m = SessionResponse{} }
|
||||
func (m *SessionResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*SessionResponse) ProtoMessage() {}
|
||||
func (*SessionResponse) Descriptor() ([]byte, []int) { return fileDescriptorSession, []int{2} }
|
||||
|
||||
func (m *SessionResponse) GetToken() string {
|
||||
if m != nil {
|
||||
return m.Token
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*SessionCreateRequest)(nil), "session.SessionCreateRequest")
|
||||
proto.RegisterType((*SessionDeleteRequest)(nil), "session.SessionDeleteRequest")
|
||||
proto.RegisterType((*SessionResponse)(nil), "session.SessionResponse")
|
||||
}
|
||||
|
||||
// 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 SessionService service
|
||||
|
||||
type SessionServiceClient interface {
|
||||
// Create a new JWT for authentication.
|
||||
Create(ctx context.Context, in *SessionCreateRequest, opts ...grpc.CallOption) (*SessionResponse, error)
|
||||
// Create a new JWT for authentication.
|
||||
Delete(ctx context.Context, in *SessionDeleteRequest, opts ...grpc.CallOption) (*SessionResponse, error)
|
||||
}
|
||||
|
||||
type sessionServiceClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewSessionServiceClient(cc *grpc.ClientConn) SessionServiceClient {
|
||||
return &sessionServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *sessionServiceClient) Create(ctx context.Context, in *SessionCreateRequest, opts ...grpc.CallOption) (*SessionResponse, error) {
|
||||
out := new(SessionResponse)
|
||||
err := grpc.Invoke(ctx, "/session.SessionService/Create", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *sessionServiceClient) Delete(ctx context.Context, in *SessionDeleteRequest, opts ...grpc.CallOption) (*SessionResponse, error) {
|
||||
out := new(SessionResponse)
|
||||
err := grpc.Invoke(ctx, "/session.SessionService/Delete", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for SessionService service
|
||||
|
||||
type SessionServiceServer interface {
|
||||
// Create a new JWT for authentication.
|
||||
Create(context.Context, *SessionCreateRequest) (*SessionResponse, error)
|
||||
// Create a new JWT for authentication.
|
||||
Delete(context.Context, *SessionDeleteRequest) (*SessionResponse, error)
|
||||
}
|
||||
|
||||
func RegisterSessionServiceServer(s *grpc.Server, srv SessionServiceServer) {
|
||||
s.RegisterService(&_SessionService_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _SessionService_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SessionCreateRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SessionServiceServer).Create(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/session.SessionService/Create",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SessionServiceServer).Create(ctx, req.(*SessionCreateRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SessionService_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SessionDeleteRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SessionServiceServer).Delete(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/session.SessionService/Delete",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SessionServiceServer).Delete(ctx, req.(*SessionDeleteRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _SessionService_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "session.SessionService",
|
||||
HandlerType: (*SessionServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Create",
|
||||
Handler: _SessionService_Create_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Delete",
|
||||
Handler: _SessionService_Delete_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "server/session/session.proto",
|
||||
}
|
||||
|
||||
func (m *SessionCreateRequest) 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 *SessionCreateRequest) MarshalTo(dAtA []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.Username) > 0 {
|
||||
dAtA[i] = 0xa
|
||||
i++
|
||||
i = encodeVarintSession(dAtA, i, uint64(len(m.Username)))
|
||||
i += copy(dAtA[i:], m.Username)
|
||||
}
|
||||
if len(m.Password) > 0 {
|
||||
dAtA[i] = 0x12
|
||||
i++
|
||||
i = encodeVarintSession(dAtA, i, uint64(len(m.Password)))
|
||||
i += copy(dAtA[i:], m.Password)
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (m *SessionDeleteRequest) 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 *SessionDeleteRequest) MarshalTo(dAtA []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (m *SessionResponse) 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 *SessionResponse) MarshalTo(dAtA []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.Token) > 0 {
|
||||
dAtA[i] = 0xa
|
||||
i++
|
||||
i = encodeVarintSession(dAtA, i, uint64(len(m.Token)))
|
||||
i += copy(dAtA[i:], m.Token)
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func encodeVarintSession(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 *SessionCreateRequest) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
l = len(m.Username)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovSession(uint64(l))
|
||||
}
|
||||
l = len(m.Password)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovSession(uint64(l))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *SessionDeleteRequest) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *SessionResponse) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
l = len(m.Token)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovSession(uint64(l))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func sovSession(x uint64) (n int) {
|
||||
for {
|
||||
n++
|
||||
x >>= 7
|
||||
if x == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
func sozSession(x uint64) (n int) {
|
||||
return sovSession(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||
}
|
||||
func (m *SessionCreateRequest) 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 ErrIntOverflowSession
|
||||
}
|
||||
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: SessionCreateRequest: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: SessionCreateRequest: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Username", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowSession
|
||||
}
|
||||
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 ErrInvalidLengthSession
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Username = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Password", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowSession
|
||||
}
|
||||
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 ErrInvalidLengthSession
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Password = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipSession(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthSession
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *SessionDeleteRequest) 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 ErrIntOverflowSession
|
||||
}
|
||||
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: SessionDeleteRequest: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: SessionDeleteRequest: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipSession(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthSession
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *SessionResponse) 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 ErrIntOverflowSession
|
||||
}
|
||||
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: SessionResponse: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: SessionResponse: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Token", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowSession
|
||||
}
|
||||
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 ErrInvalidLengthSession
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Token = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipSession(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthSession
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func skipSession(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, ErrIntOverflowSession
|
||||
}
|
||||
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, ErrIntOverflowSession
|
||||
}
|
||||
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, ErrIntOverflowSession
|
||||
}
|
||||
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, ErrInvalidLengthSession
|
||||
}
|
||||
return iNdEx, nil
|
||||
case 3:
|
||||
for {
|
||||
var innerWire uint64
|
||||
var start int = iNdEx
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowSession
|
||||
}
|
||||
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 := skipSession(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 (
|
||||
ErrInvalidLengthSession = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowSession = fmt.Errorf("proto: integer overflow")
|
||||
)
|
||||
|
||||
func init() { proto.RegisterFile("server/session/session.proto", fileDescriptorSession) }
|
||||
|
||||
var fileDescriptorSession = []byte{
|
||||
// 349 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x92, 0xc1, 0x4a, 0xfb, 0x40,
|
||||
0x10, 0xc6, 0xd9, 0xc2, 0xbf, 0x7f, 0xdd, 0x83, 0xc5, 0x10, 0xb4, 0x84, 0x5a, 0x24, 0x17, 0xa5,
|
||||
0x60, 0x96, 0xea, 0xa5, 0x78, 0x54, 0x2f, 0x5e, 0x3c, 0xb4, 0xb7, 0x82, 0x87, 0x6d, 0x3a, 0x6c,
|
||||
0xd7, 0xa6, 0x3b, 0xeb, 0xee, 0x36, 0xde, 0x7d, 0x05, 0x5f, 0x4a, 0xf0, 0x22, 0xf8, 0x02, 0x52,
|
||||
0x7c, 0x10, 0xe9, 0x26, 0x29, 0xb4, 0x95, 0x9e, 0xb2, 0xdf, 0x7e, 0x93, 0xdf, 0x7c, 0x99, 0x09,
|
||||
0x6d, 0x59, 0x30, 0x39, 0x18, 0x66, 0xc1, 0x5a, 0x89, 0xaa, 0x7a, 0x26, 0xda, 0xa0, 0xc3, 0xe0,
|
||||
0x7f, 0x29, 0xa3, 0x50, 0xa0, 0x40, 0x7f, 0xc7, 0x96, 0xa7, 0xc2, 0x8e, 0x5a, 0x02, 0x51, 0x64,
|
||||
0xc0, 0xb8, 0x96, 0x8c, 0x2b, 0x85, 0x8e, 0x3b, 0x89, 0xca, 0x96, 0x6e, 0x3c, 0xed, 0xd9, 0x44,
|
||||
0xa2, 0x77, 0x53, 0x34, 0xc0, 0xf2, 0x2e, 0x13, 0xa0, 0xc0, 0x70, 0x07, 0xe3, 0xb2, 0xe6, 0x5e,
|
||||
0x48, 0x37, 0x99, 0x8f, 0x92, 0x14, 0x67, 0x8c, 0x1b, 0xdf, 0xe2, 0xc9, 0x1f, 0x2e, 0xd2, 0x31,
|
||||
0xd3, 0x53, 0xb1, 0x7c, 0xd9, 0x32, 0xae, 0x75, 0x26, 0x53, 0x0f, 0x67, 0x79, 0x97, 0x67, 0x7a,
|
||||
0xc2, 0xb7, 0x50, 0xf1, 0x03, 0x0d, 0x07, 0x45, 0xda, 0x5b, 0x03, 0xdc, 0x41, 0x1f, 0x9e, 0xe7,
|
||||
0x60, 0x5d, 0x10, 0xd1, 0xbd, 0xb9, 0x05, 0xa3, 0xf8, 0x0c, 0x9a, 0xe4, 0x94, 0x9c, 0xef, 0xf7,
|
||||
0x57, 0x7a, 0xe9, 0x69, 0x6e, 0xed, 0x0b, 0x9a, 0x71, 0xb3, 0x56, 0x78, 0x95, 0x8e, 0x8f, 0x56,
|
||||
0xbc, 0x3b, 0xc8, 0x60, 0xc5, 0x8b, 0xcf, 0x68, 0xa3, 0xbc, 0xef, 0x83, 0xd5, 0xa8, 0x2c, 0x04,
|
||||
0x21, 0xfd, 0xe7, 0x70, 0x0a, 0xaa, 0xe4, 0x17, 0xe2, 0xf2, 0x83, 0xd0, 0x83, 0xb2, 0x72, 0x00,
|
||||
0x26, 0x97, 0x29, 0x04, 0x8f, 0xb4, 0x5e, 0x84, 0x0b, 0x4e, 0x92, 0x6a, 0xd2, 0x7f, 0x85, 0x8e,
|
||||
0x9a, 0x9b, 0x76, 0xd5, 0x2b, 0x8e, 0x5e, 0xbf, 0x7e, 0xde, 0x6a, 0x61, 0xdc, 0xf0, 0x73, 0xcd,
|
||||
0xbb, 0xd5, 0xc6, 0xae, 0x49, 0x27, 0x18, 0xd2, 0x7a, 0x91, 0x75, 0x1b, 0xbf, 0xf6, 0x0d, 0x3b,
|
||||
0xf0, 0xc7, 0x1e, 0x7f, 0xd8, 0xd9, 0xc4, 0xdf, 0xf4, 0xde, 0x17, 0x6d, 0xf2, 0xb9, 0x68, 0x93,
|
||||
0xef, 0x45, 0x9b, 0x0c, 0x3b, 0xbb, 0xf6, 0xb6, 0xfe, 0x4b, 0x8d, 0xea, 0x7e, 0x3f, 0x57, 0xbf,
|
||||
0x01, 0x00, 0x00, 0xff, 0xff, 0xac, 0xad, 0xb8, 0xb8, 0x6b, 0x02, 0x00, 0x00,
|
||||
}
|
||||
162
server/session/session.pb.gw.go
Normal file
162
server/session/session.pb.gw.go
Normal file
@@ -0,0 +1,162 @@
|
||||
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
|
||||
// source: server/session/session.proto
|
||||
|
||||
/*
|
||||
Package session is a reverse proxy.
|
||||
|
||||
It translates gRPC into RESTful JSON APIs.
|
||||
*/
|
||||
package session
|
||||
|
||||
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_SessionService_Create_0(ctx context.Context, marshaler runtime.Marshaler, client SessionServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq SessionCreateRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := client.Create(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func request_SessionService_Delete_0(ctx context.Context, marshaler runtime.Marshaler, client SessionServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq SessionDeleteRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
msg, err := client.Delete(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
// RegisterSessionServiceHandlerFromEndpoint is same as RegisterSessionServiceHandler but
|
||||
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
|
||||
func RegisterSessionServiceHandlerFromEndpoint(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 RegisterSessionServiceHandler(ctx, mux, conn)
|
||||
}
|
||||
|
||||
// RegisterSessionServiceHandler registers the http handlers for service SessionService to "mux".
|
||||
// The handlers forward requests to the grpc endpoint over "conn".
|
||||
func RegisterSessionServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
|
||||
return RegisterSessionServiceHandlerClient(ctx, mux, NewSessionServiceClient(conn))
|
||||
}
|
||||
|
||||
// RegisterSessionServiceHandler registers the http handlers for service SessionService to "mux".
|
||||
// The handlers forward requests to the grpc endpoint over the given implementation of "SessionServiceClient".
|
||||
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "SessionServiceClient"
|
||||
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
|
||||
// "SessionServiceClient" to call the correct interceptors.
|
||||
func RegisterSessionServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client SessionServiceClient) error {
|
||||
|
||||
mux.Handle("POST", pattern_SessionService_Create_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_SessionService_Create_0(rctx, inboundMarshaler, client, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_SessionService_Create_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("DELETE", pattern_SessionService_Delete_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_SessionService_Delete_0(rctx, inboundMarshaler, client, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_SessionService_Delete_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
pattern_SessionService_Create_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "session"}, ""))
|
||||
|
||||
pattern_SessionService_Delete_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "session"}, ""))
|
||||
)
|
||||
|
||||
var (
|
||||
forward_SessionService_Create_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_SessionService_Delete_0 = runtime.ForwardResponseMessage
|
||||
)
|
||||
46
server/session/session.proto
Normal file
46
server/session/session.proto
Normal file
@@ -0,0 +1,46 @@
|
||||
syntax = "proto3";
|
||||
option go_package = "github.com/argoproj/argo-cd/server/session";
|
||||
|
||||
// Session Service
|
||||
//
|
||||
// Session Service API performs CRUD actions against session resources
|
||||
package session;
|
||||
|
||||
import "gogoproto/gogo.proto";
|
||||
import "google/api/annotations.proto";
|
||||
import "k8s.io/api/core/v1/generated.proto";
|
||||
import "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1/generated.proto";
|
||||
|
||||
|
||||
// SessionCreateRequest is for logging in.
|
||||
message SessionCreateRequest {
|
||||
string username = 1;
|
||||
string password = 2;
|
||||
}
|
||||
|
||||
// SessionDeleteRequest is for logging out.
|
||||
message SessionDeleteRequest {}
|
||||
|
||||
// SessionResponse wraps the created token or returns an empty string if deleted.
|
||||
message SessionResponse {
|
||||
string token = 1;
|
||||
}
|
||||
|
||||
// SessionService
|
||||
service SessionService {
|
||||
|
||||
// Create a new JWT for authentication.
|
||||
rpc Create(SessionCreateRequest) returns (SessionResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/api/v1/session"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// Create a new JWT for authentication.
|
||||
rpc Delete(SessionDeleteRequest) returns (SessionResponse) {
|
||||
option (google.api.http) = {
|
||||
delete: "/api/v1/session"
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -22,3 +22,8 @@ func (s *Server) Version(context.Context, *empty.Empty) (*VersionMessage, error)
|
||||
Platform: vers.Platform,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AuthFuncOverride allows the version to be returned without auth
|
||||
func (s *Server) AuthFuncOverride(ctx context.Context, fullMethodName string) (context.Context, error) {
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||
// source: server/version/version.proto
|
||||
|
||||
/*
|
||||
Package version is a generated protocol buffer package.
|
||||
Package version is a generated protocol buffer package.
|
||||
|
||||
Version Service
|
||||
Version Service
|
||||
|
||||
Version Service API returns the version of the API server.
|
||||
Version Service API returns the version of the API server.
|
||||
|
||||
It is generated from these files:
|
||||
server/version/version.proto
|
||||
It is generated from these files:
|
||||
server/version/version.proto
|
||||
|
||||
It has these top-level messages:
|
||||
VersionMessage
|
||||
It has these top-level messages:
|
||||
VersionMessage
|
||||
*/
|
||||
package version
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import proto "github.com/gogo/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
import _ "google.golang.org/genproto/googleapis/api/annotations"
|
||||
import google_protobuf1 "github.com/golang/protobuf/ptypes/empty"
|
||||
|
||||
import (
|
||||
context "golang.org/x/net/context"
|
||||
grpc "google.golang.org/grpc"
|
||||
)
|
||||
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
|
||||
@@ -36,24 +36,24 @@ var _ = math.Inf
|
||||
// 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.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
// VersionMessage represents version of the ArgoCD API server
|
||||
type VersionMessage struct {
|
||||
Version string `protobuf:"bytes,1,opt,name=Version" json:"Version,omitempty"`
|
||||
BuildDate string `protobuf:"bytes,2,opt,name=BuildDate" json:"BuildDate,omitempty"`
|
||||
GitCommit string `protobuf:"bytes,3,opt,name=GitCommit" json:"GitCommit,omitempty"`
|
||||
GitTag string `protobuf:"bytes,4,opt,name=GitTag" json:"GitTag,omitempty"`
|
||||
GitTreeState string `protobuf:"bytes,5,opt,name=GitTreeState" json:"GitTreeState,omitempty"`
|
||||
GoVersion string `protobuf:"bytes,6,opt,name=GoVersion" json:"GoVersion,omitempty"`
|
||||
Compiler string `protobuf:"bytes,7,opt,name=Compiler" json:"Compiler,omitempty"`
|
||||
Platform string `protobuf:"bytes,8,opt,name=Platform" json:"Platform,omitempty"`
|
||||
Version string `protobuf:"bytes,1,opt,name=Version,proto3" json:"Version,omitempty"`
|
||||
BuildDate string `protobuf:"bytes,2,opt,name=BuildDate,proto3" json:"BuildDate,omitempty"`
|
||||
GitCommit string `protobuf:"bytes,3,opt,name=GitCommit,proto3" json:"GitCommit,omitempty"`
|
||||
GitTag string `protobuf:"bytes,4,opt,name=GitTag,proto3" json:"GitTag,omitempty"`
|
||||
GitTreeState string `protobuf:"bytes,5,opt,name=GitTreeState,proto3" json:"GitTreeState,omitempty"`
|
||||
GoVersion string `protobuf:"bytes,6,opt,name=GoVersion,proto3" json:"GoVersion,omitempty"`
|
||||
Compiler string `protobuf:"bytes,7,opt,name=Compiler,proto3" json:"Compiler,omitempty"`
|
||||
Platform string `protobuf:"bytes,8,opt,name=Platform,proto3" json:"Platform,omitempty"`
|
||||
}
|
||||
|
||||
func (m *VersionMessage) Reset() { *m = VersionMessage{} }
|
||||
func (m *VersionMessage) String() string { return proto.CompactTextString(m) }
|
||||
func (*VersionMessage) ProtoMessage() {}
|
||||
func (*VersionMessage) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||
func (*VersionMessage) Descriptor() ([]byte, []int) { return fileDescriptorVersion, []int{0} }
|
||||
|
||||
func (m *VersionMessage) GetVersion() string {
|
||||
if m != nil {
|
||||
@@ -189,28 +189,542 @@ var _VersionService_serviceDesc = grpc.ServiceDesc{
|
||||
Metadata: "server/version/version.proto",
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("server/version/version.proto", fileDescriptor0) }
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 310 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x54, 0x91, 0xcd, 0x4a, 0x03, 0x31,
|
||||
0x14, 0x85, 0x69, 0xd5, 0xfe, 0x84, 0xd2, 0x45, 0x90, 0x1a, 0xc6, 0x2e, 0xa4, 0x2b, 0x11, 0x9d,
|
||||
0x80, 0xbe, 0x41, 0xab, 0x74, 0x25, 0x14, 0x2b, 0x2e, 0xdc, 0x65, 0xda, 0xdb, 0x18, 0x99, 0xcc,
|
||||
0x1d, 0x32, 0x69, 0xc1, 0xad, 0xaf, 0xe0, 0xa3, 0xf9, 0x0a, 0x3e, 0x84, 0x4b, 0x99, 0xfc, 0x8c,
|
||||
0xcc, 0x6a, 0x72, 0xce, 0x17, 0xce, 0xe4, 0x9e, 0x4b, 0xa6, 0x15, 0x98, 0x03, 0x18, 0x7e, 0x00,
|
||||
0x53, 0x29, 0x2c, 0xe2, 0x37, 0x2d, 0x0d, 0x5a, 0xa4, 0xfd, 0x20, 0x93, 0xa9, 0x44, 0x94, 0x39,
|
||||
0x70, 0x51, 0x2a, 0x2e, 0x8a, 0x02, 0xad, 0xb0, 0x0a, 0x8b, 0xca, 0x5f, 0x4b, 0xce, 0x03, 0x75,
|
||||
0x2a, 0xdb, 0xef, 0x38, 0xe8, 0xd2, 0x7e, 0x78, 0x38, 0xfb, 0xed, 0x90, 0xf1, 0x8b, 0x8f, 0x79,
|
||||
0x84, 0xaa, 0x12, 0x12, 0x28, 0x23, 0xfd, 0xe0, 0xb0, 0xce, 0x45, 0xe7, 0x72, 0xf8, 0x14, 0x25,
|
||||
0x9d, 0x92, 0xe1, 0x7c, 0xaf, 0xf2, 0xed, 0xbd, 0xb0, 0xc0, 0xba, 0x8e, 0xfd, 0x1b, 0x35, 0x5d,
|
||||
0x2a, 0xbb, 0x40, 0xad, 0x95, 0x65, 0x47, 0x9e, 0x36, 0x06, 0x9d, 0x90, 0xde, 0x52, 0xd9, 0x67,
|
||||
0x21, 0xd9, 0xb1, 0x43, 0x41, 0xd1, 0x19, 0x19, 0xd5, 0x27, 0x03, 0xb0, 0xb6, 0x75, 0xec, 0x89,
|
||||
0xa3, 0x2d, 0xcf, 0x25, 0x63, 0x7c, 0x53, 0x2f, 0x24, 0x47, 0x83, 0x26, 0x64, 0xb0, 0x40, 0x5d,
|
||||
0xaa, 0x1c, 0x0c, 0xeb, 0x3b, 0xd8, 0xe8, 0x9a, 0xad, 0x72, 0x61, 0x77, 0x68, 0x34, 0x1b, 0x78,
|
||||
0x16, 0xf5, 0x6d, 0xd6, 0x4c, 0xbe, 0x06, 0x73, 0x50, 0x1b, 0xa0, 0xab, 0x66, 0x72, 0x3a, 0x49,
|
||||
0x7d, 0x6b, 0x69, 0x6c, 0x2d, 0x7d, 0xa8, 0x5b, 0x4b, 0xce, 0xd2, 0xb8, 0x83, 0x76, 0x6b, 0xb3,
|
||||
0xd3, 0xcf, 0xef, 0x9f, 0xaf, 0xee, 0x98, 0x8e, 0xdc, 0x16, 0xc2, 0xa5, 0xf9, 0xf5, 0xeb, 0x95,
|
||||
0x54, 0xf6, 0x6d, 0x9f, 0xa5, 0x1b, 0xd4, 0x5c, 0x18, 0x89, 0xa5, 0xc1, 0x77, 0x77, 0xb8, 0xd9,
|
||||
0x6c, 0x79, 0x7b, 0xbd, 0x59, 0xcf, 0xfd, 0xec, 0xee, 0x2f, 0x00, 0x00, 0xff, 0xff, 0x45, 0x81,
|
||||
0x09, 0xaf, 0xf7, 0x01, 0x00, 0x00,
|
||||
func (m *VersionMessage) 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 *VersionMessage) MarshalTo(dAtA []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.Version) > 0 {
|
||||
dAtA[i] = 0xa
|
||||
i++
|
||||
i = encodeVarintVersion(dAtA, i, uint64(len(m.Version)))
|
||||
i += copy(dAtA[i:], m.Version)
|
||||
}
|
||||
if len(m.BuildDate) > 0 {
|
||||
dAtA[i] = 0x12
|
||||
i++
|
||||
i = encodeVarintVersion(dAtA, i, uint64(len(m.BuildDate)))
|
||||
i += copy(dAtA[i:], m.BuildDate)
|
||||
}
|
||||
if len(m.GitCommit) > 0 {
|
||||
dAtA[i] = 0x1a
|
||||
i++
|
||||
i = encodeVarintVersion(dAtA, i, uint64(len(m.GitCommit)))
|
||||
i += copy(dAtA[i:], m.GitCommit)
|
||||
}
|
||||
if len(m.GitTag) > 0 {
|
||||
dAtA[i] = 0x22
|
||||
i++
|
||||
i = encodeVarintVersion(dAtA, i, uint64(len(m.GitTag)))
|
||||
i += copy(dAtA[i:], m.GitTag)
|
||||
}
|
||||
if len(m.GitTreeState) > 0 {
|
||||
dAtA[i] = 0x2a
|
||||
i++
|
||||
i = encodeVarintVersion(dAtA, i, uint64(len(m.GitTreeState)))
|
||||
i += copy(dAtA[i:], m.GitTreeState)
|
||||
}
|
||||
if len(m.GoVersion) > 0 {
|
||||
dAtA[i] = 0x32
|
||||
i++
|
||||
i = encodeVarintVersion(dAtA, i, uint64(len(m.GoVersion)))
|
||||
i += copy(dAtA[i:], m.GoVersion)
|
||||
}
|
||||
if len(m.Compiler) > 0 {
|
||||
dAtA[i] = 0x3a
|
||||
i++
|
||||
i = encodeVarintVersion(dAtA, i, uint64(len(m.Compiler)))
|
||||
i += copy(dAtA[i:], m.Compiler)
|
||||
}
|
||||
if len(m.Platform) > 0 {
|
||||
dAtA[i] = 0x42
|
||||
i++
|
||||
i = encodeVarintVersion(dAtA, i, uint64(len(m.Platform)))
|
||||
i += copy(dAtA[i:], m.Platform)
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func encodeVarintVersion(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 *VersionMessage) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
l = len(m.Version)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovVersion(uint64(l))
|
||||
}
|
||||
l = len(m.BuildDate)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovVersion(uint64(l))
|
||||
}
|
||||
l = len(m.GitCommit)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovVersion(uint64(l))
|
||||
}
|
||||
l = len(m.GitTag)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovVersion(uint64(l))
|
||||
}
|
||||
l = len(m.GitTreeState)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovVersion(uint64(l))
|
||||
}
|
||||
l = len(m.GoVersion)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovVersion(uint64(l))
|
||||
}
|
||||
l = len(m.Compiler)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovVersion(uint64(l))
|
||||
}
|
||||
l = len(m.Platform)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovVersion(uint64(l))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func sovVersion(x uint64) (n int) {
|
||||
for {
|
||||
n++
|
||||
x >>= 7
|
||||
if x == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
func sozVersion(x uint64) (n int) {
|
||||
return sovVersion(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||
}
|
||||
func (m *VersionMessage) 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 ErrIntOverflowVersion
|
||||
}
|
||||
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: VersionMessage: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: VersionMessage: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowVersion
|
||||
}
|
||||
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 ErrInvalidLengthVersion
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Version = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field BuildDate", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowVersion
|
||||
}
|
||||
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 ErrInvalidLengthVersion
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.BuildDate = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 3:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field GitCommit", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowVersion
|
||||
}
|
||||
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 ErrInvalidLengthVersion
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.GitCommit = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 4:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field GitTag", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowVersion
|
||||
}
|
||||
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 ErrInvalidLengthVersion
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.GitTag = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 5:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field GitTreeState", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowVersion
|
||||
}
|
||||
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 ErrInvalidLengthVersion
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.GitTreeState = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 6:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field GoVersion", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowVersion
|
||||
}
|
||||
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 ErrInvalidLengthVersion
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.GoVersion = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 7:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Compiler", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowVersion
|
||||
}
|
||||
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 ErrInvalidLengthVersion
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Compiler = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 8:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Platform", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowVersion
|
||||
}
|
||||
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 ErrInvalidLengthVersion
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Platform = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipVersion(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthVersion
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func skipVersion(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, ErrIntOverflowVersion
|
||||
}
|
||||
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, ErrIntOverflowVersion
|
||||
}
|
||||
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, ErrIntOverflowVersion
|
||||
}
|
||||
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, ErrInvalidLengthVersion
|
||||
}
|
||||
return iNdEx, nil
|
||||
case 3:
|
||||
for {
|
||||
var innerWire uint64
|
||||
var start int = iNdEx
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowVersion
|
||||
}
|
||||
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 := skipVersion(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 (
|
||||
ErrInvalidLengthVersion = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowVersion = fmt.Errorf("proto: integer overflow")
|
||||
)
|
||||
|
||||
func init() { proto.RegisterFile("server/version/version.proto", fileDescriptorVersion) }
|
||||
|
||||
var fileDescriptorVersion = []byte{
|
||||
// 327 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x54, 0x91, 0xcd, 0x4a, 0xfb, 0x40,
|
||||
0x14, 0xc5, 0x49, 0xff, 0x7f, 0xfb, 0x31, 0x94, 0x2e, 0x06, 0xa9, 0x43, 0x2c, 0x45, 0xba, 0x12,
|
||||
0xc1, 0x04, 0x74, 0xe3, 0xba, 0x55, 0xba, 0x12, 0x8a, 0x15, 0x17, 0xee, 0x26, 0xed, 0x6d, 0x1c,
|
||||
0xc9, 0xe4, 0x86, 0xc9, 0xb4, 0xe0, 0xd6, 0x57, 0xf0, 0xa5, 0x5c, 0x0a, 0xbe, 0x80, 0x14, 0x1f,
|
||||
0xc2, 0xa5, 0x64, 0x3e, 0x22, 0x59, 0x65, 0xce, 0xf9, 0x0d, 0x27, 0x73, 0xcf, 0x25, 0xa3, 0x12,
|
||||
0xd4, 0x0e, 0x54, 0xbc, 0x03, 0x55, 0x0a, 0xcc, 0xfd, 0x37, 0x2a, 0x14, 0x6a, 0xa4, 0x1d, 0x27,
|
||||
0xc3, 0x51, 0x8a, 0x98, 0x66, 0x10, 0xf3, 0x42, 0xc4, 0x3c, 0xcf, 0x51, 0x73, 0x2d, 0x30, 0x2f,
|
||||
0xed, 0xb5, 0xf0, 0xd8, 0x51, 0xa3, 0x92, 0xed, 0x26, 0x06, 0x59, 0xe8, 0x17, 0x0b, 0x27, 0x3f,
|
||||
0x01, 0x19, 0x3c, 0xd8, 0x98, 0x5b, 0x28, 0x4b, 0x9e, 0x02, 0x65, 0xa4, 0xe3, 0x1c, 0x16, 0x9c,
|
||||
0x04, 0xa7, 0xbd, 0x3b, 0x2f, 0xe9, 0x88, 0xf4, 0xa6, 0x5b, 0x91, 0xad, 0xaf, 0xb9, 0x06, 0xd6,
|
||||
0x32, 0xec, 0xcf, 0xa8, 0xe8, 0x5c, 0xe8, 0x19, 0x4a, 0x29, 0x34, 0xfb, 0x67, 0x69, 0x6d, 0xd0,
|
||||
0x21, 0x69, 0xcf, 0x85, 0xbe, 0xe7, 0x29, 0xfb, 0x6f, 0x90, 0x53, 0x74, 0x42, 0xfa, 0xd5, 0x49,
|
||||
0x01, 0x2c, 0x75, 0x15, 0x7b, 0x60, 0x68, 0xc3, 0x33, 0xc9, 0xe8, 0xdf, 0xd4, 0x76, 0xc9, 0xde,
|
||||
0xa0, 0x21, 0xe9, 0xce, 0x50, 0x16, 0x22, 0x03, 0xc5, 0x3a, 0x06, 0xd6, 0xba, 0x62, 0x8b, 0x8c,
|
||||
0xeb, 0x0d, 0x2a, 0xc9, 0xba, 0x96, 0x79, 0x7d, 0x91, 0xd4, 0x93, 0x2f, 0x41, 0xed, 0xc4, 0x0a,
|
||||
0xe8, 0xa2, 0x9e, 0x9c, 0x0e, 0x23, 0xdb, 0x5a, 0xe4, 0x5b, 0x8b, 0x6e, 0xaa, 0xd6, 0xc2, 0xa3,
|
||||
0xc8, 0xef, 0xa0, 0xd9, 0xda, 0xe4, 0xf0, 0xf5, 0xf3, 0xfb, 0xad, 0x35, 0xa0, 0x7d, 0xb3, 0x05,
|
||||
0x77, 0x69, 0x7a, 0xf5, 0xbe, 0x1f, 0x07, 0x1f, 0xfb, 0x71, 0xf0, 0xb5, 0x1f, 0x07, 0x8f, 0x67,
|
||||
0xa9, 0xd0, 0x4f, 0xdb, 0x24, 0x5a, 0xa1, 0x8c, 0xb9, 0x4a, 0xb1, 0x50, 0xf8, 0x6c, 0x0e, 0xe7,
|
||||
0xab, 0x75, 0xdc, 0x5c, 0x75, 0xd2, 0x36, 0x3f, 0xbe, 0xfc, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x19,
|
||||
0x01, 0x2c, 0x30, 0x03, 0x02, 0x00, 0x00,
|
||||
}
|
||||
|
||||
@@ -151,10 +151,12 @@ func (f *Fixture) CreateApp(t *testing.T, application *v1alpha1.Application) *v1
|
||||
// CreateController creates new controller instance
|
||||
func (f *Fixture) CreateController() *controller.ApplicationController {
|
||||
return controller.NewApplicationController(
|
||||
f.Namespace,
|
||||
f.KubeClient,
|
||||
f.AppClient,
|
||||
reposerver.NewRepositoryServerClientset(f.repoServerListener.Addr().String()),
|
||||
f.ApiRepoService,
|
||||
cluster.NewServer(f.Namespace, f.KubeClient, f.AppClient),
|
||||
f.AppComparator,
|
||||
time.Second,
|
||||
&controller.ApplicationControllerConfig{Namespace: f.Namespace, InstanceID: f.InstanceID})
|
||||
@@ -203,3 +205,7 @@ func (c *FakeGitClient) Reset(repoPath string) error {
|
||||
// do nothing
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *FakeGitClient) CommitSHA(repoPath string) (string, error) {
|
||||
return "abcdef123456890", nil
|
||||
}
|
||||
|
||||
@@ -3,11 +3,16 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
argocd "github.com/argoproj/argo-cd"
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
@@ -49,3 +54,25 @@ func AddKubectlFlagsToCmd(cmd *cobra.Command) clientcmd.ClientConfig {
|
||||
clientcmd.BindOverrideFlags(&overrides, cmd.PersistentFlags(), kflags)
|
||||
return clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, &overrides, os.Stdin)
|
||||
}
|
||||
|
||||
// PromptCredentials is a helper to prompt the user for a username and password
|
||||
func PromptCredentials(username, password string) (string, string) {
|
||||
for username == "" {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
fmt.Print("Username: ")
|
||||
usernameRaw, err := reader.ReadString('\n')
|
||||
errors.CheckError(err)
|
||||
username = strings.TrimSpace(usernameRaw)
|
||||
}
|
||||
for password == "" {
|
||||
fmt.Print("Password: ")
|
||||
passwordRaw, err := terminal.ReadPassword(syscall.Stdin)
|
||||
errors.CheckError(err)
|
||||
password = string(passwordRaw)
|
||||
if password == "" {
|
||||
fmt.Print("\n")
|
||||
}
|
||||
}
|
||||
fmt.Print("\n")
|
||||
return username, password
|
||||
}
|
||||
|
||||
74
util/cli/config.go
Normal file
74
util/cli/config.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
)
|
||||
|
||||
// unmarshalObject tries to convert a YAML or JSON byte array into the provided type.
|
||||
func unmarshalObject(data []byte, obj interface{}) error {
|
||||
// first, try unmarshaling as JSON
|
||||
// Based on technique from Kubectl, which supports both YAML and JSON:
|
||||
// https://mlafeldt.github.io/blog/teaching-go-programs-to-love-json-and-yaml/
|
||||
// http://ghodss.com/2014/the-right-way-to-handle-yaml-in-golang/
|
||||
// Short version: JSON unmarshaling won't zero out null fields; YAML unmarshaling will.
|
||||
// This may have unintended effects or hard-to-catch issues when populating our application object.
|
||||
jsonData, err := yaml.YAMLToJSON(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(jsonData, &obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// MarshalLocalYAMLFile writes JSON or YAML to a file on disk.
|
||||
// The caller is responsible for checking error return values.
|
||||
func MarshalLocalYAMLFile(path string, obj interface{}) error {
|
||||
yamlData, err := yaml.Marshal(obj)
|
||||
if err == nil {
|
||||
err = ioutil.WriteFile(path, yamlData, 0600)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// UnmarshalLocalFile retrieves JSON or YAML from a file on disk.
|
||||
// The caller is responsible for checking error return values.
|
||||
func UnmarshalLocalFile(path string, obj interface{}) error {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err == nil {
|
||||
err = unmarshalObject(data, obj)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// UnmarshalRemoteFile retrieves JSON or YAML through a GET request.
|
||||
// The caller is responsible for checking error return values.
|
||||
func UnmarshalRemoteFile(url string, obj interface{}) error {
|
||||
data, err := readRemoteFile(url)
|
||||
if err == nil {
|
||||
err = unmarshalObject(data, obj)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// ReadRemoteFile issues a GET request to retrieve the contents of the specified URL as a byte array.
|
||||
// The caller is responsible for checking error return values.
|
||||
func readRemoteFile(url string) ([]byte, error) {
|
||||
var data []byte
|
||||
resp, err := http.Get(url)
|
||||
if err == nil {
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
data, err = ioutil.ReadAll(resp.Body)
|
||||
}
|
||||
return data, err
|
||||
}
|
||||
94
util/cli/config_test.go
Normal file
94
util/cli/config_test.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUnmarshalLocalFile(t *testing.T) {
|
||||
const (
|
||||
field1 = "Hello, world!"
|
||||
field2 = 42
|
||||
)
|
||||
sentinel := fmt.Sprintf("---\nfield1: %q\nfield2: %d", field1, field2)
|
||||
|
||||
file, err := ioutil.TempFile(os.TempDir(), "")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer func() {
|
||||
_ = os.Remove(file.Name())
|
||||
}()
|
||||
|
||||
_, _ = file.WriteString(sentinel)
|
||||
_ = file.Sync()
|
||||
|
||||
var testStruct struct {
|
||||
Field1 string
|
||||
Field2 int
|
||||
}
|
||||
err = UnmarshalLocalFile(file.Name(), &testStruct)
|
||||
if err != nil {
|
||||
t.Errorf("Could not unmarshal test data: %s", err)
|
||||
}
|
||||
|
||||
if testStruct.Field1 != field1 || testStruct.Field2 != field2 {
|
||||
t.Errorf("Test data did not match! Expected {%s %d} but got: %v", field1, field2, testStruct)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalRemoteFile(t *testing.T) {
|
||||
const (
|
||||
field1 = "Hello, world!"
|
||||
field2 = 42
|
||||
)
|
||||
sentinel := fmt.Sprintf("---\nfield1: %q\nfield2: %d", field1, field2)
|
||||
|
||||
serve := func(c chan<- string) {
|
||||
// listen on first available dynamic (unprivileged) port
|
||||
listener, err := net.Listen("tcp", ":0")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// send back the address so that it can be used
|
||||
c <- listener.Addr().String()
|
||||
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
// return the sentinel text at root URL
|
||||
fmt.Fprint(w, sentinel)
|
||||
})
|
||||
|
||||
panic(http.Serve(listener, nil))
|
||||
}
|
||||
|
||||
c := make(chan string, 1)
|
||||
|
||||
// run a local webserver to test data retrieval
|
||||
go serve(c)
|
||||
|
||||
address := <-c
|
||||
t.Logf("Listening at address: %s", address)
|
||||
|
||||
data, err := readRemoteFile("http://" + address)
|
||||
if string(data) != sentinel {
|
||||
t.Errorf("Test data did not match (err = %v)! Expected %q and received %q", err, sentinel, string(data))
|
||||
}
|
||||
|
||||
var testStruct struct {
|
||||
Field1 string
|
||||
Field2 int
|
||||
}
|
||||
err = UnmarshalRemoteFile("http://"+address, &testStruct)
|
||||
if err != nil {
|
||||
t.Errorf("Could not unmarshal test data: %s", err)
|
||||
}
|
||||
|
||||
if testStruct.Field1 != field1 || testStruct.Field2 != field2 {
|
||||
t.Errorf("Test data did not match! Expected {%s %d} but got: %v", field1, field2, testStruct)
|
||||
}
|
||||
}
|
||||
150
util/config/configmanager.go
Normal file
150
util/config/configmanager.go
Normal file
@@ -0,0 +1,150 @@
|
||||
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,
|
||||
}
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"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
|
||||
}
|
||||
|
||||
type configMapData struct {
|
||||
rootCredentialsSecretName string
|
||||
}
|
||||
|
||||
const (
|
||||
// defaultConfigMapName default name of config map with argo-cd settings
|
||||
defaultConfigMapName = "argo-cd-cm"
|
||||
|
||||
// defaultRootCredentialsSecretName contains default name of secret with root user credentials
|
||||
defaultRootCredentialsSecretName = "argo-cd-root-credentials"
|
||||
|
||||
// configManagerRootUsernameKey designates the root username inside a Kubernetes secret.
|
||||
configManagerRootUsernameKey = "root.username"
|
||||
|
||||
// configManagerRootPasswordKey designates the root password inside a Kubernetes secret.
|
||||
configManagerRootPasswordKey = "root.password"
|
||||
)
|
||||
|
||||
// ConfigManager holds config info for a new manager with which to access Kubernetes ConfigMaps.
|
||||
type ConfigManager struct {
|
||||
clientset kubernetes.Interface
|
||||
namespace string
|
||||
configMapName string
|
||||
}
|
||||
|
||||
// GetSettings retrieves settings from the ConfigManager.
|
||||
func (mgr *ConfigManager) GetSettings() (ArgoCDSettings, error) {
|
||||
settings := ArgoCDSettings{}
|
||||
settings.LocalUsers = make(map[string]string)
|
||||
data, err := mgr.getConfigMapData()
|
||||
if err != nil {
|
||||
return settings, err
|
||||
}
|
||||
|
||||
// Try to retrieve the secret
|
||||
rootCredentials, err := mgr.readSecret(data.rootCredentialsSecretName)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
return settings, nil
|
||||
} else {
|
||||
return settings, err
|
||||
}
|
||||
}
|
||||
// Retrieve credential info from the secret
|
||||
rootUsername, okUsername := rootCredentials.Data[configManagerRootUsernameKey]
|
||||
rootPassword, okPassword := rootCredentials.Data[configManagerRootPasswordKey]
|
||||
|
||||
if okUsername && okPassword {
|
||||
// Store credential info inside LocalUsers
|
||||
settings.LocalUsers[string(rootUsername)] = string(rootPassword)
|
||||
}
|
||||
return settings, nil
|
||||
}
|
||||
|
||||
func (mgr *ConfigManager) SetRootUserCredentials(username string, password string) error {
|
||||
data, err := mgr.getConfigMapData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Don't commit plaintext passwords
|
||||
passwordHash, err := HashPassword(password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
credentials := map[string]string{
|
||||
configManagerRootUsernameKey: username,
|
||||
configManagerRootPasswordKey: passwordHash,
|
||||
}
|
||||
|
||||
// See if we've already written this secret
|
||||
secret, err := mgr.clientset.CoreV1().Secrets(mgr.namespace).Get(data.rootCredentialsSecretName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
newSecret := &apiv1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: data.rootCredentialsSecretName,
|
||||
},
|
||||
}
|
||||
newSecret.StringData = credentials
|
||||
_, err = mgr.clientset.CoreV1().Secrets(mgr.namespace).Create(newSecret)
|
||||
|
||||
} else {
|
||||
secret.StringData = credentials
|
||||
_, err = mgr.clientset.CoreV1().Secrets(mgr.namespace).Update(secret)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// NewConfigManager generates a new ConfigManager pointer and returns it
|
||||
func NewConfigManager(clientset kubernetes.Interface, namespace, configMapName string) (mgr *ConfigManager) {
|
||||
if configMapName == "" {
|
||||
configMapName = defaultConfigMapName
|
||||
}
|
||||
mgr = &ConfigManager{
|
||||
clientset: clientset,
|
||||
namespace: namespace,
|
||||
configMapName: configMapName,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (mgr *ConfigManager) getConfigMapData() (configMapData, error) {
|
||||
data := configMapData{}
|
||||
configMap, err := mgr.readConfigMap(mgr.configMapName)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
data.rootCredentialsSecretName = defaultRootCredentialsSecretName
|
||||
return data, nil
|
||||
} else {
|
||||
return data, err
|
||||
}
|
||||
}
|
||||
rootCredentialsSecretName, ok := configMap.Data[defaultRootCredentialsSecretName]
|
||||
if !ok {
|
||||
rootCredentialsSecretName = defaultRootCredentialsSecretName
|
||||
}
|
||||
data.rootCredentialsSecretName = rootCredentialsSecretName
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// ReadConfigMap retrieves a config map from Kubernetes.
|
||||
func (mgr *ConfigManager) readConfigMap(name string) (configMap *apiv1.ConfigMap, err error) {
|
||||
configMap, err = mgr.clientset.CoreV1().ConfigMaps(mgr.namespace).Get(name, metav1.GetOptions{})
|
||||
return
|
||||
}
|
||||
|
||||
// ReadSecret retrieves a secret from Kubernetes.
|
||||
func (mgr *ConfigManager) readSecret(name string) (secret *apiv1.Secret, err error) {
|
||||
secret, err = mgr.clientset.CoreV1().Secrets(mgr.namespace).Get(name, metav1.GetOptions{})
|
||||
return
|
||||
}
|
||||
@@ -26,7 +26,7 @@ func Diff(left, right *unstructured.Unstructured) *DiffResult {
|
||||
leftObj = left.Object
|
||||
}
|
||||
if right != nil {
|
||||
rightObj = removeMapFields(leftObj, right.Object)
|
||||
rightObj = RemoveMapFields(leftObj, right.Object)
|
||||
}
|
||||
gjDiff := gojsondiff.New().CompareObjects(leftObj, rightObj)
|
||||
dr := DiffResult{
|
||||
@@ -75,7 +75,7 @@ func (d *DiffResult) ASCIIFormat(left *unstructured.Unstructured, formatOpts for
|
||||
func removeFields(config, live interface{}) interface{} {
|
||||
switch c := config.(type) {
|
||||
case map[string]interface{}:
|
||||
return removeMapFields(c, live.(map[string]interface{}))
|
||||
return RemoveMapFields(c, live.(map[string]interface{}))
|
||||
case []interface{}:
|
||||
return removeListFields(c, live.([]interface{}))
|
||||
default:
|
||||
@@ -83,7 +83,7 @@ func removeFields(config, live interface{}) interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
func removeMapFields(config, live map[string]interface{}) map[string]interface{} {
|
||||
func RemoveMapFields(config, live map[string]interface{}) map[string]interface{} {
|
||||
result := map[string]interface{}{}
|
||||
for k, v1 := range config {
|
||||
v2, ok := live[k]
|
||||
|
||||
@@ -3,8 +3,12 @@ package git
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
|
||||
"strings"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
@@ -13,16 +17,66 @@ import (
|
||||
type Client interface {
|
||||
CloneOrFetch(url string, username string, password string, sshPrivateKey string, repoPath string) error
|
||||
Checkout(repoPath string, sha string) (string, error)
|
||||
CommitSHA(repoPath string) (string, error)
|
||||
Reset(repoPath string) error
|
||||
}
|
||||
|
||||
// NativeGitClient implements Client interface using git CLI
|
||||
type NativeGitClient struct {
|
||||
rootDirectoryPath string
|
||||
type NativeGitClient struct{}
|
||||
|
||||
// Init initializes a local git repository and sets the remote origin
|
||||
func (m *NativeGitClient) Init(repo string, repoPath string) error {
|
||||
log.Infof("Initializing %s to %s", repo, repoPath)
|
||||
err := os.MkdirAll(repoPath, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := runCmd(repoPath, "git", "init"); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := runCmd(repoPath, "git", "remote", "add", "origin", repo); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 != "" {
|
||||
gitCredentialsFile := path.Join(repoPath, ".git", "credentials")
|
||||
repoURL, err := url.ParseRequestURI(repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
repoURL.User = url.UserPassword(username, password)
|
||||
cmdURL := repoURL.String()
|
||||
err = ioutil.WriteFile(gitCredentialsFile, []byte(cmdURL), 0600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to set git credentials: %v", err)
|
||||
}
|
||||
_, err = runCmd(repoPath, "git", "config", "--local", "credential.helper", fmt.Sprintf("store --file=%s", gitCredentialsFile))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if sshPrivateKey != "" {
|
||||
sshPrivateKeyFile := path.Join(repoPath, ".git", "ssh-private-key")
|
||||
err := ioutil.WriteFile(sshPrivateKeyFile, []byte(sshPrivateKey), 0600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to set git credentials: %v", err)
|
||||
}
|
||||
sshCmd := fmt.Sprintf("ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i %s", sshPrivateKeyFile)
|
||||
_, err = runCmd(repoPath, "git", "config", "--local", "core.sshCommand", sshCmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CloneOrFetch either clone or fetch repository into specified directory path.
|
||||
func (m *NativeGitClient) CloneOrFetch(repo string, username string, password string, sshPrivateKey string, repoPath string) error {
|
||||
log.Debugf("Cloning/Fetching repo %s at %s", repo, repoPath)
|
||||
var needClone bool
|
||||
if _, err := os.Stat(repoPath); os.IsNotExist(err) {
|
||||
needClone = true
|
||||
@@ -32,88 +86,104 @@ func (m *NativeGitClient) CloneOrFetch(repo string, username string, password st
|
||||
_, err = cmd.Output()
|
||||
needClone = err != nil
|
||||
}
|
||||
|
||||
repoURL, env, err := GetGitCommandEnvAndURL(repo, username, password, sshPrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if needClone {
|
||||
_, err := exec.Command("rm", "-rf", repoPath).Output()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to clean repo cache at %s: %v", repoPath, err)
|
||||
}
|
||||
|
||||
log.Infof("Cloning %s to %s", repo, repoPath)
|
||||
cmd := exec.Command("git", "clone", repoURL, repoPath)
|
||||
cmd.Env = env
|
||||
_, err = cmd.Output()
|
||||
err = m.Init(repo, repoPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to clone repository %s: %v", repo, err)
|
||||
}
|
||||
} else {
|
||||
log.Infof("Fetching %s", repo)
|
||||
// Fetch remote changes and delete all local branches
|
||||
cmd := exec.Command("sh", "-c", "git fetch --all && git checkout --detach HEAD")
|
||||
cmd.Env = env
|
||||
cmd.Dir = repoPath
|
||||
_, err := cmd.Output()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to fetch repo %s: %v", repoPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
cmd = exec.Command("sh", "-c", "for i in $(git branch --merged | grep -v \\*); do git branch -D $i; done")
|
||||
cmd.Dir = repoPath
|
||||
_, err = cmd.Output()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to delete local branches for %s: %v", repoPath, err)
|
||||
err := m.SetCredentials(repo, username, password, sshPrivateKey, repoPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Fetch remote changes
|
||||
if _, err = runCmd(repoPath, "git", "fetch", "origin"); err != nil {
|
||||
return err
|
||||
}
|
||||
// git fetch does not update the HEAD reference. The following command will update the local
|
||||
// knowledge of what remote considers the “default branch”
|
||||
// See: https://stackoverflow.com/questions/8839958/how-does-origin-head-get-set
|
||||
if _, err := runCmd(repoPath, "git", "remote", "set-head", "origin", "-a"); err != nil {
|
||||
return err
|
||||
}
|
||||
// Delete all local branches (we must first detach so we are not checked out a branch we are about to delete)
|
||||
if _, err = runCmd(repoPath, "git", "checkout", "--detach", "origin/HEAD"); err != nil {
|
||||
return err
|
||||
}
|
||||
branchesOut, err := runCmd(repoPath, "git", "for-each-ref", "--format=%(refname:short)", "refs/heads/")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
branchesOut = strings.TrimSpace(branchesOut)
|
||||
if branchesOut != "" {
|
||||
branches := strings.Split(branchesOut, "\n")
|
||||
args := []string{"branch", "-D"}
|
||||
args = append(args, branches...)
|
||||
if _, err = runCmd(repoPath, "git", args...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reset resets local changes
|
||||
// Reset resets local changes in a repository
|
||||
func (m *NativeGitClient) Reset(repoPath string) error {
|
||||
cmd := exec.Command("sh", "-c", "git reset --hard HEAD && git clean -f")
|
||||
cmd.Dir = repoPath
|
||||
_, err := cmd.Output()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to reset repository %s: %v", repoPath, err)
|
||||
if _, err := runCmd(repoPath, "git", "reset", "--hard", "origin/HEAD"); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := runCmd(repoPath, "git", "clean", "-f"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Checkout checkout specified git sha
|
||||
func (m *NativeGitClient) Checkout(repoPath string, sha string) (string, error) {
|
||||
if sha == "" {
|
||||
sha = "origin/HEAD"
|
||||
func (m *NativeGitClient) Checkout(repoPath string, revision string) (string, error) {
|
||||
if revision == "" || revision == "HEAD" {
|
||||
revision = "origin/HEAD"
|
||||
}
|
||||
checkoutCmd := exec.Command("git", "checkout", sha)
|
||||
checkoutCmd.Dir = repoPath
|
||||
_, err := checkoutCmd.Output()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to checkout revision %s: %v", sha, err)
|
||||
if _, err := runCmd(repoPath, "git", "checkout", revision); err != nil {
|
||||
return "", err
|
||||
}
|
||||
revisionCmd := exec.Command("git", "rev-parse", "HEAD")
|
||||
revisionCmd.Dir = repoPath
|
||||
output, err := revisionCmd.Output()
|
||||
return m.CommitSHA(repoPath)
|
||||
}
|
||||
|
||||
// CommitSHA returns current commit sha from `git rev-parse HEAD`
|
||||
func (m *NativeGitClient) CommitSHA(repoPath string) (string, error) {
|
||||
out, err := runCmd(repoPath, "git", "rev-parse", "HEAD")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(output), nil
|
||||
|
||||
return strings.TrimSpace(out), nil
|
||||
}
|
||||
|
||||
// NewNativeGitClient creates new instance of NativeGitClient
|
||||
func NewNativeGitClient() (Client, error) {
|
||||
rootDirPath, err := ioutil.TempDir("", "argo-git")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &NativeGitClient{
|
||||
rootDirectoryPath: rootDirPath,
|
||||
}, nil
|
||||
return &NativeGitClient{}, nil
|
||||
}
|
||||
|
||||
// runCmd is a convenience function to run a command in a given directory and return its output
|
||||
func runCmd(cwd string, command string, args ...string) (string, error) {
|
||||
cmd := exec.Command(command, args...)
|
||||
log.Debug(strings.Join(cmd.Args, " "))
|
||||
cmd.Dir = cwd
|
||||
out, err := cmd.Output()
|
||||
if len(out) > 0 {
|
||||
log.Debug(string(out))
|
||||
}
|
||||
if err != nil {
|
||||
exErr, ok := err.(*exec.ExitError)
|
||||
if ok {
|
||||
errOutput := strings.Split(string(exErr.Stderr), "\n")[0]
|
||||
log.Debug(errOutput)
|
||||
return string(out), fmt.Errorf("'%s' failed: %v", strings.Join(cmd.Args, " "), errOutput)
|
||||
}
|
||||
return string(out), fmt.Errorf("'%s' failed: %v", strings.Join(cmd.Args, " "), err)
|
||||
}
|
||||
return string(out), nil
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ func TestRepo(repo, username, password string, sshPrivateKey string) error {
|
||||
exErr := err.(*exec.ExitError)
|
||||
errOutput := strings.Split(string(exErr.Stderr), "\n")[0]
|
||||
errOutput = redactPassword(errOutput, password)
|
||||
return fmt.Errorf("failed to test %s: %s", repo, errOutput)
|
||||
return fmt.Errorf("%s: %s", repo, errOutput)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -9,17 +9,24 @@ type Client struct {
|
||||
}
|
||||
|
||||
// Checkout provides a mock function with given fields: repoPath, sha
|
||||
func (_m *Client) Checkout(repoPath string, sha string) error {
|
||||
func (_m *Client) Checkout(repoPath string, sha string) (string, error) {
|
||||
ret := _m.Called(repoPath, sha)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string, string) error); ok {
|
||||
var r0 string
|
||||
if rf, ok := ret.Get(0).(func(string, string) string); ok {
|
||||
r0 = rf(repoPath, sha)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
|
||||
return r0
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string, string) error); ok {
|
||||
r1 = rf(repoPath, sha)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// CloneOrFetch provides a mock function with given fields: url, username, password, sshPrivateKey, repoPath
|
||||
@@ -35,3 +42,38 @@ func (_m *Client) CloneOrFetch(url string, username string, password string, ssh
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// CommitSHA provides a mock function with given fields: repoPath
|
||||
func (_m *Client) CommitSHA(repoPath string) (string, error) {
|
||||
ret := _m.Called(repoPath)
|
||||
|
||||
var r0 string
|
||||
if rf, ok := ret.Get(0).(func(string) string); ok {
|
||||
r0 = rf(repoPath)
|
||||
} else {
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||
r1 = rf(repoPath)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Reset provides a mock function with given fields: repoPath
|
||||
func (_m *Client) Reset(repoPath string) error {
|
||||
ret := _m.Called(repoPath)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string) error); ok {
|
||||
r0 = rf(repoPath)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
75
util/grpc/errors.go
Normal file
75
util/grpc/errors.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
apierr "k8s.io/apimachinery/pkg/api/errors"
|
||||
)
|
||||
|
||||
func kubeErrToGRPC(err error) error {
|
||||
/*
|
||||
Unmapped source Kubernetes API errors as of 2018-04-16:
|
||||
* IsConflict => 409
|
||||
* IsGone => 410
|
||||
* IsResourceExpired => 410
|
||||
* IsServerTimeout => 500
|
||||
* IsTooManyRequests => 429
|
||||
* IsUnexpectedServerError => should probably be a panic
|
||||
* IsUnexpectedObjectError => should probably be a panic
|
||||
|
||||
Unmapped target gRPC codes as of 2018-04-16:
|
||||
* Canceled Code = 1
|
||||
* Unknown Code = 2
|
||||
* ResourceExhausted Code = 8
|
||||
* Aborted Code = 10
|
||||
* OutOfRange Code = 11
|
||||
* DataLoss Code = 15
|
||||
*/
|
||||
|
||||
rewrapError := func(err error, code codes.Code) error {
|
||||
return status.Errorf(code, err.Error())
|
||||
}
|
||||
|
||||
switch {
|
||||
case apierr.IsNotFound(err):
|
||||
err = rewrapError(err, codes.NotFound)
|
||||
case apierr.IsAlreadyExists(err):
|
||||
err = rewrapError(err, codes.AlreadyExists)
|
||||
case apierr.IsInvalid(err):
|
||||
err = rewrapError(err, codes.InvalidArgument)
|
||||
case apierr.IsMethodNotSupported(err):
|
||||
err = rewrapError(err, codes.Unimplemented)
|
||||
case apierr.IsServiceUnavailable(err):
|
||||
err = rewrapError(err, codes.Unavailable)
|
||||
case apierr.IsBadRequest(err):
|
||||
err = rewrapError(err, codes.FailedPrecondition)
|
||||
case apierr.IsUnauthorized(err):
|
||||
err = rewrapError(err, codes.Unauthenticated)
|
||||
case apierr.IsForbidden(err):
|
||||
err = rewrapError(err, codes.PermissionDenied)
|
||||
case apierr.IsTimeout(err):
|
||||
err = rewrapError(err, codes.DeadlineExceeded)
|
||||
case apierr.IsInternalError(err):
|
||||
err = rewrapError(err, codes.Internal)
|
||||
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// ErrorCodeUnaryServerInterceptor replaces Kubernetes errors with relevant gRPC equivalents, if any.
|
||||
func ErrorCodeUnaryServerInterceptor() grpc.UnaryServerInterceptor {
|
||||
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
|
||||
resp, err = handler(ctx, req)
|
||||
return resp, kubeErrToGRPC(err)
|
||||
}
|
||||
}
|
||||
|
||||
// ErrorCodeStreamServerInterceptor replaces Kubernetes errors with relevant gRPC equivalents, if any.
|
||||
func ErrorCodeStreamServerInterceptor() grpc.StreamServerInterceptor {
|
||||
return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||
err := handler(srv, ss)
|
||||
return kubeErrToGRPC(err)
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,18 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// PanicLoggerUnaryServerInterceptor returns a new unary server interceptor for recovering from panics and returning error
|
||||
@@ -15,7 +21,7 @@ func PanicLoggerUnaryServerInterceptor(log *logrus.Entry) grpc.UnaryServerInterc
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Errorf("Recovered from panic: %+v\n%s", r, debug.Stack())
|
||||
err = grpc.Errorf(codes.Internal, "%s", r)
|
||||
err = status.Errorf(codes.Internal, "%s", r)
|
||||
}
|
||||
}()
|
||||
return handler(ctx, req)
|
||||
@@ -28,9 +34,118 @@ func PanicLoggerStreamServerInterceptor(log *logrus.Entry) grpc.StreamServerInte
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Errorf("Recovered from panic: %+v\n%s", r, debug.Stack())
|
||||
err = grpc.Errorf(codes.Internal, "%s", r)
|
||||
err = status.Errorf(codes.Internal, "%s", r)
|
||||
}
|
||||
}()
|
||||
return handler(srv, stream)
|
||||
}
|
||||
}
|
||||
|
||||
// BlockingDial is a helper method to dial the given address, using optional TLS credentials,
|
||||
// and blocking until the returned connection is ready. If the given credentials are nil, the
|
||||
// connection will be insecure (plain-text).
|
||||
// Lifted from: https://github.com/fullstorydev/grpcurl/blob/master/grpcurl.go
|
||||
func BlockingDial(ctx context.Context, network, address string, creds credentials.TransportCredentials, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
|
||||
// grpc.Dial doesn't provide any information on permanent connection errors (like
|
||||
// TLS handshake failures). So in order to provide good error messages, we need a
|
||||
// custom dialer that can provide that info. That means we manage the TLS handshake.
|
||||
result := make(chan interface{}, 1)
|
||||
writeResult := func(res interface{}) {
|
||||
// non-blocking write: we only need the first result
|
||||
select {
|
||||
case result <- res:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
dialer := func(address string, timeout time.Duration) (net.Conn, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
|
||||
conn, err := (&net.Dialer{Cancel: ctx.Done()}).Dial(network, address)
|
||||
if err != nil {
|
||||
writeResult(err)
|
||||
return nil, err
|
||||
}
|
||||
if creds != nil {
|
||||
conn, _, err = creds.ClientHandshake(ctx, address, conn)
|
||||
if err != nil {
|
||||
writeResult(err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// Even with grpc.FailOnNonTempDialError, this call will usually timeout in
|
||||
// the face of TLS handshake errors. So we can't rely on grpc.WithBlock() to
|
||||
// know when we're done. So we run it in a goroutine and then use result
|
||||
// channel to either get the channel or fail-fast.
|
||||
go func() {
|
||||
opts = append(opts,
|
||||
grpc.WithBlock(),
|
||||
grpc.FailOnNonTempDialError(true),
|
||||
grpc.WithDialer(dialer),
|
||||
grpc.WithInsecure(), // we are handling TLS, so tell grpc not to
|
||||
)
|
||||
conn, err := grpc.DialContext(ctx, address, opts...)
|
||||
var res interface{}
|
||||
if err != nil {
|
||||
res = err
|
||||
} else {
|
||||
res = conn
|
||||
}
|
||||
writeResult(res)
|
||||
}()
|
||||
|
||||
select {
|
||||
case res := <-result:
|
||||
if conn, ok := res.(*grpc.ClientConn); ok {
|
||||
return conn, nil
|
||||
}
|
||||
return nil, res.(error)
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
type TLSTestResult struct {
|
||||
TLS bool
|
||||
InsecureErr error
|
||||
}
|
||||
|
||||
func TestTLS(address string) (*TLSTestResult, error) {
|
||||
if parts := strings.Split(address, ":"); len(parts) == 1 {
|
||||
// If port is unspecified, assume the most likely port
|
||||
address += ":443"
|
||||
}
|
||||
var testResult TLSTestResult
|
||||
var tlsConfig tls.Config
|
||||
tlsConfig.InsecureSkipVerify = true
|
||||
creds := credentials.NewTLS(&tlsConfig)
|
||||
conn, err := BlockingDial(context.Background(), "tcp", address, creds)
|
||||
if err == nil {
|
||||
_ = conn.Close()
|
||||
testResult.TLS = true
|
||||
creds := credentials.NewTLS(&tls.Config{})
|
||||
conn, err := BlockingDial(context.Background(), "tcp", address, creds)
|
||||
if err == nil {
|
||||
_ = conn.Close()
|
||||
} else {
|
||||
// if connection was successful with InsecureSkipVerify true, but unsuccessful with
|
||||
// InsecureSkipVerify false, it means server is not configured securely
|
||||
testResult.InsecureErr = err
|
||||
}
|
||||
return &testResult, nil
|
||||
}
|
||||
// If we get here, we were unable to connect via TLS (even with InsecureSkipVerify: true)
|
||||
// It may be because server is running without TLS, or because of real issues (e.g. connection
|
||||
// refused). Test if server accepts plain-text connections
|
||||
conn, err = BlockingDial(context.Background(), "tcp", address, nil)
|
||||
if err == nil {
|
||||
_ = conn.Close()
|
||||
testResult.TLS = false
|
||||
return &testResult, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -3,21 +3,30 @@ package ksonnet
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"encoding/json"
|
||||
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/diff"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/ksonnet/ksonnet/metadata"
|
||||
"github.com/ksonnet/ksonnet/metadata/app"
|
||||
"github.com/ksonnet/ksonnet/pkg/app"
|
||||
"github.com/ksonnet/ksonnet/pkg/component"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/afero"
|
||||
"k8s.io/api/apps/v1beta1"
|
||||
"k8s.io/api/apps/v1beta2"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
v1ExtBeta1 "k8s.io/api/extensions/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
var (
|
||||
diffSeparator = regexp.MustCompile(`\n---`)
|
||||
lineSeparator = regexp.MustCompile(`\n`)
|
||||
)
|
||||
|
||||
// KsonnetApp represents a ksonnet application directory and provides wrapper functionality around
|
||||
@@ -29,6 +38,9 @@ type KsonnetApp interface {
|
||||
// App is the Ksonnet application
|
||||
App() app.App
|
||||
|
||||
// Spec is the Ksonnet application spec
|
||||
Spec() *app.Spec
|
||||
|
||||
// Show returns a list of unstructured objects that would be applied to an environment
|
||||
Show(environment string) ([]*unstructured.Unstructured, error)
|
||||
|
||||
@@ -39,24 +51,41 @@ type KsonnetApp interface {
|
||||
SetComponentParams(environment string, component string, param string, value string) error
|
||||
}
|
||||
|
||||
// KsonnetVersion returns the version of ksonnet used when running ksonnet commands
|
||||
func KsonnetVersion() (string, error) {
|
||||
cmd := exec.Command("ks", "version")
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to determine ksonnet version: %v", err)
|
||||
}
|
||||
ksonnetVersionStr := strings.Split(string(out), "\n")[0]
|
||||
parts := strings.SplitN(ksonnetVersionStr, ":", 2)
|
||||
if len(parts) != 2 {
|
||||
return "", fmt.Errorf("unexpected version string format: %s", ksonnetVersionStr)
|
||||
}
|
||||
return strings.TrimSpace(parts[1]), nil
|
||||
}
|
||||
|
||||
type ksonnetApp struct {
|
||||
manager metadata.Manager
|
||||
app app.App
|
||||
app app.App
|
||||
spec app.Spec
|
||||
}
|
||||
|
||||
// NewKsonnetApp tries to create a new wrapper to run commands on the `ks` command-line tool.
|
||||
func NewKsonnetApp(path string) (KsonnetApp, error) {
|
||||
ksApp := ksonnetApp{}
|
||||
mgr, err := metadata.Find(path)
|
||||
a, err := app.Load(afero.NewOsFs(), path, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ksApp.manager = mgr
|
||||
app, err := ksApp.manager.App()
|
||||
ksApp.app = a
|
||||
|
||||
var spec app.Spec
|
||||
err = cli.UnmarshalLocalFile(filepath.Join(a.Root(), "app.yaml"), &spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ksApp.app = app
|
||||
ksApp.spec = spec
|
||||
return &ksApp, nil
|
||||
}
|
||||
|
||||
@@ -80,14 +109,19 @@ func (k *ksonnetApp) ksCmd(args ...string) (string, error) {
|
||||
}
|
||||
|
||||
func (k *ksonnetApp) Root() string {
|
||||
return k.manager.Root()
|
||||
return k.app.Root()
|
||||
}
|
||||
|
||||
// Spec is the Ksonnet application spec (app.yaml)
|
||||
// App is the Ksonnet application
|
||||
func (k *ksonnetApp) App() app.App {
|
||||
return k.app
|
||||
}
|
||||
|
||||
// Spec is the Ksonnet application spec
|
||||
func (k *ksonnetApp) Spec() *app.Spec {
|
||||
return &k.spec
|
||||
}
|
||||
|
||||
// Show generates a concatenated list of Kubernetes manifests in the given environment.
|
||||
func (k *ksonnetApp) Show(environment string) ([]*unstructured.Unstructured, error) {
|
||||
out, err := k.ksCmd("show", environment)
|
||||
@@ -105,35 +139,77 @@ func (k *ksonnetApp) Show(environment string) ([]*unstructured.Unstructured, err
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to unmarshal manifest from `ks show`")
|
||||
}
|
||||
err = remarshal(&obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objs = append(objs, &obj)
|
||||
}
|
||||
// TODO(jessesuen): we need to sort objects based on their dependency order of creation
|
||||
return objs, nil
|
||||
}
|
||||
|
||||
// remarshal checks resource kind and version and re-marshal using corresponding struct custom marshaller. This ensures that expected resource state is formatter same as actual
|
||||
// resource state in kubernetes and allows to find differences between actual and target states more accurate.
|
||||
func remarshal(obj *unstructured.Unstructured) error {
|
||||
var newObj interface{}
|
||||
switch obj.GetAPIVersion() + ":" + obj.GetKind() {
|
||||
case "apps/v1beta1:Deployment":
|
||||
newObj = &v1beta1.Deployment{}
|
||||
case "apps/v1beta2:Deployment":
|
||||
newObj = &v1beta2.Deployment{}
|
||||
case "extensions/v1beta1":
|
||||
newObj = &v1ExtBeta1.Deployment{}
|
||||
case "apps/v1beta1:StatefulSet":
|
||||
newObj = &v1beta1.StatefulSet{}
|
||||
case "apps/v1beta2:StatefulSet":
|
||||
newObj = &v1beta2.StatefulSet{}
|
||||
case "v1:Service":
|
||||
newObj = &corev1.Service{}
|
||||
}
|
||||
if newObj != nil {
|
||||
oldObj := obj.Object
|
||||
data, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = json.Unmarshal(data, newObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data, err = json.Marshal(newObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = json.Unmarshal(data, obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// remove all default values specified by custom formatter
|
||||
obj.Object = diff.RemoveMapFields(oldObj, obj.Object)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListEnvParams returns list of environment parameters
|
||||
func (k *ksonnetApp) ListEnvParams(environment string) ([]*v1alpha1.ComponentParameter, error) {
|
||||
// count of rows to skip in command-line output
|
||||
const skipRows = 2
|
||||
out, err := k.ksCmd("param", "list", "--env", environment)
|
||||
mod, err := component.DefaultManager.Module(k.app, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ksParams, err := mod.Params(environment)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var params []*v1alpha1.ComponentParameter
|
||||
rows := lineSeparator.Split(out, -1)
|
||||
for _, row := range rows[skipRows:] {
|
||||
if strings.TrimSpace(row) == "" {
|
||||
continue
|
||||
}
|
||||
fields := strings.Fields(row)
|
||||
component, param, rawValue := fields[0], fields[1], fields[2]
|
||||
value, err := strconv.Unquote(rawValue)
|
||||
for _, ksParam := range ksParams {
|
||||
value, err := strconv.Unquote(ksParam.Value)
|
||||
if err != nil {
|
||||
value = rawValue
|
||||
value = ksParam.Value
|
||||
}
|
||||
componentParam := v1alpha1.ComponentParameter{
|
||||
Component: component,
|
||||
Name: param,
|
||||
Component: ksParam.Component,
|
||||
Name: ksParam.Key,
|
||||
Value: value,
|
||||
}
|
||||
params = append(params, &componentParam)
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
// Package kube provides helper utilities common for kubernetes
|
||||
|
||||
package kube
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -14,12 +19,38 @@ import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
const (
|
||||
listVerb = "list"
|
||||
deleteVerb = "delete"
|
||||
deleteCollectionVerb = "deletecollection"
|
||||
)
|
||||
|
||||
const (
|
||||
ServiceKind = "Service"
|
||||
EndpointsKind = "Endpoints"
|
||||
)
|
||||
|
||||
var (
|
||||
// location to use for generating temporary files, such as the ca.crt needed by kubectl
|
||||
kubectlTempDir string
|
||||
)
|
||||
|
||||
func init() {
|
||||
fileInfo, err := os.Stat("/dev/shm")
|
||||
if err == nil && fileInfo.IsDir() {
|
||||
kubectlTempDir = "/dev/shm"
|
||||
}
|
||||
}
|
||||
|
||||
// TestConfig tests to make sure the REST config is usable
|
||||
func TestConfig(config *rest.Config) error {
|
||||
kubeclientset, err := kubernetes.NewForConfig(config)
|
||||
@@ -84,8 +115,135 @@ func GetLiveResource(dclient dynamic.Interface, obj *unstructured.Unstructured,
|
||||
return liveObj, nil
|
||||
}
|
||||
|
||||
func WatchResourcesWithLabel(ctx context.Context, config *rest.Config, namespace string, labelName string) (chan watch.Event, error) {
|
||||
log.Infof("Start watching for resources changes with label %s in cluster %s", labelName, config.Host)
|
||||
dynClientPool := dynamic.NewDynamicClientPool(config)
|
||||
disco, err := discovery.NewDiscoveryClientForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serverResources, err := disco.ServerResources()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resources := make([]dynamic.ResourceInterface, 0)
|
||||
for _, apiResourcesList := range serverResources {
|
||||
for i := range apiResourcesList.APIResources {
|
||||
apiResource := apiResourcesList.APIResources[i]
|
||||
watchSupported := false
|
||||
for _, verb := range apiResource.Verbs {
|
||||
if verb == "watch" {
|
||||
watchSupported = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if watchSupported {
|
||||
dclient, err := dynClientPool.ClientForGroupVersionKind(schema.FromAPIVersionAndKind(apiResourcesList.GroupVersion, apiResource.Kind))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resources = append(resources, dclient.Resource(&apiResource, namespace))
|
||||
}
|
||||
}
|
||||
}
|
||||
ch := make(chan watch.Event)
|
||||
go func() {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(resources))
|
||||
for i := 0; i < len(resources); i++ {
|
||||
resource := resources[i]
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
watch, err := resource.Watch(metav1.ListOptions{LabelSelector: labelName})
|
||||
go func() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
watch.Stop()
|
||||
}
|
||||
}()
|
||||
if err == nil {
|
||||
for event := range watch.ResultChan() {
|
||||
ch <- event
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
close(ch)
|
||||
log.Infof("Stop watching for resources changes with label %s in cluster %s", labelName, config.ServerName)
|
||||
}()
|
||||
return ch, nil
|
||||
}
|
||||
|
||||
// GetResourcesWithLabel returns all kubernetes resources with specified label
|
||||
func GetResourcesWithLabel(config *rest.Config, namespace string, labelName string, labelValue string) ([]*unstructured.Unstructured, error) {
|
||||
dynClientPool := dynamic.NewDynamicClientPool(config)
|
||||
disco, err := discovery.NewDiscoveryClientForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resources, err := disco.ServerResources()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var resourceInterfaces []dynamic.ResourceInterface
|
||||
|
||||
for _, apiResourcesList := range resources {
|
||||
for i := range apiResourcesList.APIResources {
|
||||
apiResource := apiResourcesList.APIResources[i]
|
||||
listSupported := false
|
||||
for _, verb := range apiResource.Verbs {
|
||||
if verb == listVerb {
|
||||
listSupported = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if listSupported {
|
||||
dclient, err := dynClientPool.ClientForGroupVersionKind(schema.FromAPIVersionAndKind(apiResourcesList.GroupVersion, apiResource.Kind))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resourceInterfaces = append(resourceInterfaces, dclient.Resource(&apiResource, namespace))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var asyncErr error
|
||||
var result []*unstructured.Unstructured
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(resourceInterfaces))
|
||||
for i := range resourceInterfaces {
|
||||
client := resourceInterfaces[i]
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
list, err := client.List(metav1.ListOptions{
|
||||
LabelSelector: fmt.Sprintf("%s=%s", labelName, labelValue),
|
||||
})
|
||||
if err != nil {
|
||||
asyncErr = err
|
||||
return
|
||||
}
|
||||
// apply client side filtering since not every kubernetes API supports label filtering
|
||||
for i := range list.(*unstructured.UnstructuredList).Items {
|
||||
item := list.(*unstructured.UnstructuredList).Items[i]
|
||||
labels := item.GetLabels()
|
||||
if labels != nil {
|
||||
if value, ok := labels[labelName]; ok && value == labelValue {
|
||||
result = append(result, &item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
return result, asyncErr
|
||||
}
|
||||
|
||||
// DeleteResourceWithLabel delete all resources which match to specified label selector
|
||||
func DeleteResourceWithLabel(config *rest.Config, namespace string, labelSelector string) error {
|
||||
func DeleteResourceWithLabel(config *rest.Config, namespace string, labelName string, labelValue string) error {
|
||||
dynClientPool := dynamic.NewDynamicClientPool(config)
|
||||
disco, err := discovery.NewDiscoveryClientForConfig(config)
|
||||
if err != nil {
|
||||
@@ -95,14 +253,21 @@ func DeleteResourceWithLabel(config *rest.Config, namespace string, labelSelecto
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var resourceInterfaces []struct {
|
||||
dynamic.ResourceInterface
|
||||
bool
|
||||
}
|
||||
|
||||
for _, apiResourcesList := range resources {
|
||||
for _, apiResource := range apiResourcesList.APIResources {
|
||||
for i := range apiResourcesList.APIResources {
|
||||
apiResource := apiResourcesList.APIResources[i]
|
||||
deleteCollectionSupported := false
|
||||
deleteSupported := false
|
||||
for _, verb := range apiResource.Verbs {
|
||||
if verb == "deletecollection" {
|
||||
if verb == deleteCollectionVerb {
|
||||
deleteCollectionSupported = true
|
||||
} else if verb == "delete" {
|
||||
} else if verb == deleteVerb {
|
||||
deleteSupported = true
|
||||
}
|
||||
}
|
||||
@@ -110,31 +275,61 @@ func DeleteResourceWithLabel(config *rest.Config, namespace string, labelSelecto
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
propagationPolicy := metav1.DeletePropagationForeground
|
||||
if deleteCollectionSupported {
|
||||
err = dclient.Resource(&apiResource, namespace).DeleteCollection(&metav1.DeleteOptions{
|
||||
PropagationPolicy: &propagationPolicy,
|
||||
}, metav1.ListOptions{LabelSelector: labelSelector})
|
||||
if err != nil && !apierr.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
} else if deleteSupported {
|
||||
items, err := dclient.Resource(&apiResource, namespace).List(metav1.ListOptions{LabelSelector: labelSelector})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, item := range items.(*unstructured.UnstructuredList).Items {
|
||||
err = dclient.Resource(&apiResource, namespace).Delete(item.GetName(), &metav1.DeleteOptions{
|
||||
PropagationPolicy: &propagationPolicy,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if deleteCollectionSupported || deleteSupported {
|
||||
resourceInterfaces = append(resourceInterfaces, struct {
|
||||
dynamic.ResourceInterface
|
||||
bool
|
||||
}{dclient.Resource(&apiResource, namespace), deleteCollectionSupported})
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
var asyncErr error
|
||||
propagationPolicy := metav1.DeletePropagationForeground
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(resourceInterfaces))
|
||||
|
||||
for i := range resourceInterfaces {
|
||||
client := resourceInterfaces[i].ResourceInterface
|
||||
deleteCollectionSupported := resourceInterfaces[i].bool
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if deleteCollectionSupported {
|
||||
err = client.DeleteCollection(&metav1.DeleteOptions{
|
||||
PropagationPolicy: &propagationPolicy,
|
||||
}, metav1.ListOptions{LabelSelector: fmt.Sprintf("%s=%s", labelName, labelValue)})
|
||||
if err != nil && !apierr.IsNotFound(err) {
|
||||
asyncErr = err
|
||||
}
|
||||
} else {
|
||||
items, err := client.List(metav1.ListOptions{LabelSelector: fmt.Sprintf("%s=%s", labelName, labelValue)})
|
||||
if err != nil {
|
||||
asyncErr = err
|
||||
return
|
||||
}
|
||||
for _, item := range items.(*unstructured.UnstructuredList).Items {
|
||||
// apply client side filtering since not every kubernetes API supports label filtering
|
||||
labels := item.GetLabels()
|
||||
if labels != nil {
|
||||
if value, ok := labels[labelName]; ok && value == labelValue {
|
||||
err = client.Delete(item.GetName(), &metav1.DeleteOptions{
|
||||
PropagationPolicy: &propagationPolicy,
|
||||
})
|
||||
if err != nil && !apierr.IsNotFound(err) {
|
||||
asyncErr = err
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
return asyncErr
|
||||
}
|
||||
|
||||
// GetLiveResources returns the corresponding live resource from a list of resources
|
||||
@@ -236,32 +431,120 @@ func ListAllResources(config *rest.Config, apiResources []metav1.APIResource, na
|
||||
return resources, nil
|
||||
}
|
||||
|
||||
// ApplyResource performs an apply of a unstructured resource
|
||||
func ApplyResource(config *rest.Config, obj *unstructured.Unstructured, namespace string) (*unstructured.Unstructured, error) {
|
||||
// deleteFile is best effort deletion of a file
|
||||
func deleteFile(path string) {
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
return
|
||||
}
|
||||
_ = os.Remove(path)
|
||||
}
|
||||
|
||||
// DeleteResource deletes resource
|
||||
func DeleteResource(config *rest.Config, obj *unstructured.Unstructured, namespace string) error {
|
||||
dynClientPool := dynamic.NewDynamicClientPool(config)
|
||||
disco, err := discovery.NewDiscoveryClientForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
gvk := obj.GroupVersionKind()
|
||||
dclient, err := dynClientPool.ClientForGroupVersionKind(gvk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
apiResource, err := ServerResourceForGroupVersionKind(disco, gvk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
reIf := dclient.Resource(apiResource, namespace)
|
||||
liveObj, err := reIf.Update(obj)
|
||||
if err != nil {
|
||||
if !apierr.IsNotFound(err) {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
liveObj, err = reIf.Create(obj)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
}
|
||||
return liveObj, nil
|
||||
propagationPolicy := metav1.DeletePropagationForeground
|
||||
return reIf.Delete(obj.GetName(), &metav1.DeleteOptions{PropagationPolicy: &propagationPolicy})
|
||||
}
|
||||
|
||||
// ApplyResource performs an apply of a unstructured resource
|
||||
func ApplyResource(config *rest.Config, obj *unstructured.Unstructured, namespace string) (*unstructured.Unstructured, error) {
|
||||
log.Infof("Applying resource %s/%s in cluster: %s, namespace: %s", obj.GetKind(), obj.GetName(), config.Host, namespace)
|
||||
f, err := ioutil.TempFile(kubectlTempDir, "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to generate temp file for kubeconfig: %v", err)
|
||||
}
|
||||
_ = f.Close()
|
||||
err = WriteKubeConfig(config, namespace, f.Name())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to write kubeconfig: %v", err)
|
||||
}
|
||||
defer deleteFile(f.Name())
|
||||
|
||||
manifestBytes, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd := exec.Command("kubectl", "--kubeconfig", f.Name(), "-n", namespace, "apply", "-o", "json", "-f", "-")
|
||||
log.Info(cmd.Args)
|
||||
cmd.Stdin = bytes.NewReader(manifestBytes)
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
exErr := err.(*exec.ExitError)
|
||||
return nil, fmt.Errorf("failed to apply '%s': %s", obj.GetName(), exErr.Stderr)
|
||||
}
|
||||
var liveObj unstructured.Unstructured
|
||||
err = json.Unmarshal(out, &liveObj)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to apply '%s': %s", obj.GetName(), err)
|
||||
}
|
||||
return &liveObj, nil
|
||||
}
|
||||
|
||||
// WriteKubeConfig takes a rest.Config and writes it as a kubeconfig at the specified path
|
||||
func WriteKubeConfig(restConfig *rest.Config, namespace, filename string) error {
|
||||
var kubeConfig = clientcmdapi.Config{
|
||||
CurrentContext: restConfig.Host,
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
restConfig.Host: {
|
||||
Cluster: restConfig.Host,
|
||||
AuthInfo: restConfig.Host,
|
||||
Namespace: namespace,
|
||||
},
|
||||
},
|
||||
Clusters: map[string]*clientcmdapi.Cluster{
|
||||
restConfig.Host: {
|
||||
Server: restConfig.Host,
|
||||
},
|
||||
},
|
||||
AuthInfos: map[string]*clientcmdapi.AuthInfo{
|
||||
restConfig.Host: {},
|
||||
},
|
||||
}
|
||||
// Set Cluster info
|
||||
if restConfig.TLSClientConfig.Insecure {
|
||||
kubeConfig.Clusters[restConfig.Host].InsecureSkipTLSVerify = true
|
||||
}
|
||||
if restConfig.TLSClientConfig.CAFile != "" {
|
||||
kubeConfig.Clusters[restConfig.Host].CertificateAuthority = restConfig.TLSClientConfig.CAFile
|
||||
}
|
||||
// Set AuthInfo
|
||||
if len(restConfig.TLSClientConfig.CAData) > 0 {
|
||||
kubeConfig.Clusters[restConfig.Host].CertificateAuthorityData = restConfig.TLSClientConfig.CAData
|
||||
}
|
||||
if restConfig.TLSClientConfig.CertFile != "" {
|
||||
kubeConfig.AuthInfos[restConfig.Host].ClientCertificate = restConfig.TLSClientConfig.CertFile
|
||||
}
|
||||
if len(restConfig.TLSClientConfig.CertData) > 0 {
|
||||
kubeConfig.AuthInfos[restConfig.Host].ClientCertificateData = restConfig.TLSClientConfig.CertData
|
||||
}
|
||||
if restConfig.TLSClientConfig.KeyFile != "" {
|
||||
kubeConfig.AuthInfos[restConfig.Host].ClientKey = restConfig.TLSClientConfig.KeyFile
|
||||
}
|
||||
if len(restConfig.TLSClientConfig.KeyData) > 0 {
|
||||
kubeConfig.AuthInfos[restConfig.Host].ClientKeyData = restConfig.TLSClientConfig.KeyData
|
||||
}
|
||||
if restConfig.Username != "" {
|
||||
kubeConfig.AuthInfos[restConfig.Host].Username = restConfig.Username
|
||||
}
|
||||
if restConfig.Password != "" {
|
||||
kubeConfig.AuthInfos[restConfig.Host].Password = restConfig.Password
|
||||
}
|
||||
if restConfig.BearerToken != "" {
|
||||
kubeConfig.AuthInfos[restConfig.Host].Token = restConfig.BearerToken
|
||||
}
|
||||
return clientcmd.WriteToFile(kubeConfig, filename)
|
||||
}
|
||||
|
||||
176
util/localconfig/localconfig.go
Normal file
176
util/localconfig/localconfig.go
Normal file
@@ -0,0 +1,176 @@
|
||||
package localconfig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
)
|
||||
|
||||
// LocalConfig is a local ArgoCD config file
|
||||
type LocalConfig struct {
|
||||
CurrentContext string `json:"current-context"`
|
||||
Contexts []ContextRef `json:"contexts"`
|
||||
Servers []Server `json:"servers"`
|
||||
Users []User `json:"users"`
|
||||
}
|
||||
|
||||
// ContextRef is a reference to a Server and User for an API client
|
||||
type ContextRef struct {
|
||||
Name string `json:"name"`
|
||||
Server string `json:"server"`
|
||||
User string `json:"user"`
|
||||
}
|
||||
|
||||
// Context is the resolved Server and User objects resolved
|
||||
type Context struct {
|
||||
Name string
|
||||
Server Server
|
||||
User User
|
||||
}
|
||||
|
||||
// Server contains ArgoCD server information
|
||||
type Server struct {
|
||||
// Server is the ArgoCD server address
|
||||
Server string `json:"server"`
|
||||
// Insecure indicates to connect to the server over TLS insecurely
|
||||
Insecure bool `json:"insecure,omitempty"`
|
||||
// CACertificateAuthorityData is the base64 string of a PEM encoded certificate
|
||||
// TODO: not yet implemented
|
||||
CACertificateAuthorityData string `json:"certificate-authority-data,omitempty"`
|
||||
// PlainText indicates to connect with TLS disabled
|
||||
PlainText bool `json:"plain-text,omitempty"`
|
||||
}
|
||||
|
||||
// User contains user authentication information
|
||||
type User struct {
|
||||
Name string `json:"name"`
|
||||
AuthToken string `json:"auth-token,omitempty"`
|
||||
}
|
||||
|
||||
// ReadLocalConfig loads up the local configuration file. Returns nil if config does not exist
|
||||
func ReadLocalConfig(path string) (*LocalConfig, error) {
|
||||
var err error
|
||||
var config LocalConfig
|
||||
err = cli.UnmarshalLocalFile(path, &config)
|
||||
if os.IsNotExist(err) {
|
||||
return nil, nil
|
||||
}
|
||||
err = ValidateLocalConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
func ValidateLocalConfig(config LocalConfig) error {
|
||||
if config.CurrentContext == "" {
|
||||
return fmt.Errorf("Local config invalid: current-context unset")
|
||||
}
|
||||
if _, err := config.ResolveContext(config.CurrentContext); err != nil {
|
||||
return fmt.Errorf("Local config invalid: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteLocalConfig writes a new local configuration file.
|
||||
func WriteLocalConfig(config LocalConfig, configPath string) error {
|
||||
err := os.MkdirAll(path.Dir(configPath), os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cli.MarshalLocalYAMLFile(configPath, config)
|
||||
}
|
||||
|
||||
// ResolveContext resolves the specified context. If unspecified, resolves the current context
|
||||
func (l *LocalConfig) ResolveContext(name string) (*Context, error) {
|
||||
if name == "" {
|
||||
name = l.CurrentContext
|
||||
}
|
||||
for _, ctx := range l.Contexts {
|
||||
if ctx.Name == name {
|
||||
server, err := l.GetServer(ctx.Server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user, err := l.GetUser(ctx.User)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Context{
|
||||
Name: ctx.Name,
|
||||
Server: *server,
|
||||
User: *user,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("Context '%s' undefined", name)
|
||||
}
|
||||
|
||||
func (l *LocalConfig) GetServer(name string) (*Server, error) {
|
||||
for _, s := range l.Servers {
|
||||
if s.Server == name {
|
||||
return &s, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("Server '%s' undefined", name)
|
||||
}
|
||||
|
||||
func (l *LocalConfig) UpsertServer(server Server) {
|
||||
for i, s := range l.Servers {
|
||||
if s.Server == server.Server {
|
||||
l.Servers[i] = server
|
||||
return
|
||||
}
|
||||
}
|
||||
l.Servers = append(l.Servers, server)
|
||||
}
|
||||
|
||||
func (l *LocalConfig) GetUser(name string) (*User, error) {
|
||||
for _, u := range l.Users {
|
||||
if u.Name == name {
|
||||
return &u, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("User '%s' undefined", name)
|
||||
}
|
||||
|
||||
func (l *LocalConfig) UpsertUser(user User) {
|
||||
for i, u := range l.Users {
|
||||
if u.Name == user.Name {
|
||||
l.Users[i] = user
|
||||
return
|
||||
}
|
||||
}
|
||||
l.Users = append(l.Users, user)
|
||||
}
|
||||
|
||||
func (l *LocalConfig) UpsertContext(context ContextRef) {
|
||||
for i, c := range l.Contexts {
|
||||
if c.Name == context.Name {
|
||||
l.Contexts[i] = context
|
||||
return
|
||||
}
|
||||
}
|
||||
l.Contexts = append(l.Contexts, context)
|
||||
}
|
||||
|
||||
// DefaultConfigDir returns the local configuration path for settings such as cached authentication tokens.
|
||||
func DefaultConfigDir() (string, error) {
|
||||
usr, err := user.Current()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return path.Join(usr.HomeDir, ".argocd"), nil
|
||||
}
|
||||
|
||||
// DefaultLocalConfigPath returns the local configuration path for settings such as cached authentication tokens.
|
||||
func DefaultLocalConfigPath() (string, error) {
|
||||
dir, err := DefaultConfigDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return path.Join(dir, "config"), nil
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
package util
|
||||
package password
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
@@ -28,14 +31,22 @@ var preferredHashers = []PasswordHasher{
|
||||
|
||||
// HashPasswordWithHashers hashes an entered password using the first hasher in the provided list of hashers.
|
||||
func hashPasswordWithHashers(password string, hashers []PasswordHasher) (string, error) {
|
||||
// Even though good hashers will disallow blank passwords, let's be explicit that ALL BLANK PASSWORDS ARE INVALID. Full stop.
|
||||
if password == "" {
|
||||
return "", fmt.Errorf("blank passwords are not allowed")
|
||||
}
|
||||
return hashers[0].HashPassword(password)
|
||||
}
|
||||
|
||||
// VerifyPasswordWithHashers verifies an entered password against a hashed password using one or more algorithms. It returns whether the hash is "stale" (i.e., was verified using something other than the FIRST hasher specified).
|
||||
func verifyPasswordWithHashers(password, hashedPassword string, hashers []PasswordHasher) (valid, stale bool) {
|
||||
// Be explicit here for security, even though zero values can be assumed.
|
||||
valid = false
|
||||
stale = false
|
||||
func verifyPasswordWithHashers(password, hashedPassword string, hashers []PasswordHasher) (bool, bool) {
|
||||
// Even though good hashers will disallow blank passwords, let's be explicit that ALL BLANK PASSWORDS ARE INVALID. Full stop.
|
||||
if password == "" {
|
||||
return false, false
|
||||
}
|
||||
|
||||
valid, stale := false, false
|
||||
|
||||
for idx, hasher := range hashers {
|
||||
if hasher.VerifyPassword(password, hashedPassword) {
|
||||
valid = true
|
||||
@@ -45,7 +56,8 @@ func verifyPasswordWithHashers(password, hashedPassword string, hashers []Passwo
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
return valid, stale
|
||||
}
|
||||
|
||||
// HashPassword hashes against the current preferred hasher.
|
||||
@@ -66,7 +78,7 @@ func (h DummyPasswordHasher) HashPassword(password string) (string, error) {
|
||||
|
||||
// VerifyPassword validates whether a one-way digest ("hash") of a password was created from a given plaintext password.
|
||||
func (h DummyPasswordHasher) VerifyPassword(password, hashedPassword string) bool {
|
||||
return password == hashedPassword
|
||||
return 1 == subtle.ConstantTimeCompare([]byte(password), []byte(hashedPassword))
|
||||
}
|
||||
|
||||
// HashPassword creates a one-way digest ("hash") of a password. In the case of Bcrypt, a pseudorandom salt is included automatically by the underlying library. For security reasons, the work factor is always at _least_ bcrypt.DefaultCost.
|
||||
@@ -1,4 +1,4 @@
|
||||
package util
|
||||
package password
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -12,10 +12,10 @@ func testPasswordHasher(t *testing.T, h PasswordHasher) {
|
||||
)
|
||||
hashedPassword, _ := h.HashPassword(defaultPassword)
|
||||
if !h.VerifyPassword(defaultPassword, hashedPassword) {
|
||||
t.Errorf("Password \"%s\" should have validated against hash \"%s\"", defaultPassword, hashedPassword)
|
||||
t.Errorf("Password %q should have validated against hash %q", defaultPassword, hashedPassword)
|
||||
}
|
||||
if h.VerifyPassword(defaultPassword, pollution+hashedPassword) {
|
||||
t.Errorf("Password \"%s\" should NOT have validated against hash \"%s\"", defaultPassword, pollution+hashedPassword)
|
||||
t.Errorf("Password %q should NOT have validated against hash %q", defaultPassword, pollution+hashedPassword)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ func TestDummyPasswordHasher(t *testing.T) {
|
||||
func TestPasswordHashing(t *testing.T) {
|
||||
const (
|
||||
defaultPassword = "Hello, world!"
|
||||
blankPassword = ""
|
||||
)
|
||||
hashers := []PasswordHasher{
|
||||
BcryptPasswordHasher{0},
|
||||
@@ -42,17 +43,26 @@ func TestPasswordHashing(t *testing.T) {
|
||||
hashedPassword, _ := hashPasswordWithHashers(defaultPassword, hashers)
|
||||
valid, stale := verifyPasswordWithHashers(defaultPassword, hashedPassword, hashers)
|
||||
if !valid {
|
||||
t.Errorf("Password \"%s\" should have validated against hash \"%s\"", defaultPassword, hashedPassword)
|
||||
t.Errorf("Password %q should have validated against hash %q", defaultPassword, hashedPassword)
|
||||
}
|
||||
if stale {
|
||||
t.Errorf("Password \"%s\" should not have been marked stale against hash \"%s\"", defaultPassword, hashedPassword)
|
||||
t.Errorf("Password %q should not have been marked stale against hash %q", defaultPassword, hashedPassword)
|
||||
}
|
||||
valid, stale = verifyPasswordWithHashers(defaultPassword, defaultPassword, hashers)
|
||||
if !valid {
|
||||
t.Errorf("Password \"%s\" should have validated against itself with dummy hasher", defaultPassword)
|
||||
t.Errorf("Password %q should have validated against itself with dummy hasher", defaultPassword)
|
||||
}
|
||||
if !stale {
|
||||
t.Errorf("Password \"%s\" should have been acknowledged stale against itself with dummy hasher", defaultPassword)
|
||||
t.Errorf("Password %q should have been acknowledged stale against itself with dummy hasher", defaultPassword)
|
||||
}
|
||||
|
||||
hashedPassword, err := hashPasswordWithHashers(blankPassword, hashers)
|
||||
if err == nil {
|
||||
t.Errorf("Blank password should have produced error, rather than hash %q", hashedPassword)
|
||||
}
|
||||
|
||||
valid, _ = verifyPasswordWithHashers(blankPassword, "", hashers)
|
||||
if valid != false {
|
||||
t.Errorf("Blank password should have failed verification")
|
||||
}
|
||||
}
|
||||
118
util/session/sessionmanager.go
Normal file
118
util/session/sessionmanager.go
Normal file
@@ -0,0 +1,118 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
util_password "github.com/argoproj/argo-cd/util/password"
|
||||
jwt "github.com/dgrijalva/jwt-go"
|
||||
)
|
||||
|
||||
// SessionManager generates and validates JWT tokens for login sessions.
|
||||
type SessionManager struct {
|
||||
serverSecretKey []byte
|
||||
}
|
||||
|
||||
const (
|
||||
// sessionManagerClaimsIssuer fills the "iss" field of the token.
|
||||
sessionManagerClaimsIssuer = "argocd"
|
||||
|
||||
// invalidLoginError, for security purposes, doesn't say whether the username or password was invalid. This does not mitigate the potential for timing attacks to determine which is which.
|
||||
invalidLoginError = "Invalid username or password"
|
||||
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{
|
||||
serverSecretKey: secretKey,
|
||||
}
|
||||
}
|
||||
|
||||
// Create creates a new token for a given subject (user) and returns it as a string.
|
||||
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,
|
||||
},
|
||||
}
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
|
||||
// Unix and get the complete encoded token as a string using the secret
|
||||
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) {
|
||||
// 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) {
|
||||
// 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
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// MakeSignature generates a cryptographically-secure pseudo-random token, based on a given number of random bytes, for signing purposes.
|
||||
func MakeSignature(size int) ([]byte, error) {
|
||||
b := make([]byte, size)
|
||||
_, err := rand.Read(b)
|
||||
if err != nil {
|
||||
b = nil
|
||||
}
|
||||
return b, err
|
||||
}
|
||||
|
||||
// LoginLocalUser checks if a username/password combo is correct and creates a new token if so.
|
||||
// [TODO] This may belong elsewhere.
|
||||
func (mgr SessionManager) LoginLocalUser(username, password string, users map[string]string) (string, error) {
|
||||
if password == "" {
|
||||
err := fmt.Errorf(blankPasswordError)
|
||||
return "", err
|
||||
}
|
||||
|
||||
passwordHash, ok := users[username]
|
||||
if !ok {
|
||||
// Username was not found in local user store.
|
||||
// Ensure we still send password to hashing algorithm for comparison.
|
||||
// This mitigates potential for timing attacks that benefit from short-circuiting,
|
||||
// provided the hashing library/algorithm in use doesn't itself short-circuit.
|
||||
passwordHash = ""
|
||||
}
|
||||
|
||||
if valid, _ := util_password.VerifyPassword(password, passwordHash); valid {
|
||||
token, err := mgr.Create(username)
|
||||
if err == nil {
|
||||
return token, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf(invalidLoginError)
|
||||
}
|
||||
38
util/session/sessionmanager_test.go
Normal file
38
util/session/sessionmanager_test.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSessionManager(t *testing.T) {
|
||||
const (
|
||||
defaultSecretKey = "Hello, world!"
|
||||
defaultSubject = "argo"
|
||||
)
|
||||
mgr := SessionManager{[]byte(defaultSecretKey)}
|
||||
|
||||
token, err := mgr.Create(defaultSubject)
|
||||
if err != nil {
|
||||
t.Errorf("Could not create token: %v", err)
|
||||
}
|
||||
|
||||
claims, err := mgr.Parse(token)
|
||||
if err != nil {
|
||||
t.Errorf("Could not parse token: %v", err)
|
||||
}
|
||||
|
||||
subject := claims.Subject
|
||||
if subject != "argo" {
|
||||
t.Errorf("Token claim subject \"%s\" does not match expected subject \"%s\".", subject, defaultSubject)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakeSignature(t *testing.T) {
|
||||
for size := 1; size <= 64; size++ {
|
||||
s, err := MakeSignature(size)
|
||||
if err != nil {
|
||||
t.Errorf("Could not generate signature of size %d: %v", size, err)
|
||||
}
|
||||
t.Logf("Generated token: %v", s)
|
||||
}
|
||||
}
|
||||
180
util/tls/tls.go
Normal file
180
util/tls/tls.go
Normal file
@@ -0,0 +1,180 @@
|
||||
package tls
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultRSABits = 2048
|
||||
)
|
||||
|
||||
type CertOptions struct {
|
||||
// Hostnames and IPs to generate a certificate for
|
||||
Hosts []string
|
||||
// Name of organization in certificate
|
||||
Organization string
|
||||
// Creation date
|
||||
ValidFrom time.Time
|
||||
// Duration that certificate is valid for
|
||||
ValidFor time.Duration
|
||||
// whether this cert should be its own Certificate Authority
|
||||
IsCA bool
|
||||
// Size of RSA key to generate. Ignored if --ecdsa-curve is set
|
||||
RSABits int
|
||||
// ECDSA curve to use to generate a key. Valid values are P224, P256 (recommended), P384, P521
|
||||
ECDSACurve string
|
||||
}
|
||||
|
||||
func publicKey(priv interface{}) interface{} {
|
||||
switch k := priv.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
return &k.PublicKey
|
||||
case *ecdsa.PrivateKey:
|
||||
return &k.PublicKey
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func pemBlockForKey(priv interface{}) *pem.Block {
|
||||
switch k := priv.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}
|
||||
case *ecdsa.PrivateKey:
|
||||
b, err := x509.MarshalECPrivateKey(k)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Unable to marshal ECDSA private key: %v", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func generate(opts CertOptions) ([]byte, crypto.PrivateKey, error) {
|
||||
if len(opts.Hosts) == 0 {
|
||||
return nil, nil, fmt.Errorf("hosts not supplied")
|
||||
}
|
||||
|
||||
var privateKey crypto.PrivateKey
|
||||
var err error
|
||||
switch opts.ECDSACurve {
|
||||
case "":
|
||||
rsaBits := DefaultRSABits
|
||||
if opts.RSABits != 0 {
|
||||
rsaBits = opts.RSABits
|
||||
}
|
||||
privateKey, err = rsa.GenerateKey(rand.Reader, rsaBits)
|
||||
case "P224":
|
||||
privateKey, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
|
||||
case "P256":
|
||||
privateKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
case "P384":
|
||||
privateKey, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
||||
case "P521":
|
||||
privateKey, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
|
||||
default:
|
||||
return nil, nil, fmt.Errorf("Unrecognized elliptic curve: %q", opts.ECDSACurve)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to generate private key: %s", err)
|
||||
}
|
||||
|
||||
var notBefore time.Time
|
||||
if opts.ValidFrom.IsZero() {
|
||||
notBefore = time.Now()
|
||||
} else {
|
||||
notBefore = opts.ValidFrom
|
||||
}
|
||||
var validFor time.Duration
|
||||
if opts.ValidFor == 0 {
|
||||
validFor = 365 * 24 * time.Hour
|
||||
}
|
||||
notAfter := notBefore.Add(validFor)
|
||||
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to generate serial number: %s", err)
|
||||
}
|
||||
|
||||
if opts.Organization == "" {
|
||||
return nil, nil, fmt.Errorf("organization not supplied")
|
||||
}
|
||||
template := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{opts.Organization},
|
||||
},
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
for _, h := range opts.Hosts {
|
||||
if ip := net.ParseIP(h); ip != nil {
|
||||
template.IPAddresses = append(template.IPAddresses, ip)
|
||||
} else {
|
||||
template.DNSNames = append(template.DNSNames, h)
|
||||
}
|
||||
}
|
||||
|
||||
if opts.IsCA {
|
||||
template.IsCA = true
|
||||
template.KeyUsage |= x509.KeyUsageCertSign
|
||||
}
|
||||
|
||||
certBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(privateKey), privateKey)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Failed to create certificate: %s", err)
|
||||
}
|
||||
return certBytes, privateKey, nil
|
||||
}
|
||||
|
||||
// generatePEM generates a new certificate and key and returns it as PEM encoded bytes
|
||||
func generatePEM(opts CertOptions) ([]byte, []byte, error) {
|
||||
certBytes, privateKey, err := generate(opts)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
certpem := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certBytes})
|
||||
keypem := pem.EncodeToMemory(pemBlockForKey(privateKey))
|
||||
return certpem, keypem, nil
|
||||
}
|
||||
|
||||
// GenerateX509KeyPair generates a X509 key pair
|
||||
func GenerateX509KeyPair(opts CertOptions) (*tls.Certificate, error) {
|
||||
certpem, keypem, err := generatePEM(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cert, err := tls.X509KeyPair(certpem, keypem)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &cert, nil
|
||||
}
|
||||
|
||||
// EncodeX509KeyPair encodes a TLS Certificate into its pem encoded for storage
|
||||
func EncodeX509KeyPair(cert tls.Certificate) ([]byte, []byte) {
|
||||
certpem := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Certificate[0]})
|
||||
keypem := pem.EncodeToMemory(pemBlockForKey(cert.PrivateKey))
|
||||
return certpem, keypem
|
||||
}
|
||||
Reference in New Issue
Block a user