mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-02-20 17:48:47 +01:00
Compare commits
73 Commits
appsetdocs
...
v1.3.6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89be1c9ce6 | ||
|
|
d16f29f0ac | ||
|
|
5c1f581584 | ||
|
|
3e150df0ed | ||
|
|
ce8d5a4533 | ||
|
|
c93ce9082e | ||
|
|
68e1a5fbeb | ||
|
|
6f38cf3133 | ||
|
|
2be56f232a | ||
|
|
bb5d2f4197 | ||
|
|
fff5355314 | ||
|
|
f44ce07664 | ||
|
|
116440690b | ||
|
|
7cef1313c7 | ||
|
|
a5a65cdfe7 | ||
|
|
219fae8380 | ||
|
|
ba04a028c1 | ||
|
|
634e0d6323 | ||
|
|
962fb84fba | ||
|
|
0a8507ac6e | ||
|
|
3026882f17 | ||
|
|
e84c56b279 | ||
|
|
25a1b4ccc6 | ||
|
|
90bc83d1f7 | ||
|
|
130b1e6218 | ||
|
|
af5f1a7e69 | ||
|
|
be93e7918b | ||
|
|
4ea88b621d | ||
|
|
e1336f1f23 | ||
|
|
0af64eaf9a | ||
|
|
9f8608c9fc | ||
|
|
4c42c5fc70 | ||
|
|
1c01f9b7f5 | ||
|
|
24d43e45f7 | ||
|
|
22826baf46 | ||
|
|
58675b5266 | ||
|
|
786a94b03b | ||
|
|
79e364228a | ||
|
|
fba1c0d4f3 | ||
|
|
65e427eb4f | ||
|
|
a808fd2989 | ||
|
|
947b074d9a | ||
|
|
53252aa6a1 | ||
|
|
9df5e9560a | ||
|
|
7a9f7b01f8 | ||
|
|
52f7a66826 | ||
|
|
bcdaddcf69 | ||
|
|
593715038c | ||
|
|
4f84498265 | ||
|
|
8186ff0bb9 | ||
|
|
0432a85832 | ||
|
|
a0d37654b4 | ||
|
|
fe3b17322a | ||
|
|
59623b85fe | ||
|
|
905a8b0d75 | ||
|
|
30935e2019 | ||
|
|
f3e0e097de | ||
|
|
e1ff01cb56 | ||
|
|
e0de3300d2 | ||
|
|
40cb6fa9ce | ||
|
|
11de95cbac | ||
|
|
c7cd2e92bb | ||
|
|
a4f13f5f29 | ||
|
|
6460de9d03 | ||
|
|
6f6b03f74c | ||
|
|
36775ae1bb | ||
|
|
4a360cf9f4 | ||
|
|
6e56302fa4 | ||
|
|
3d82d5aab7 | ||
|
|
7df03b3c89 | ||
|
|
e059760906 | ||
|
|
8925b52bc8 | ||
|
|
8a43840f0b |
@@ -177,7 +177,6 @@ jobs:
|
||||
command: PATH=dist:$PATH make test-e2e
|
||||
environment:
|
||||
ARGOCD_OPTS: "--server localhost:8080 --plaintext"
|
||||
ARGOCD_E2E_EXPECT_TIMEOUT: "30"
|
||||
ARGOCD_E2E_K3S: "true"
|
||||
- store_test_results:
|
||||
path: test-results
|
||||
|
||||
49
Dockerfile
49
Dockerfile
@@ -23,47 +23,16 @@ RUN apt-get update && apt-get install -y \
|
||||
|
||||
WORKDIR /tmp
|
||||
|
||||
# Install dep
|
||||
ENV DEP_VERSION=0.5.0
|
||||
RUN wget https://github.com/golang/dep/releases/download/v${DEP_VERSION}/dep-linux-amd64 -O /usr/local/bin/dep && \
|
||||
chmod +x /usr/local/bin/dep
|
||||
ADD hack/install.sh .
|
||||
ADD hack/installers installers
|
||||
|
||||
# Install packr
|
||||
ENV PACKR_VERSION=1.21.9
|
||||
RUN wget https://github.com/gobuffalo/packr/releases/download/v${PACKR_VERSION}/packr_${PACKR_VERSION}_linux_amd64.tar.gz && \
|
||||
tar -vxf packr*.tar.gz -C /tmp/ && \
|
||||
mv /tmp/packr /usr/local/bin/packr
|
||||
|
||||
# Install kubectl
|
||||
# NOTE: keep the version synced with https://storage.googleapis.com/kubernetes-release/release/stable.txt
|
||||
ENV KUBECTL_VERSION=1.14.0
|
||||
RUN curl -L -o /usr/local/bin/kubectl -LO https://storage.googleapis.com/kubernetes-release/release/v${KUBECTL_VERSION}/bin/linux/amd64/kubectl && \
|
||||
chmod +x /usr/local/bin/kubectl && \
|
||||
kubectl version --client
|
||||
|
||||
# Install ksonnet
|
||||
ENV KSONNET_VERSION=0.13.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 && \
|
||||
ks version
|
||||
|
||||
# Install helm
|
||||
ENV HELM_VERSION=2.12.1
|
||||
RUN wget https://storage.googleapis.com/kubernetes-helm/helm-v${HELM_VERSION}-linux-amd64.tar.gz && \
|
||||
tar -C /tmp/ -xf helm-v${HELM_VERSION}-linux-amd64.tar.gz && \
|
||||
mv /tmp/linux-amd64/helm /usr/local/bin/helm && \
|
||||
helm version --client
|
||||
|
||||
ENV KUSTOMIZE_VERSION=3.1.0
|
||||
RUN curl -L -o /usr/local/bin/kustomize https://github.com/kubernetes-sigs/kustomize/releases/download/v${KUSTOMIZE_VERSION}/kustomize_${KUSTOMIZE_VERSION}_linux_amd64 && \
|
||||
chmod +x /usr/local/bin/kustomize && \
|
||||
kustomize version
|
||||
|
||||
# Install AWS IAM Authenticator
|
||||
ENV AWS_IAM_AUTHENTICATOR_VERSION=0.4.0-alpha.1
|
||||
RUN curl -L -o /usr/local/bin/aws-iam-authenticator https://github.com/kubernetes-sigs/aws-iam-authenticator/releases/download/${AWS_IAM_AUTHENTICATOR_VERSION}/aws-iam-authenticator_${AWS_IAM_AUTHENTICATOR_VERSION}_linux_amd64 && \
|
||||
chmod +x /usr/local/bin/aws-iam-authenticator
|
||||
RUN ./install.sh dep-linux
|
||||
RUN ./install.sh packr-linux
|
||||
RUN ./install.sh kubectl-linux
|
||||
RUN ./install.sh ksonnet-linux
|
||||
RUN ./install.sh helm-linux
|
||||
RUN ./install.sh kustomize-linux
|
||||
RUN ./install.sh aws-iam-authenticator-linux
|
||||
|
||||
####################################################################################################
|
||||
# Argo CD Base - used as the base for both the release and dev argocd images
|
||||
|
||||
94
Gopkg.lock
generated
94
Gopkg.lock
generated
@@ -135,6 +135,17 @@
|
||||
revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e"
|
||||
version = "v3.2.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:c05f1899f086e3b4613d94d9e6f7ba6f4b6587498a1aa6037c5c294b22f5a743"
|
||||
name = "github.com/docker/distribution"
|
||||
packages = [
|
||||
"digestset",
|
||||
"reference",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "2461543d988979529609e8cb6fca9ca190dc48da"
|
||||
version = "v2.7.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:b021ef379356343bdc13ec101e546b756fcef4b1186d08163bef7d3bc8c1e07f"
|
||||
name = "github.com/docker/docker"
|
||||
@@ -651,6 +662,14 @@
|
||||
revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd"
|
||||
version = "1.0.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:5d9b668b0b4581a978f07e7d2e3314af18eb27b3fb5d19b70185b7c575723d11"
|
||||
name = "github.com/opencontainers/go-digest"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "279bed98673dd5bef374d3b6e4b09e2af76183bf"
|
||||
version = "v1.0.0-rc1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:4c0404dc03d974acd5fcd8b8d3ce687b13bd169db032b89275e8b9d77b98ce8c"
|
||||
name = "github.com/patrickmn/go-cache"
|
||||
@@ -1548,15 +1567,74 @@
|
||||
digest = "1:78aa6079e011ece0d28513c7fe1bd64284fa9eb5d671760803a839ffdf0e9e38"
|
||||
name = "k8s.io/kubernetes"
|
||||
packages = [
|
||||
"pkg/api/legacyscheme",
|
||||
"pkg/api/v1/pod",
|
||||
"pkg/apis/apps",
|
||||
"pkg/apis/apps/install",
|
||||
"pkg/apis/apps/v1",
|
||||
"pkg/apis/apps/v1beta1",
|
||||
"pkg/apis/apps/v1beta2",
|
||||
"pkg/apis/authentication",
|
||||
"pkg/apis/authentication/install",
|
||||
"pkg/apis/authentication/v1",
|
||||
"pkg/apis/authentication/v1beta1",
|
||||
"pkg/apis/authorization",
|
||||
"pkg/apis/authorization/install",
|
||||
"pkg/apis/authorization/v1",
|
||||
"pkg/apis/authorization/v1beta1",
|
||||
"pkg/apis/autoscaling",
|
||||
"pkg/apis/autoscaling/install",
|
||||
"pkg/apis/autoscaling/v1",
|
||||
"pkg/apis/autoscaling/v2beta1",
|
||||
"pkg/apis/autoscaling/v2beta2",
|
||||
"pkg/apis/batch",
|
||||
"pkg/apis/batch/install",
|
||||
"pkg/apis/batch/v1",
|
||||
"pkg/apis/batch/v1beta1",
|
||||
"pkg/apis/batch/v2alpha1",
|
||||
"pkg/apis/certificates",
|
||||
"pkg/apis/certificates/install",
|
||||
"pkg/apis/certificates/v1beta1",
|
||||
"pkg/apis/coordination",
|
||||
"pkg/apis/coordination/install",
|
||||
"pkg/apis/coordination/v1",
|
||||
"pkg/apis/coordination/v1beta1",
|
||||
"pkg/apis/core",
|
||||
"pkg/apis/core/install",
|
||||
"pkg/apis/core/v1",
|
||||
"pkg/apis/events",
|
||||
"pkg/apis/events/install",
|
||||
"pkg/apis/events/v1beta1",
|
||||
"pkg/apis/extensions",
|
||||
"pkg/apis/extensions/install",
|
||||
"pkg/apis/extensions/v1beta1",
|
||||
"pkg/apis/networking",
|
||||
"pkg/apis/policy",
|
||||
"pkg/apis/policy/install",
|
||||
"pkg/apis/policy/v1beta1",
|
||||
"pkg/apis/rbac",
|
||||
"pkg/apis/rbac/install",
|
||||
"pkg/apis/rbac/v1",
|
||||
"pkg/apis/rbac/v1alpha1",
|
||||
"pkg/apis/rbac/v1beta1",
|
||||
"pkg/apis/scheduling",
|
||||
"pkg/apis/scheduling/install",
|
||||
"pkg/apis/scheduling/v1",
|
||||
"pkg/apis/scheduling/v1alpha1",
|
||||
"pkg/apis/scheduling/v1beta1",
|
||||
"pkg/apis/settings",
|
||||
"pkg/apis/settings/install",
|
||||
"pkg/apis/settings/v1alpha1",
|
||||
"pkg/apis/storage",
|
||||
"pkg/apis/storage/install",
|
||||
"pkg/apis/storage/v1",
|
||||
"pkg/apis/storage/v1alpha1",
|
||||
"pkg/apis/storage/v1beta1",
|
||||
"pkg/kubectl/scheme",
|
||||
"pkg/kubectl/util/term",
|
||||
"pkg/util/interrupt",
|
||||
"pkg/util/node",
|
||||
"pkg/util/parsers",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "2d20b5759406ded89f8b25cf085ff4733b144ba5"
|
||||
@@ -1733,10 +1811,26 @@
|
||||
"k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1",
|
||||
"k8s.io/kube-openapi/cmd/openapi-gen",
|
||||
"k8s.io/kube-openapi/pkg/common",
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme",
|
||||
"k8s.io/kubernetes/pkg/api/v1/pod",
|
||||
"k8s.io/kubernetes/pkg/apis/apps",
|
||||
"k8s.io/kubernetes/pkg/apis/apps/install",
|
||||
"k8s.io/kubernetes/pkg/apis/authentication/install",
|
||||
"k8s.io/kubernetes/pkg/apis/authorization/install",
|
||||
"k8s.io/kubernetes/pkg/apis/autoscaling/install",
|
||||
"k8s.io/kubernetes/pkg/apis/batch",
|
||||
"k8s.io/kubernetes/pkg/apis/batch/install",
|
||||
"k8s.io/kubernetes/pkg/apis/certificates/install",
|
||||
"k8s.io/kubernetes/pkg/apis/coordination/install",
|
||||
"k8s.io/kubernetes/pkg/apis/core",
|
||||
"k8s.io/kubernetes/pkg/apis/core/install",
|
||||
"k8s.io/kubernetes/pkg/apis/events/install",
|
||||
"k8s.io/kubernetes/pkg/apis/extensions/install",
|
||||
"k8s.io/kubernetes/pkg/apis/policy/install",
|
||||
"k8s.io/kubernetes/pkg/apis/rbac/install",
|
||||
"k8s.io/kubernetes/pkg/apis/scheduling/install",
|
||||
"k8s.io/kubernetes/pkg/apis/settings/install",
|
||||
"k8s.io/kubernetes/pkg/apis/storage/install",
|
||||
"k8s.io/kubernetes/pkg/kubectl/scheme",
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/term",
|
||||
"k8s.io/kubernetes/pkg/util/node",
|
||||
|
||||
@@ -77,7 +77,7 @@ func newCommand() *cobra.Command {
|
||||
errors.CheckError(err)
|
||||
|
||||
settingsMgr := settings.NewSettingsManager(ctx, kubeClient, namespace)
|
||||
kubectl := kube.KubectlCmd{}
|
||||
kubectl := &kube.KubectlCmd{}
|
||||
appController, err := controller.NewApplicationController(
|
||||
namespace,
|
||||
settingsMgr,
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"syscall"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
@@ -73,6 +74,7 @@ func NewCommand() *cobra.Command {
|
||||
command.AddCommand(NewImportCommand())
|
||||
command.AddCommand(NewExportCommand())
|
||||
command.AddCommand(NewClusterConfig())
|
||||
command.AddCommand(NewProjectsCommand())
|
||||
|
||||
command.Flags().StringVar(&logLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
|
||||
return command
|
||||
@@ -108,7 +110,7 @@ func NewRunDexCommand() *cobra.Command {
|
||||
} else {
|
||||
err = ioutil.WriteFile("/tmp/dex.yaml", dexCfgBytes, 0644)
|
||||
errors.CheckError(err)
|
||||
log.Info(string(dexCfgBytes))
|
||||
log.Info(redactor(string(dexCfgBytes)))
|
||||
cmd = exec.Command("dex", "serve", "/tmp/dex.yaml")
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
@@ -219,6 +221,7 @@ func NewImportCommand() *cobra.Command {
|
||||
os.Exit(1)
|
||||
}
|
||||
config, err := clientConfig.ClientConfig()
|
||||
errors.CheckError(err)
|
||||
config.QPS = 100
|
||||
config.Burst = 50
|
||||
errors.CheckError(err)
|
||||
@@ -532,6 +535,11 @@ func NewClusterConfig() *cobra.Command {
|
||||
return command
|
||||
}
|
||||
|
||||
func redactor(dirtyString string) string {
|
||||
dirtyString = regexp.MustCompile("(clientSecret: )[^ \n]*").ReplaceAllString(dirtyString, "$1********")
|
||||
return regexp.MustCompile("(secret: )[^ \n]*").ReplaceAllString(dirtyString, "$1********")
|
||||
}
|
||||
|
||||
func main() {
|
||||
if err := NewCommand().Execute(); err != nil {
|
||||
fmt.Println(err)
|
||||
|
||||
73
cmd/argocd-util/main_test.go
Normal file
73
cmd/argocd-util/main_test.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var textToRedact = `
|
||||
- config:
|
||||
clientID: aabbccddeeff00112233
|
||||
clientSecret: $dex.github.clientSecret
|
||||
orgs:
|
||||
- name: your-github-org
|
||||
redirectURI: https://argocd.example.com/api/dex/callback
|
||||
id: github
|
||||
name: GitHub
|
||||
type: github
|
||||
grpc:
|
||||
addr: 0.0.0.0:5557
|
||||
issuer: https://argocd.example.com/api/dex
|
||||
oauth2:
|
||||
skipApprovalScreen: true
|
||||
staticClients:
|
||||
- id: argo-cd
|
||||
name: Argo CD
|
||||
redirectURIs:
|
||||
- https://argocd.example.com/auth/callback
|
||||
secret: Dis9M-GA11oTwZVQQWdDklPQw-sWXZkWJFyyEhMs
|
||||
- id: argo-cd-cli
|
||||
name: Argo CD CLI
|
||||
public: true
|
||||
redirectURIs:
|
||||
- http://localhost
|
||||
storage:
|
||||
type: memory
|
||||
web:
|
||||
http: 0.0.0.0:5556`
|
||||
|
||||
var expectedRedaction = `
|
||||
- config:
|
||||
clientID: aabbccddeeff00112233
|
||||
clientSecret: ********
|
||||
orgs:
|
||||
- name: your-github-org
|
||||
redirectURI: https://argocd.example.com/api/dex/callback
|
||||
id: github
|
||||
name: GitHub
|
||||
type: github
|
||||
grpc:
|
||||
addr: 0.0.0.0:5557
|
||||
issuer: https://argocd.example.com/api/dex
|
||||
oauth2:
|
||||
skipApprovalScreen: true
|
||||
staticClients:
|
||||
- id: argo-cd
|
||||
name: Argo CD
|
||||
redirectURIs:
|
||||
- https://argocd.example.com/auth/callback
|
||||
secret: ********
|
||||
- id: argo-cd-cli
|
||||
name: Argo CD CLI
|
||||
public: true
|
||||
redirectURIs:
|
||||
- http://localhost
|
||||
storage:
|
||||
type: memory
|
||||
web:
|
||||
http: 0.0.0.0:5556`
|
||||
|
||||
func TestSecretsRedactor(t *testing.T) {
|
||||
assert.Equal(t, expectedRedaction, redactor(textToRedact))
|
||||
}
|
||||
192
cmd/argocd-util/projects.go
Normal file
192
cmd/argocd-util/projects.go
Normal file
@@ -0,0 +1,192 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
|
||||
appclient "github.com/argoproj/argo-cd/pkg/client/clientset/versioned/typed/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/diff"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
func NewProjectsCommand() *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "projects",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
c.HelpFunc()(c, args)
|
||||
},
|
||||
}
|
||||
|
||||
command.AddCommand(NewUpdatePolicyRuleCommand())
|
||||
return command
|
||||
}
|
||||
|
||||
func globMatch(pattern string, val string) bool {
|
||||
if pattern == "*" {
|
||||
return true
|
||||
}
|
||||
if ok, err := filepath.Match(pattern, val); ok && err == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getModification(modification string, resource string, scope string, permission string) (func(string, string) string, error) {
|
||||
switch modification {
|
||||
case "set":
|
||||
if scope == "" {
|
||||
return nil, fmt.Errorf("Flag --group cannot be empty if permission should be set in role")
|
||||
}
|
||||
if permission == "" {
|
||||
return nil, fmt.Errorf("Flag --permission cannot be empty if permission should be set in role")
|
||||
}
|
||||
return func(proj string, action string) string {
|
||||
return fmt.Sprintf("%s, %s, %s/%s, %s", resource, action, proj, scope, permission)
|
||||
}, nil
|
||||
case "remove":
|
||||
return func(proj string, action string) string {
|
||||
return ""
|
||||
}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("modification %s is not supported", modification)
|
||||
}
|
||||
|
||||
func saveProject(updated v1alpha1.AppProject, orig v1alpha1.AppProject, projectsIf appclient.AppProjectInterface, dryRun bool) error {
|
||||
fmt.Printf("===== %s ======\n", updated.Name)
|
||||
target, err := kube.ToUnstructured(&updated)
|
||||
errors.CheckError(err)
|
||||
live, err := kube.ToUnstructured(&orig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = diff.PrintDiff(updated.Name, target, live)
|
||||
if !dryRun {
|
||||
_, err = projectsIf.Update(&updated)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func formatPolicy(proj string, role string, permission string) string {
|
||||
return fmt.Sprintf("p, proj:%s:%s, %s", proj, role, permission)
|
||||
}
|
||||
|
||||
func split(input string, delimiter string) []string {
|
||||
parts := strings.Split(input, delimiter)
|
||||
for i := range parts {
|
||||
parts[i] = strings.TrimSpace(parts[i])
|
||||
}
|
||||
return parts
|
||||
}
|
||||
|
||||
func NewUpdatePolicyRuleCommand() *cobra.Command {
|
||||
var (
|
||||
clientConfig clientcmd.ClientConfig
|
||||
resource string
|
||||
scope string
|
||||
rolePattern string
|
||||
permission string
|
||||
dryRun bool
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "update-role-policy PROJECT_GLOB MODIFICATION ACTION",
|
||||
Short: "Implement bulk project role update. Useful to back-fill existing project policies or remove obsolete actions.",
|
||||
Example: ` # Add policy that allows executing any action (action/*) to roles which name matches to *deployer* in all projects
|
||||
argocd-util projects update-role-policy '*' set 'action/*' --role '*deployer*' --resource applications --scope '*' --permission allow
|
||||
|
||||
# Remove policy that which manages running (action/*) from all roles which name matches *deployer* in all projects
|
||||
argocd-util projects update-role-policy '*' remove override --role '*deployer*'
|
||||
`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) != 3 {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
projectGlob := args[0]
|
||||
modificationType := args[1]
|
||||
action := args[2]
|
||||
|
||||
config, err := clientConfig.ClientConfig()
|
||||
errors.CheckError(err)
|
||||
config.QPS = 100
|
||||
config.Burst = 50
|
||||
|
||||
namespace, _, err := clientConfig.Namespace()
|
||||
errors.CheckError(err)
|
||||
appclients := appclientset.NewForConfigOrDie(config)
|
||||
|
||||
modification, err := getModification(modificationType, resource, scope, permission)
|
||||
errors.CheckError(err)
|
||||
projIf := appclients.ArgoprojV1alpha1().AppProjects(namespace)
|
||||
|
||||
err = updateProjects(projIf, projectGlob, rolePattern, action, modification, dryRun)
|
||||
errors.CheckError(err)
|
||||
},
|
||||
}
|
||||
command.Flags().StringVar(&resource, "resource", "", "Resource e.g. 'applications'")
|
||||
command.Flags().StringVar(&scope, "scope", "", "Resource scope e.g. '*'")
|
||||
command.Flags().StringVar(&rolePattern, "role", "*", "Role name pattern e.g. '*deployer*'")
|
||||
command.Flags().StringVar(&permission, "permission", "", "Action permission")
|
||||
command.Flags().BoolVar(&dryRun, "dry-run", true, "Dry run")
|
||||
clientConfig = cli.AddKubectlFlagsToCmd(command)
|
||||
return command
|
||||
}
|
||||
|
||||
func updateProjects(projIf appclient.AppProjectInterface, projectGlob string, rolePattern string, action string, modification func(string, string) string, dryRun bool) error {
|
||||
projects, err := projIf.List(v1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, proj := range projects.Items {
|
||||
if !globMatch(projectGlob, proj.Name) {
|
||||
continue
|
||||
}
|
||||
origProj := proj.DeepCopy()
|
||||
updated := false
|
||||
for i, role := range proj.Spec.Roles {
|
||||
if !globMatch(rolePattern, role.Name) {
|
||||
continue
|
||||
}
|
||||
actionPolicyIndex := -1
|
||||
for i := range role.Policies {
|
||||
parts := split(role.Policies[i], ",")
|
||||
if len(parts) != 6 || parts[3] != action {
|
||||
continue
|
||||
}
|
||||
actionPolicyIndex = i
|
||||
break
|
||||
}
|
||||
policyPermission := modification(proj.Name, action)
|
||||
if actionPolicyIndex == -1 && policyPermission != "" {
|
||||
updated = true
|
||||
role.Policies = append(role.Policies, formatPolicy(proj.Name, role.Name, policyPermission))
|
||||
} else if actionPolicyIndex > -1 && policyPermission == "" {
|
||||
updated = true
|
||||
role.Policies = append(role.Policies[:actionPolicyIndex], role.Policies[actionPolicyIndex+1:]...)
|
||||
} else if actionPolicyIndex > -1 && policyPermission != "" {
|
||||
updated = true
|
||||
role.Policies[actionPolicyIndex] = formatPolicy(proj.Name, role.Name, policyPermission)
|
||||
}
|
||||
proj.Spec.Roles[i] = role
|
||||
}
|
||||
if updated {
|
||||
err = saveProject(proj, *origProj, projIf, dryRun)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
78
cmd/argocd-util/projects_test.go
Normal file
78
cmd/argocd-util/projects_test.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/pkg/client/clientset/versioned/fake"
|
||||
)
|
||||
|
||||
const (
|
||||
namespace = "default"
|
||||
)
|
||||
|
||||
func newProj(name string, roleNames ...string) *v1alpha1.AppProject {
|
||||
var roles []v1alpha1.ProjectRole
|
||||
for i := range roleNames {
|
||||
roles = append(roles, v1alpha1.ProjectRole{Name: roleNames[i]})
|
||||
}
|
||||
return &v1alpha1.AppProject{ObjectMeta: v1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
}, Spec: v1alpha1.AppProjectSpec{
|
||||
Roles: roles,
|
||||
}}
|
||||
}
|
||||
|
||||
func TestUpdateProjects_FindMatchingProject(t *testing.T) {
|
||||
clientset := fake.NewSimpleClientset(newProj("foo", "test"), newProj("bar", "test"))
|
||||
|
||||
modification, err := getModification("set", "*", "*", "allow")
|
||||
assert.NoError(t, err)
|
||||
err = updateProjects(clientset.ArgoprojV1alpha1().AppProjects(namespace), "ba*", "*", "set", modification, false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
fooProj, err := clientset.ArgoprojV1alpha1().AppProjects(namespace).Get("foo", v1.GetOptions{})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, fooProj.Spec.Roles[0].Policies, 0)
|
||||
|
||||
barProj, err := clientset.ArgoprojV1alpha1().AppProjects(namespace).Get("bar", v1.GetOptions{})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, barProj.Spec.Roles[0].Policies, []string{"p, proj:bar:test, *, set, bar/*, allow"})
|
||||
}
|
||||
|
||||
func TestUpdateProjects_FindMatchingRole(t *testing.T) {
|
||||
clientset := fake.NewSimpleClientset(newProj("proj", "foo", "bar"))
|
||||
|
||||
modification, err := getModification("set", "*", "*", "allow")
|
||||
assert.NoError(t, err)
|
||||
err = updateProjects(clientset.ArgoprojV1alpha1().AppProjects(namespace), "*", "fo*", "set", modification, false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
proj, err := clientset.ArgoprojV1alpha1().AppProjects(namespace).Get("proj", v1.GetOptions{})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, proj.Spec.Roles[0].Policies, []string{"p, proj:proj:foo, *, set, proj/*, allow"})
|
||||
assert.Len(t, proj.Spec.Roles[1].Policies, 0)
|
||||
}
|
||||
|
||||
func TestGetModification_SetPolicy(t *testing.T) {
|
||||
modification, err := getModification("set", "*", "*", "allow")
|
||||
assert.NoError(t, err)
|
||||
policy := modification("proj", "myaction")
|
||||
assert.Equal(t, "*, myaction, proj/*, allow", policy)
|
||||
}
|
||||
|
||||
func TestGetModification_RemovePolicy(t *testing.T) {
|
||||
modification, err := getModification("remove", "*", "*", "allow")
|
||||
assert.NoError(t, err)
|
||||
policy := modification("proj", "myaction")
|
||||
assert.Equal(t, "", policy)
|
||||
}
|
||||
|
||||
func TestGetModification_NotSupported(t *testing.T) {
|
||||
_, err := getModification("bar", "*", "*", "allow")
|
||||
assert.Errorf(t, err, "modification bar is not supported")
|
||||
}
|
||||
@@ -784,7 +784,7 @@ func getLocalObjects(app *argoappv1.Application, local, appLabelKey, kubeVersion
|
||||
}
|
||||
|
||||
func getLocalObjectsString(app *argoappv1.Application, local, appLabelKey, kubeVersion string, kustomizeOptions *argoappv1.KustomizeOptions) []string {
|
||||
res, err := repository.GenerateManifests(local, &repoapiclient.ManifestRequest{
|
||||
res, err := repository.GenerateManifests(local, "/", &repoapiclient.ManifestRequest{
|
||||
ApplicationSource: &app.Spec.Source,
|
||||
AppLabelKey: appLabelKey,
|
||||
AppLabelValue: app.Name,
|
||||
@@ -972,8 +972,7 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
}
|
||||
|
||||
foundDiffs = true
|
||||
err = diff.PrintDiff(item.key.Name, target, live)
|
||||
errors.CheckError(err)
|
||||
_ = diff.PrintDiff(item.key.Name, target, live)
|
||||
}
|
||||
}
|
||||
if foundDiffs {
|
||||
@@ -1298,7 +1297,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
cluster, err := clusterIf.Get(context.Background(), &clusterpkg.ClusterQuery{Server: app.Spec.Destination.Server})
|
||||
errors.CheckError(err)
|
||||
util.Close(conn)
|
||||
localObjsStrings = getLocalObjectsString(app, local, cluster.ServerVersion, argoSettings.AppLabelKey, argoSettings.KustomizeOptions)
|
||||
localObjsStrings = getLocalObjectsString(app, local, argoSettings.AppLabelKey, cluster.ServerVersion, argoSettings.KustomizeOptions)
|
||||
}
|
||||
|
||||
syncReq := applicationpkg.ApplicationSyncRequest{
|
||||
@@ -1962,7 +1961,7 @@ func filterResources(command *cobra.Command, resources []*argoappv1.ResourceDiff
|
||||
if resourceName != "" && resourceName != obj.GetName() {
|
||||
continue
|
||||
}
|
||||
if kind != "" && kind != gvk.Kind {
|
||||
if kind != gvk.Kind {
|
||||
continue
|
||||
}
|
||||
copy := obj.DeepCopy()
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
@@ -121,7 +120,8 @@ func NewApplicationResourceActionsListCommand(clientOpts *argocdclient.ClientOpt
|
||||
func NewApplicationResourceActionsRunCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var namespace string
|
||||
var resourceName string
|
||||
var kindArg string
|
||||
var kind string
|
||||
var group string
|
||||
var all bool
|
||||
var command = &cobra.Command{
|
||||
Use: "run APPNAME ACTION",
|
||||
@@ -130,7 +130,9 @@ func NewApplicationResourceActionsRunCommand(clientOpts *argocdclient.ClientOpti
|
||||
|
||||
command.Flags().StringVar(&resourceName, "resource-name", "", "Name of resource")
|
||||
command.Flags().StringVar(&namespace, "namespace", "", "Namespace")
|
||||
command.Flags().StringVar(&kindArg, "kind", "", "Kind")
|
||||
command.Flags().StringVar(&kind, "kind", "", "Kind")
|
||||
command.Flags().StringVar(&group, "group", "", "Group")
|
||||
errors.CheckError(command.MarkFlagRequired("kind"))
|
||||
command.Flags().BoolVar(&all, "all", false, "Indicates whether to run the action on multiple matching resources")
|
||||
|
||||
command.Run = func(c *cobra.Command, args []string) {
|
||||
@@ -146,31 +148,14 @@ func NewApplicationResourceActionsRunCommand(clientOpts *argocdclient.ClientOpti
|
||||
ctx := context.Background()
|
||||
resources, err := appIf.ManagedResources(ctx, &applicationpkg.ResourcesQuery{ApplicationName: &appName})
|
||||
errors.CheckError(err)
|
||||
|
||||
var group string
|
||||
var kind string
|
||||
var actionNameOnly string
|
||||
// Backwards comparability for running resume actions
|
||||
if actionName == "resume" && kindArg == "Rollout" {
|
||||
group = "argoproj.io"
|
||||
kind = "Rollout"
|
||||
actionNameOnly = "resume"
|
||||
commandTail := ""
|
||||
if resourceName != "" {
|
||||
commandTail += " --resource-name " + resourceName
|
||||
filteredObjects := filterResources(command, resources.Items, group, kind, namespace, resourceName, all)
|
||||
var resGroup = filteredObjects[0].GroupVersionKind().Group
|
||||
for i := range filteredObjects[1:] {
|
||||
if filteredObjects[i].GroupVersionKind().Group != resGroup {
|
||||
log.Fatal("Ambiguous resource group. Use flag --group to specify resource group explicitly.")
|
||||
}
|
||||
if namespace != "" {
|
||||
commandTail += " --namespace " + namespace
|
||||
}
|
||||
if all {
|
||||
commandTail += " --all"
|
||||
}
|
||||
fmt.Printf("\nWarning: this syntax for running the \"resume\" action has been deprecated. Please run the action as\n\n\targocd app actions run %s argoproj.io/Rollout/resume%s\n\n", appName, commandTail)
|
||||
} else {
|
||||
group, kind, actionNameOnly = parseActionName(actionName)
|
||||
}
|
||||
|
||||
filteredObjects := filterResources(command, resources.Items, group, kind, namespace, resourceName, all)
|
||||
for i := range filteredObjects {
|
||||
obj := filteredObjects[i]
|
||||
gvk := obj.GroupVersionKind()
|
||||
@@ -181,18 +166,10 @@ func NewApplicationResourceActionsRunCommand(clientOpts *argocdclient.ClientOpti
|
||||
ResourceName: objResourceName,
|
||||
Group: gvk.Group,
|
||||
Kind: gvk.Kind,
|
||||
Action: actionNameOnly,
|
||||
Action: actionName,
|
||||
})
|
||||
errors.CheckError(err)
|
||||
}
|
||||
}
|
||||
return command
|
||||
}
|
||||
|
||||
func parseActionName(action string) (string, string, string) {
|
||||
actionSplit := strings.Split(action, "/")
|
||||
if len(actionSplit) != 3 {
|
||||
log.Fatal("Action name is malformed")
|
||||
}
|
||||
return actionSplit[0], actionSplit[1], actionSplit[2]
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
repositorypkg "github.com/argoproj/argo-cd/pkg/apiclient/repository"
|
||||
@@ -50,13 +51,20 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
)
|
||||
|
||||
// For better readability and easier formatting
|
||||
var repoAddExamples = `
|
||||
Add a SSH repository using a private key for authentication, ignoring the server's host key:",
|
||||
$ argocd repo add git@git.example.com --insecure-ignore-host-key --ssh-private-key-path ~/id_rsa",
|
||||
Add a HTTPS repository using username/password and TLS client certificates:",
|
||||
$ argocd repo add https://git.example.com --username git --password secret --tls-client-cert-path ~/mycert.crt --tls-client-cert-key-path ~/mycert.key",
|
||||
Add a HTTPS repository using username/password without verifying the server's TLS certificate:",
|
||||
$ argocd repo add https://git.example.com --username git --password secret --insecure-skip-server-verification",
|
||||
var repoAddExamples = ` # Add a Git repository via SSH using a private key for authentication, ignoring the server's host key:
|
||||
argocd repo add git@git.example.com:repos/repo --insecure-ignore-host-key --ssh-private-key-path ~/id_rsa
|
||||
|
||||
# Add a private Git repository via HTTPS using username/password and TLS client certificates:
|
||||
argocd repo add https://git.example.com/repos/repo --username git --password secret --tls-client-cert-path ~/mycert.crt --tls-client-cert-key-path ~/mycert.key
|
||||
|
||||
# Add a private Git repository via HTTPS using username/password without verifying the server's TLS certificate
|
||||
argocd repo add https://git.example.com/repos/repo --username git --password secret --insecure-skip-server-verification
|
||||
|
||||
# Add a public Helm repository named 'stable' via HTTPS
|
||||
argocd repo add https://kubernetes-charts.storage.googleapis.com --type helm --name stable
|
||||
|
||||
# Add a private Helm repository named 'stable' via HTTPS
|
||||
argocd repo add https://kubernetes-charts.storage.googleapis.com --type helm --name stable --username test --password test
|
||||
`
|
||||
|
||||
var command = &cobra.Command{
|
||||
@@ -113,6 +121,10 @@ Add a HTTPS repository using username/password without verifying the server's TL
|
||||
repo.Insecure = insecureSkipServerVerification
|
||||
repo.EnableLFS = enableLfs
|
||||
|
||||
if repo.Type == "helm" && repo.Name == "" {
|
||||
errors.CheckError(fmt.Errorf("Must specify --name for repos of type 'helm'"))
|
||||
}
|
||||
|
||||
conn, repoIf := argocdclient.NewClientOrDie(clientOpts).NewRepoClientOrDie()
|
||||
defer util.Close(conn)
|
||||
|
||||
@@ -148,8 +160,8 @@ Add a HTTPS repository using username/password without verifying the server's TL
|
||||
fmt.Printf("repository '%s' added\n", createdRepo.Repo)
|
||||
},
|
||||
}
|
||||
command.Flags().StringVar(&repo.Type, "type", "", "type of the repository, \"git\" or \"helm\"")
|
||||
command.Flags().StringVar(&repo.Name, "name", "", "name of the repository")
|
||||
command.Flags().StringVar(&repo.Type, "type", common.DefaultRepoType, "type of the repository, \"git\" or \"helm\"")
|
||||
command.Flags().StringVar(&repo.Name, "name", "", "name of the repository, mandatory for repositories of type helm")
|
||||
command.Flags().StringVar(&repo.Username, "username", "", "username to the repository")
|
||||
command.Flags().StringVar(&repo.Password, "password", "", "password to the repository")
|
||||
command.Flags().StringVar(&sshPrivateKeyPath, "ssh-private-key-path", "", "path to the private ssh key (e.g. ~/.ssh/id_rsa)")
|
||||
|
||||
@@ -21,9 +21,10 @@ const (
|
||||
ArgoCDTLSCertsConfigMapName = "argocd-tls-certs-cm"
|
||||
)
|
||||
|
||||
// Default system namespace
|
||||
// Some default configurables
|
||||
const (
|
||||
DefaultSystemNamespace = "kube-system"
|
||||
DefaultRepoType = "git"
|
||||
)
|
||||
|
||||
// Default listener ports for ArgoCD components
|
||||
|
||||
@@ -136,7 +136,8 @@ func NewApplicationController(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
projInformer := v1alpha1.NewAppProjectInformer(applicationClientset, namespace, appResyncPeriod, cache.Indexers{})
|
||||
indexers := cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}
|
||||
projInformer := v1alpha1.NewAppProjectInformer(applicationClientset, namespace, appResyncPeriod, indexers)
|
||||
metricsAddr := fmt.Sprintf("0.0.0.0:%d", metricsPort)
|
||||
ctrl.metricsServer = metrics.NewMetricsServer(metricsAddr, appLister, func() error {
|
||||
_, err := kubeClientset.Discovery().ServerVersion()
|
||||
@@ -803,6 +804,7 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
|
||||
revision = app.Status.Sync.Revision
|
||||
}
|
||||
|
||||
observedAt := metav1.Now()
|
||||
compareResult := ctrl.appStateManager.CompareAppState(app, revision, app.Spec.Source, refreshType == appv1.RefreshTypeHard, localManifests)
|
||||
|
||||
ctrl.normalizeApplication(origApp, app)
|
||||
@@ -830,8 +832,10 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
|
||||
}
|
||||
}
|
||||
|
||||
app.Status.ObservedAt = &compareResult.reconciledAt
|
||||
app.Status.ReconciledAt = &compareResult.reconciledAt
|
||||
if app.Status.ReconciledAt == nil || comparisonLevel == CompareWithLatest {
|
||||
app.Status.ReconciledAt = &observedAt
|
||||
}
|
||||
app.Status.ObservedAt = &observedAt
|
||||
app.Status.Sync = *compareResult.syncStatus
|
||||
app.Status.Health = *compareResult.healthStatus
|
||||
app.Status.Resources = compareResult.resources
|
||||
@@ -850,23 +854,22 @@ func (ctrl *ApplicationController) needRefreshAppStatus(app *appv1.Application,
|
||||
compareWith := CompareWithLatest
|
||||
refreshType := appv1.RefreshTypeNormal
|
||||
expired := app.Status.ReconciledAt == nil || app.Status.ReconciledAt.Add(statusRefreshTimeout).Before(time.Now().UTC())
|
||||
if requestedType, ok := app.IsRefreshRequested(); ok || expired {
|
||||
if ok {
|
||||
refreshType = requestedType
|
||||
reason = fmt.Sprintf("%s refresh requested", refreshType)
|
||||
} else if expired {
|
||||
reason = fmt.Sprintf("comparison expired. reconciledAt: %v, expiry: %v", app.Status.ReconciledAt, statusRefreshTimeout)
|
||||
}
|
||||
} else if requested, level := ctrl.isRefreshRequested(app.Name); requested {
|
||||
compareWith = level
|
||||
reason = fmt.Sprintf("controller refresh requested")
|
||||
} else if app.Status.Sync.Status == appv1.SyncStatusCodeUnknown && expired {
|
||||
reason = "comparison status unknown"
|
||||
|
||||
if requestedType, ok := app.IsRefreshRequested(); ok {
|
||||
// user requested app refresh.
|
||||
refreshType = requestedType
|
||||
reason = fmt.Sprintf("%s refresh requested", refreshType)
|
||||
} else if expired {
|
||||
reason = fmt.Sprintf("comparison expired. reconciledAt: %v, expiry: %v", app.Status.ReconciledAt, statusRefreshTimeout)
|
||||
} else if !app.Spec.Source.Equals(app.Status.Sync.ComparedTo.Source) {
|
||||
reason = "spec.source differs"
|
||||
} else if !app.Spec.Destination.Equals(app.Status.Sync.ComparedTo.Destination) {
|
||||
reason = "spec.destination differs"
|
||||
} else if requested, level := ctrl.isRefreshRequested(app.Name); requested {
|
||||
compareWith = level
|
||||
reason = fmt.Sprintf("controller refresh requested")
|
||||
}
|
||||
|
||||
if reason != "" {
|
||||
logCtx.Infof("Refreshing app status (%s), level (%d)", reason, compareWith)
|
||||
return true, refreshType, compareWith
|
||||
|
||||
@@ -2,6 +2,7 @@ package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -106,6 +107,7 @@ func newFakeController(data *fakeData) *ApplicationController {
|
||||
ctrl.stateCache = &mockStateCache
|
||||
mockStateCache.On("IsNamespaced", mock.Anything, mock.Anything).Return(true, nil)
|
||||
mockStateCache.On("GetManagedLiveObjs", mock.Anything, mock.Anything).Return(data.managedLiveObjs, nil)
|
||||
mockStateCache.On("GetServerVersion", mock.Anything).Return("v1.2.3", nil)
|
||||
response := make(map[kube.ResourceKey]argoappv1.ResourceNode)
|
||||
for k, v := range data.namespacedResources {
|
||||
response[k] = v.ResourceNode
|
||||
@@ -597,6 +599,24 @@ func TestNeedRefreshAppStatus(t *testing.T) {
|
||||
assert.Equal(t, argoappv1.RefreshTypeHard, refreshType)
|
||||
assert.Equal(t, CompareWithLatest, compareWith)
|
||||
}
|
||||
|
||||
{
|
||||
app := app.DeepCopy()
|
||||
// ensure that CompareWithLatest level is used if application source has changed
|
||||
ctrl.requestAppRefresh(app.Name, ComparisonWithNothing)
|
||||
// sample app source change
|
||||
app.Spec.Source.Helm = &argoappv1.ApplicationSourceHelm{
|
||||
Parameters: []argoappv1.HelmParameter{{
|
||||
Name: "foo",
|
||||
Value: "bar",
|
||||
}},
|
||||
}
|
||||
|
||||
needRefresh, refreshType, compareWith = ctrl.needRefreshAppStatus(app, 1*time.Hour)
|
||||
assert.True(t, needRefresh)
|
||||
assert.Equal(t, argoappv1.RefreshTypeNormal, refreshType)
|
||||
assert.Equal(t, CompareWithLatest, compareWith)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRefreshAppConditions(t *testing.T) {
|
||||
@@ -651,3 +671,63 @@ func TestRefreshAppConditions(t *testing.T) {
|
||||
assert.Equal(t, "Application referencing project wrong project which does not exist", app.Status.Conditions[0].Message)
|
||||
})
|
||||
}
|
||||
|
||||
func TestUpdateReconciledAt(t *testing.T) {
|
||||
app := newFakeApp()
|
||||
reconciledAt := metav1.NewTime(time.Now().Add(-1 * time.Second))
|
||||
app.Status = argoappv1.ApplicationStatus{ReconciledAt: &reconciledAt}
|
||||
app.Status.Sync = argoappv1.SyncStatus{ComparedTo: argoappv1.ComparedTo{Source: app.Spec.Source, Destination: app.Spec.Destination}}
|
||||
ctrl := newFakeController(&fakeData{
|
||||
apps: []runtime.Object{app, &defaultProj},
|
||||
manifestResponse: &apiclient.ManifestResponse{
|
||||
Manifests: []string{},
|
||||
Namespace: test.FakeDestNamespace,
|
||||
Server: test.FakeClusterURL,
|
||||
Revision: "abc123",
|
||||
},
|
||||
managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
|
||||
})
|
||||
key, _ := cache.MetaNamespaceKeyFunc(app)
|
||||
fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset)
|
||||
fakeAppCs.ReactionChain = nil
|
||||
receivedPatch := map[string]interface{}{}
|
||||
fakeAppCs.AddReactor("patch", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) {
|
||||
if patchAction, ok := action.(kubetesting.PatchAction); ok {
|
||||
assert.NoError(t, json.Unmarshal(patchAction.GetPatch(), &receivedPatch))
|
||||
}
|
||||
return true, nil, nil
|
||||
})
|
||||
|
||||
t.Run("UpdatedOnFullReconciliation", func(t *testing.T) {
|
||||
receivedPatch = map[string]interface{}{}
|
||||
ctrl.requestAppRefresh(app.Name, CompareWithLatest)
|
||||
ctrl.appRefreshQueue.Add(key)
|
||||
|
||||
ctrl.processAppRefreshQueueItem()
|
||||
|
||||
_, updated, err := unstructured.NestedString(receivedPatch, "status", "reconciledAt")
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, updated)
|
||||
|
||||
_, updated, err = unstructured.NestedString(receivedPatch, "status", "observedAt")
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, updated)
|
||||
})
|
||||
|
||||
t.Run("NotUpdatedOnPartialReconciliation", func(t *testing.T) {
|
||||
receivedPatch = map[string]interface{}{}
|
||||
ctrl.appRefreshQueue.Add(key)
|
||||
ctrl.requestAppRefresh(app.Name, CompareWithRecent)
|
||||
|
||||
ctrl.processAppRefreshQueueItem()
|
||||
|
||||
_, updated, err := unstructured.NestedString(receivedPatch, "status", "reconciledAt")
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, updated)
|
||||
|
||||
_, updated, err = unstructured.NestedString(receivedPatch, "status", "observedAt")
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, updated)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
10
controller/cache/cache.go
vendored
10
controller/cache/cache.go
vendored
@@ -27,6 +27,9 @@ type cacheSettings struct {
|
||||
}
|
||||
|
||||
type LiveStateCache interface {
|
||||
// Returns k8s server version
|
||||
GetServerVersion(serverURL string) (string, error)
|
||||
// Returns true of given group kind is a namespaced resource
|
||||
IsNamespaced(server string, gk schema.GroupKind) (bool, error)
|
||||
// Executes give callback against resource specified by the key and all its children
|
||||
IterateHierarchy(server string, key kube.ResourceKey, action func(child appv1.ResourceNode, appName string)) error
|
||||
@@ -187,6 +190,13 @@ func (c *liveStateCache) GetManagedLiveObjs(a *appv1.Application, targetObjs []*
|
||||
}
|
||||
return clusterInfo.getManagedLiveObjs(a, targetObjs, c.metricsServer)
|
||||
}
|
||||
func (c *liveStateCache) GetServerVersion(serverURL string) (string, error) {
|
||||
clusterInfo, err := c.getSyncedCluster(serverURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return clusterInfo.serverVersion, nil
|
||||
}
|
||||
|
||||
func isClusterHasApps(apps []interface{}, cluster *appv1.Cluster) bool {
|
||||
for _, obj := range apps {
|
||||
|
||||
27
controller/cache/cache_test.go
vendored
Normal file
27
controller/cache/cache_test.go
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetServerVersion(t *testing.T) {
|
||||
now := time.Now()
|
||||
cache := &liveStateCache{
|
||||
lock: &sync.Mutex{},
|
||||
clusters: map[string]*clusterInfo{
|
||||
"http://localhost": {
|
||||
syncTime: &now,
|
||||
syncLock: &sync.Mutex{},
|
||||
lock: &sync.Mutex{},
|
||||
serverVersion: "123",
|
||||
},
|
||||
}}
|
||||
|
||||
version, err := cache.GetServerVersion("http://localhost")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "123", version)
|
||||
}
|
||||
18
controller/cache/cluster.go
vendored
18
controller/cache/cluster.go
vendored
@@ -39,10 +39,11 @@ type apiMeta struct {
|
||||
}
|
||||
|
||||
type clusterInfo struct {
|
||||
syncLock *sync.Mutex
|
||||
syncTime *time.Time
|
||||
syncError error
|
||||
apisMeta map[schema.GroupKind]*apiMeta
|
||||
syncLock *sync.Mutex
|
||||
syncTime *time.Time
|
||||
syncError error
|
||||
apisMeta map[schema.GroupKind]*apiMeta
|
||||
serverVersion string
|
||||
|
||||
lock *sync.Mutex
|
||||
nodes map[kube.ResourceKey]*node
|
||||
@@ -309,8 +310,13 @@ func (c *clusterInfo) sync() (err error) {
|
||||
}
|
||||
c.apisMeta = make(map[schema.GroupKind]*apiMeta)
|
||||
c.nodes = make(map[kube.ResourceKey]*node)
|
||||
|
||||
apis, err := c.kubectl.GetAPIResources(c.cluster.RESTConfig(), c.cacheSettingsSrc().ResourcesFilter)
|
||||
config := c.cluster.RESTConfig()
|
||||
version, err := c.kubectl.GetServerVersion(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.serverVersion = version
|
||||
apis, err := c.kubectl.GetAPIResources(config, c.cacheSettingsSrc().ResourcesFilter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
24
controller/cache/mocks/LiveStateCache.go
vendored
24
controller/cache/mocks/LiveStateCache.go
vendored
@@ -5,9 +5,8 @@ package mocks
|
||||
import (
|
||||
context "context"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
kube "github.com/argoproj/argo-cd/util/kube"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
@@ -67,6 +66,27 @@ func (_m *LiveStateCache) GetNamespaceTopLevelResources(server string, namespace
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetServerVersion provides a mock function with given fields: server
|
||||
func (_m *LiveStateCache) GetServerVersion(server string) (string, error) {
|
||||
ret := _m.Called(server)
|
||||
|
||||
var r0 string
|
||||
if rf, ok := ret.Get(0).(func(string) string); ok {
|
||||
r0 = rf(server)
|
||||
} else {
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||
r1 = rf(server)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Invalidate provides a mock function with given fields:
|
||||
func (_m *LiveStateCache) Invalidate() {
|
||||
_m.Called()
|
||||
|
||||
@@ -63,7 +63,6 @@ type AppStateManager interface {
|
||||
}
|
||||
|
||||
type comparisonResult struct {
|
||||
reconciledAt metav1.Time
|
||||
syncStatus *v1alpha1.SyncStatus
|
||||
healthStatus *v1alpha1.HealthStatus
|
||||
resources []v1alpha1.ResourceStatus
|
||||
@@ -129,11 +128,7 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, source v1alpha1
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
cluster, err := m.db.GetCluster(context.Background(), app.Spec.Destination.Server)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
cluster.ServerVersion, err = m.kubectl.GetServerVersion(cluster.RESTConfig())
|
||||
serverVersion, err := m.liveStateCache.GetServerVersion(app.Spec.Destination.Server)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
@@ -150,7 +145,7 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, source v1alpha1
|
||||
KustomizeOptions: &appv1.KustomizeOptions{
|
||||
BuildOptions: buildOptions,
|
||||
},
|
||||
KubeVersion: cluster.ServerVersion,
|
||||
KubeVersion: serverVersion,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
@@ -275,13 +270,11 @@ func (m *appStateManager) getComparisonSettings(app *appv1.Application) (string,
|
||||
// revision and supplied source. If revision or overrides are empty, then compares against
|
||||
// revision and overrides in the app spec.
|
||||
func (m *appStateManager) CompareAppState(app *v1alpha1.Application, revision string, source v1alpha1.ApplicationSource, noCache bool, localManifests []string) *comparisonResult {
|
||||
reconciledAt := metav1.Now()
|
||||
appLabelKey, resourceOverrides, diffNormalizer, err := m.getComparisonSettings(app)
|
||||
|
||||
// return unknown comparison result if basic comparison settings cannot be loaded
|
||||
if err != nil {
|
||||
return &comparisonResult{
|
||||
reconciledAt: reconciledAt,
|
||||
syncStatus: &v1alpha1.SyncStatus{
|
||||
ComparedTo: appv1.ComparedTo{Source: source, Destination: app.Spec.Destination},
|
||||
Status: appv1.SyncStatusCodeUnknown,
|
||||
@@ -435,6 +428,10 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, revision st
|
||||
} else {
|
||||
resState.Status = v1alpha1.SyncStatusCodeSynced
|
||||
}
|
||||
// we can't say anything about the status if we were unable to get the target objects
|
||||
if failedToLoadObjs {
|
||||
resState.Status = v1alpha1.SyncStatusCodeUnknown
|
||||
}
|
||||
managedResources[i] = managedResource{
|
||||
Name: resState.Name,
|
||||
Namespace: resState.Namespace,
|
||||
@@ -472,7 +469,6 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, revision st
|
||||
}
|
||||
|
||||
compRes := comparisonResult{
|
||||
reconciledAt: reconciledAt,
|
||||
syncStatus: &syncStatus,
|
||||
healthStatus: healthStatus,
|
||||
resources: resourceSummaries,
|
||||
|
||||
@@ -45,7 +45,7 @@ func TestCompareAppStateMissing(t *testing.T) {
|
||||
data := fakeData{
|
||||
apps: []runtime.Object{app},
|
||||
manifestResponse: &apiclient.ManifestResponse{
|
||||
Manifests: []string{string(test.PodManifest)},
|
||||
Manifests: []string{test.PodManifest},
|
||||
Namespace: test.FakeDestNamespace,
|
||||
Server: test.FakeClusterURL,
|
||||
Revision: "abc123",
|
||||
@@ -354,7 +354,6 @@ func TestReturnUnknownComparisonStateOnSettingLoadError(t *testing.T) {
|
||||
|
||||
assert.Equal(t, argoappv1.HealthStatusUnknown, compRes.healthStatus.Status)
|
||||
assert.Equal(t, argoappv1.SyncStatusCodeUnknown, compRes.syncStatus.Status)
|
||||
assert.NotNil(t, compRes.reconciledAt)
|
||||
}
|
||||
|
||||
func TestSetManagedResourcesKnownOrphanedResourceExceptions(t *testing.T) {
|
||||
|
||||
@@ -56,6 +56,8 @@ Ensure dependencies are up to date first:
|
||||
dep ensure
|
||||
make dev-builder-image
|
||||
make install-lint-tools
|
||||
go get github.com/mattn/goreman
|
||||
go get github.com/jstemmer/go-junit-report
|
||||
```
|
||||
|
||||
Common make targets:
|
||||
|
||||
8
hack/installers/install-aws-iam-authenticator-linux.sh
Executable file
8
hack/installers/install-aws-iam-authenticator-linux.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
set -eux -o pipefail
|
||||
|
||||
AWS_IAM_AUTHENTICATOR_VERSION=0.4.0-alpha.1
|
||||
[ -e $DOWNLOADS/aws-iam-authenticator ] || curl -sLf --retry 3 -o $DOWNLOADS/aws-iam-authenticator https://github.com/kubernetes-sigs/aws-iam-authenticator/releases/download/${AWS_IAM_AUTHENTICATOR_VERSION}/aws-iam-authenticator_${AWS_IAM_AUTHENTICATOR_VERSION}_linux_amd64
|
||||
cp $DOWNLOADS/aws-iam-authenticator $BIN/
|
||||
chmod +x $BIN/aws-iam-authenticator
|
||||
aws-iam-authenticator version
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
set -eux -o pipefail
|
||||
|
||||
[ -e $DOWNLOADS/helm.tar.gz ] || curl -sLf --retry 3 -o $DOWNLOADS/helm.tar.gz https://storage.googleapis.com/kubernetes-helm/helm-v2.13.1-linux-amd64.tar.gz
|
||||
[ -e $DOWNLOADS/helm.tar.gz ] || curl -sLf --retry 3 -o $DOWNLOADS/helm.tar.gz https://storage.googleapis.com/kubernetes-helm/helm-v2.15.2-linux-amd64.tar.gz
|
||||
tar -C /tmp/ -xf $DOWNLOADS/helm.tar.gz
|
||||
cp /tmp/linux-amd64/helm $BIN/helm
|
||||
helm version --client
|
||||
|
||||
@@ -1,11 +1,25 @@
|
||||
#!/bin/bash
|
||||
set -eux -o pipefail
|
||||
|
||||
# TODO we use v2 for generating manifests, v3 for production - we should always use v3
|
||||
KUSTOMIZE_VERSION=${KUSTOMIZE_VERSION:-3.1.0}
|
||||
KUSTOMIZE_VERSION=${KUSTOMIZE_VERSION:-3.2.1}
|
||||
DL=$DOWNLOADS/kustomize-${KUSTOMIZE_VERSION}
|
||||
|
||||
[ -e $DL ] || curl -sLf --retry 3 -o $DL https://github.com/kubernetes-sigs/kustomize/releases/download/v${KUSTOMIZE_VERSION}/kustomize_${KUSTOMIZE_VERSION}_linux_amd64
|
||||
# Note that kustomize release URIs have changed for v3.2.1. Then again for
|
||||
# v3.3.0. When upgrading to versions >= v3.3.0 please change the URI format. And
|
||||
# also note that as of version v3.3.0, assets are in .tar.gz form.
|
||||
# v3.2.0 = https://github.com/kubernetes-sigs/kustomize/releases/download/v3.2.0/kustomize_3.2.0_linux_amd64
|
||||
# v3.2.1 = https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/v3.2.1/kustomize_kustomize.v3.2.1_linux_amd64
|
||||
# v3.3.0 = https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/v3.3.0/kustomize_v3.3.0_linux_amd64.tar.gz
|
||||
case $KUSTOMIZE_VERSION in
|
||||
2.*)
|
||||
URL=https://github.com/kubernetes-sigs/kustomize/releases/download/v${KUSTOMIZE_VERSION}/kustomize_${KUSTOMIZE_VERSION}_linux_amd64
|
||||
;;
|
||||
*)
|
||||
URL=https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/v${KUSTOMIZE_VERSION}/kustomize_kustomize.v${KUSTOMIZE_VERSION}_linux_amd64
|
||||
;;
|
||||
esac
|
||||
|
||||
[ -e $DL ] || curl -sLf --retry 3 -o $DL $URL
|
||||
cp $DL $BIN/kustomize
|
||||
chmod +x $BIN/kustomize
|
||||
kustomize version
|
||||
|
||||
9
hack/installers/install-packr-linux.sh
Executable file
9
hack/installers/install-packr-linux.sh
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
set -eux -o pipefail
|
||||
|
||||
PACKR_VERSION=1.21.9
|
||||
[ -e $DOWNLOADS/parkr.tar.gz ] || curl -sLf --retry 3 -o $DOWNLOADS/parkr.tar.gz https://github.com/gobuffalo/packr/releases/download/v${PACKR_VERSION}/packr_${PACKR_VERSION}_linux_amd64.tar.gz
|
||||
tar -vxf $DOWNLOADS/parkr.tar.gz -C /tmp/
|
||||
cp /tmp/packr $BIN/
|
||||
chmod +x $BIN/packr
|
||||
packr version
|
||||
@@ -12,7 +12,7 @@ bases:
|
||||
images:
|
||||
- name: argoproj/argocd
|
||||
newName: argoproj/argocd
|
||||
newTag: latest
|
||||
newTag: v1.3.6
|
||||
- name: argoproj/argocd-ui
|
||||
newName: argoproj/argocd-ui
|
||||
newTag: latest
|
||||
newTag: v1.3.6
|
||||
|
||||
@@ -1032,6 +1032,8 @@ spec:
|
||||
type: object
|
||||
type: array
|
||||
observedAt:
|
||||
description: ObservedAt indicates when the application state was updated
|
||||
without querying latest git state
|
||||
format: date-time
|
||||
type: string
|
||||
operationState:
|
||||
@@ -1509,6 +1511,8 @@ spec:
|
||||
- startedAt
|
||||
type: object
|
||||
reconciledAt:
|
||||
description: ReconciledAt indicates when the application state was reconciled
|
||||
using the latest git version
|
||||
format: date-time
|
||||
type: string
|
||||
resources:
|
||||
|
||||
@@ -18,7 +18,7 @@ bases:
|
||||
images:
|
||||
- name: argoproj/argocd
|
||||
newName: argoproj/argocd
|
||||
newTag: latest
|
||||
newTag: v1.3.6
|
||||
- name: argoproj/argocd-ui
|
||||
newName: argoproj/argocd-ui
|
||||
newTag: latest
|
||||
newTag: v1.3.6
|
||||
|
||||
@@ -1033,6 +1033,8 @@ spec:
|
||||
type: object
|
||||
type: array
|
||||
observedAt:
|
||||
description: ObservedAt indicates when the application state was updated
|
||||
without querying latest git state
|
||||
format: date-time
|
||||
type: string
|
||||
operationState:
|
||||
@@ -1510,6 +1512,8 @@ spec:
|
||||
- startedAt
|
||||
type: object
|
||||
reconciledAt:
|
||||
description: ReconciledAt indicates when the application state was reconciled
|
||||
using the latest git version
|
||||
format: date-time
|
||||
type: string
|
||||
resources:
|
||||
@@ -2978,7 +2982,7 @@ spec:
|
||||
- argocd-redis-ha-announce-2:26379
|
||||
- --sentinelmaster
|
||||
- argocd
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -3032,7 +3036,7 @@ spec:
|
||||
- cp
|
||||
- /usr/local/bin/argocd-util
|
||||
- /shared
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -3088,7 +3092,7 @@ spec:
|
||||
- argocd-redis-ha-announce-2:26379
|
||||
- --sentinelmaster
|
||||
- argocd
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
initialDelaySeconds: 5
|
||||
@@ -3162,7 +3166,7 @@ spec:
|
||||
- argocd-redis-ha-announce-2:26379
|
||||
- --sentinelmaster
|
||||
- argocd
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -1033,6 +1033,8 @@ spec:
|
||||
type: object
|
||||
type: array
|
||||
observedAt:
|
||||
description: ObservedAt indicates when the application state was updated
|
||||
without querying latest git state
|
||||
format: date-time
|
||||
type: string
|
||||
operationState:
|
||||
@@ -1510,6 +1512,8 @@ spec:
|
||||
- startedAt
|
||||
type: object
|
||||
reconciledAt:
|
||||
description: ReconciledAt indicates when the application state was reconciled
|
||||
using the latest git version
|
||||
format: date-time
|
||||
type: string
|
||||
resources:
|
||||
@@ -2893,7 +2897,7 @@ spec:
|
||||
- argocd-redis-ha-announce-2:26379
|
||||
- --sentinelmaster
|
||||
- argocd
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -2947,7 +2951,7 @@ spec:
|
||||
- cp
|
||||
- /usr/local/bin/argocd-util
|
||||
- /shared
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -3003,7 +3007,7 @@ spec:
|
||||
- argocd-redis-ha-announce-2:26379
|
||||
- --sentinelmaster
|
||||
- argocd
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
initialDelaySeconds: 5
|
||||
@@ -3077,7 +3081,7 @@ spec:
|
||||
- argocd-redis-ha-announce-2:26379
|
||||
- --sentinelmaster
|
||||
- argocd
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -1033,6 +1033,8 @@ spec:
|
||||
type: object
|
||||
type: array
|
||||
observedAt:
|
||||
description: ObservedAt indicates when the application state was updated
|
||||
without querying latest git state
|
||||
format: date-time
|
||||
type: string
|
||||
operationState:
|
||||
@@ -1510,6 +1512,8 @@ spec:
|
||||
- startedAt
|
||||
type: object
|
||||
reconciledAt:
|
||||
description: ReconciledAt indicates when the application state was reconciled
|
||||
using the latest git version
|
||||
format: date-time
|
||||
type: string
|
||||
resources:
|
||||
@@ -2742,7 +2746,7 @@ spec:
|
||||
- "20"
|
||||
- --operation-processors
|
||||
- "10"
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -2796,7 +2800,7 @@ spec:
|
||||
- cp
|
||||
- /usr/local/bin/argocd-util
|
||||
- /shared
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -2860,7 +2864,7 @@ spec:
|
||||
- argocd-repo-server
|
||||
- --redis
|
||||
- argocd-redis:6379
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
initialDelaySeconds: 5
|
||||
@@ -2911,7 +2915,7 @@ spec:
|
||||
- argocd-server
|
||||
- --staticassets
|
||||
- /shared/app
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -1033,6 +1033,8 @@ spec:
|
||||
type: object
|
||||
type: array
|
||||
observedAt:
|
||||
description: ObservedAt indicates when the application state was updated
|
||||
without querying latest git state
|
||||
format: date-time
|
||||
type: string
|
||||
operationState:
|
||||
@@ -1510,6 +1512,8 @@ spec:
|
||||
- startedAt
|
||||
type: object
|
||||
reconciledAt:
|
||||
description: ReconciledAt indicates when the application state was reconciled
|
||||
using the latest git version
|
||||
format: date-time
|
||||
type: string
|
||||
resources:
|
||||
@@ -2657,7 +2661,7 @@ spec:
|
||||
- "20"
|
||||
- --operation-processors
|
||||
- "10"
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -2711,7 +2715,7 @@ spec:
|
||||
- cp
|
||||
- /usr/local/bin/argocd-util
|
||||
- /shared
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -2775,7 +2779,7 @@ spec:
|
||||
- argocd-repo-server
|
||||
- --redis
|
||||
- argocd-redis:6379
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
initialDelaySeconds: 5
|
||||
@@ -2826,7 +2830,7 @@ spec:
|
||||
- argocd-server
|
||||
- --staticassets
|
||||
- /shared/app
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -33,7 +33,7 @@ const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
|
||||
func (m *AWSAuthConfig) Reset() { *m = AWSAuthConfig{} }
|
||||
func (*AWSAuthConfig) ProtoMessage() {}
|
||||
func (*AWSAuthConfig) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{0}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{0}
|
||||
}
|
||||
func (m *AWSAuthConfig) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -61,7 +61,7 @@ var xxx_messageInfo_AWSAuthConfig proto.InternalMessageInfo
|
||||
func (m *AppProject) Reset() { *m = AppProject{} }
|
||||
func (*AppProject) ProtoMessage() {}
|
||||
func (*AppProject) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{1}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{1}
|
||||
}
|
||||
func (m *AppProject) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -89,7 +89,7 @@ var xxx_messageInfo_AppProject proto.InternalMessageInfo
|
||||
func (m *AppProjectList) Reset() { *m = AppProjectList{} }
|
||||
func (*AppProjectList) ProtoMessage() {}
|
||||
func (*AppProjectList) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{2}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{2}
|
||||
}
|
||||
func (m *AppProjectList) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -117,7 +117,7 @@ var xxx_messageInfo_AppProjectList proto.InternalMessageInfo
|
||||
func (m *AppProjectSpec) Reset() { *m = AppProjectSpec{} }
|
||||
func (*AppProjectSpec) ProtoMessage() {}
|
||||
func (*AppProjectSpec) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{3}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{3}
|
||||
}
|
||||
func (m *AppProjectSpec) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -145,7 +145,7 @@ var xxx_messageInfo_AppProjectSpec proto.InternalMessageInfo
|
||||
func (m *Application) Reset() { *m = Application{} }
|
||||
func (*Application) ProtoMessage() {}
|
||||
func (*Application) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{4}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{4}
|
||||
}
|
||||
func (m *Application) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -173,7 +173,7 @@ var xxx_messageInfo_Application proto.InternalMessageInfo
|
||||
func (m *ApplicationCondition) Reset() { *m = ApplicationCondition{} }
|
||||
func (*ApplicationCondition) ProtoMessage() {}
|
||||
func (*ApplicationCondition) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{5}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{5}
|
||||
}
|
||||
func (m *ApplicationCondition) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -201,7 +201,7 @@ var xxx_messageInfo_ApplicationCondition proto.InternalMessageInfo
|
||||
func (m *ApplicationDestination) Reset() { *m = ApplicationDestination{} }
|
||||
func (*ApplicationDestination) ProtoMessage() {}
|
||||
func (*ApplicationDestination) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{6}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{6}
|
||||
}
|
||||
func (m *ApplicationDestination) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -229,7 +229,7 @@ var xxx_messageInfo_ApplicationDestination proto.InternalMessageInfo
|
||||
func (m *ApplicationList) Reset() { *m = ApplicationList{} }
|
||||
func (*ApplicationList) ProtoMessage() {}
|
||||
func (*ApplicationList) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{7}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{7}
|
||||
}
|
||||
func (m *ApplicationList) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -257,7 +257,7 @@ var xxx_messageInfo_ApplicationList proto.InternalMessageInfo
|
||||
func (m *ApplicationSource) Reset() { *m = ApplicationSource{} }
|
||||
func (*ApplicationSource) ProtoMessage() {}
|
||||
func (*ApplicationSource) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{8}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{8}
|
||||
}
|
||||
func (m *ApplicationSource) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -285,7 +285,7 @@ var xxx_messageInfo_ApplicationSource proto.InternalMessageInfo
|
||||
func (m *ApplicationSourceDirectory) Reset() { *m = ApplicationSourceDirectory{} }
|
||||
func (*ApplicationSourceDirectory) ProtoMessage() {}
|
||||
func (*ApplicationSourceDirectory) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{9}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{9}
|
||||
}
|
||||
func (m *ApplicationSourceDirectory) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -313,7 +313,7 @@ var xxx_messageInfo_ApplicationSourceDirectory proto.InternalMessageInfo
|
||||
func (m *ApplicationSourceHelm) Reset() { *m = ApplicationSourceHelm{} }
|
||||
func (*ApplicationSourceHelm) ProtoMessage() {}
|
||||
func (*ApplicationSourceHelm) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{10}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{10}
|
||||
}
|
||||
func (m *ApplicationSourceHelm) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -341,7 +341,7 @@ var xxx_messageInfo_ApplicationSourceHelm proto.InternalMessageInfo
|
||||
func (m *ApplicationSourceJsonnet) Reset() { *m = ApplicationSourceJsonnet{} }
|
||||
func (*ApplicationSourceJsonnet) ProtoMessage() {}
|
||||
func (*ApplicationSourceJsonnet) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{11}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{11}
|
||||
}
|
||||
func (m *ApplicationSourceJsonnet) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -369,7 +369,7 @@ var xxx_messageInfo_ApplicationSourceJsonnet proto.InternalMessageInfo
|
||||
func (m *ApplicationSourceKsonnet) Reset() { *m = ApplicationSourceKsonnet{} }
|
||||
func (*ApplicationSourceKsonnet) ProtoMessage() {}
|
||||
func (*ApplicationSourceKsonnet) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{12}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{12}
|
||||
}
|
||||
func (m *ApplicationSourceKsonnet) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -397,7 +397,7 @@ var xxx_messageInfo_ApplicationSourceKsonnet proto.InternalMessageInfo
|
||||
func (m *ApplicationSourceKustomize) Reset() { *m = ApplicationSourceKustomize{} }
|
||||
func (*ApplicationSourceKustomize) ProtoMessage() {}
|
||||
func (*ApplicationSourceKustomize) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{13}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{13}
|
||||
}
|
||||
func (m *ApplicationSourceKustomize) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -425,7 +425,7 @@ var xxx_messageInfo_ApplicationSourceKustomize proto.InternalMessageInfo
|
||||
func (m *ApplicationSourcePlugin) Reset() { *m = ApplicationSourcePlugin{} }
|
||||
func (*ApplicationSourcePlugin) ProtoMessage() {}
|
||||
func (*ApplicationSourcePlugin) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{14}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{14}
|
||||
}
|
||||
func (m *ApplicationSourcePlugin) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -453,7 +453,7 @@ var xxx_messageInfo_ApplicationSourcePlugin proto.InternalMessageInfo
|
||||
func (m *ApplicationSpec) Reset() { *m = ApplicationSpec{} }
|
||||
func (*ApplicationSpec) ProtoMessage() {}
|
||||
func (*ApplicationSpec) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{15}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{15}
|
||||
}
|
||||
func (m *ApplicationSpec) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -481,7 +481,7 @@ var xxx_messageInfo_ApplicationSpec proto.InternalMessageInfo
|
||||
func (m *ApplicationStatus) Reset() { *m = ApplicationStatus{} }
|
||||
func (*ApplicationStatus) ProtoMessage() {}
|
||||
func (*ApplicationStatus) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{16}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{16}
|
||||
}
|
||||
func (m *ApplicationStatus) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -509,7 +509,7 @@ var xxx_messageInfo_ApplicationStatus proto.InternalMessageInfo
|
||||
func (m *ApplicationSummary) Reset() { *m = ApplicationSummary{} }
|
||||
func (*ApplicationSummary) ProtoMessage() {}
|
||||
func (*ApplicationSummary) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{17}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{17}
|
||||
}
|
||||
func (m *ApplicationSummary) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -537,7 +537,7 @@ var xxx_messageInfo_ApplicationSummary proto.InternalMessageInfo
|
||||
func (m *ApplicationTree) Reset() { *m = ApplicationTree{} }
|
||||
func (*ApplicationTree) ProtoMessage() {}
|
||||
func (*ApplicationTree) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{18}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{18}
|
||||
}
|
||||
func (m *ApplicationTree) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -565,7 +565,7 @@ var xxx_messageInfo_ApplicationTree proto.InternalMessageInfo
|
||||
func (m *ApplicationWatchEvent) Reset() { *m = ApplicationWatchEvent{} }
|
||||
func (*ApplicationWatchEvent) ProtoMessage() {}
|
||||
func (*ApplicationWatchEvent) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{19}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{19}
|
||||
}
|
||||
func (m *ApplicationWatchEvent) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -593,7 +593,7 @@ var xxx_messageInfo_ApplicationWatchEvent proto.InternalMessageInfo
|
||||
func (m *Cluster) Reset() { *m = Cluster{} }
|
||||
func (*Cluster) ProtoMessage() {}
|
||||
func (*Cluster) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{20}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{20}
|
||||
}
|
||||
func (m *Cluster) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -621,7 +621,7 @@ var xxx_messageInfo_Cluster proto.InternalMessageInfo
|
||||
func (m *ClusterConfig) Reset() { *m = ClusterConfig{} }
|
||||
func (*ClusterConfig) ProtoMessage() {}
|
||||
func (*ClusterConfig) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{21}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{21}
|
||||
}
|
||||
func (m *ClusterConfig) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -649,7 +649,7 @@ var xxx_messageInfo_ClusterConfig proto.InternalMessageInfo
|
||||
func (m *ClusterList) Reset() { *m = ClusterList{} }
|
||||
func (*ClusterList) ProtoMessage() {}
|
||||
func (*ClusterList) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{22}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{22}
|
||||
}
|
||||
func (m *ClusterList) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -677,7 +677,7 @@ var xxx_messageInfo_ClusterList proto.InternalMessageInfo
|
||||
func (m *Command) Reset() { *m = Command{} }
|
||||
func (*Command) ProtoMessage() {}
|
||||
func (*Command) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{23}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{23}
|
||||
}
|
||||
func (m *Command) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -705,7 +705,7 @@ var xxx_messageInfo_Command proto.InternalMessageInfo
|
||||
func (m *ComparedTo) Reset() { *m = ComparedTo{} }
|
||||
func (*ComparedTo) ProtoMessage() {}
|
||||
func (*ComparedTo) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{24}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{24}
|
||||
}
|
||||
func (m *ComparedTo) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -733,7 +733,7 @@ var xxx_messageInfo_ComparedTo proto.InternalMessageInfo
|
||||
func (m *ComponentParameter) Reset() { *m = ComponentParameter{} }
|
||||
func (*ComponentParameter) ProtoMessage() {}
|
||||
func (*ComponentParameter) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{25}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{25}
|
||||
}
|
||||
func (m *ComponentParameter) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -761,7 +761,7 @@ var xxx_messageInfo_ComponentParameter proto.InternalMessageInfo
|
||||
func (m *ConfigManagementPlugin) Reset() { *m = ConfigManagementPlugin{} }
|
||||
func (*ConfigManagementPlugin) ProtoMessage() {}
|
||||
func (*ConfigManagementPlugin) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{26}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{26}
|
||||
}
|
||||
func (m *ConfigManagementPlugin) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -789,7 +789,7 @@ var xxx_messageInfo_ConfigManagementPlugin proto.InternalMessageInfo
|
||||
func (m *ConnectionState) Reset() { *m = ConnectionState{} }
|
||||
func (*ConnectionState) ProtoMessage() {}
|
||||
func (*ConnectionState) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{27}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{27}
|
||||
}
|
||||
func (m *ConnectionState) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -817,7 +817,7 @@ var xxx_messageInfo_ConnectionState proto.InternalMessageInfo
|
||||
func (m *EnvEntry) Reset() { *m = EnvEntry{} }
|
||||
func (*EnvEntry) ProtoMessage() {}
|
||||
func (*EnvEntry) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{28}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{28}
|
||||
}
|
||||
func (m *EnvEntry) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -845,7 +845,7 @@ var xxx_messageInfo_EnvEntry proto.InternalMessageInfo
|
||||
func (m *HealthStatus) Reset() { *m = HealthStatus{} }
|
||||
func (*HealthStatus) ProtoMessage() {}
|
||||
func (*HealthStatus) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{29}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{29}
|
||||
}
|
||||
func (m *HealthStatus) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -873,7 +873,7 @@ var xxx_messageInfo_HealthStatus proto.InternalMessageInfo
|
||||
func (m *HelmParameter) Reset() { *m = HelmParameter{} }
|
||||
func (*HelmParameter) ProtoMessage() {}
|
||||
func (*HelmParameter) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{30}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{30}
|
||||
}
|
||||
func (m *HelmParameter) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -901,7 +901,7 @@ var xxx_messageInfo_HelmParameter proto.InternalMessageInfo
|
||||
func (m *Info) Reset() { *m = Info{} }
|
||||
func (*Info) ProtoMessage() {}
|
||||
func (*Info) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{31}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{31}
|
||||
}
|
||||
func (m *Info) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -929,7 +929,7 @@ var xxx_messageInfo_Info proto.InternalMessageInfo
|
||||
func (m *InfoItem) Reset() { *m = InfoItem{} }
|
||||
func (*InfoItem) ProtoMessage() {}
|
||||
func (*InfoItem) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{32}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{32}
|
||||
}
|
||||
func (m *InfoItem) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -957,7 +957,7 @@ var xxx_messageInfo_InfoItem proto.InternalMessageInfo
|
||||
func (m *JWTToken) Reset() { *m = JWTToken{} }
|
||||
func (*JWTToken) ProtoMessage() {}
|
||||
func (*JWTToken) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{33}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{33}
|
||||
}
|
||||
func (m *JWTToken) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -985,7 +985,7 @@ var xxx_messageInfo_JWTToken proto.InternalMessageInfo
|
||||
func (m *JsonnetVar) Reset() { *m = JsonnetVar{} }
|
||||
func (*JsonnetVar) ProtoMessage() {}
|
||||
func (*JsonnetVar) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{34}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{34}
|
||||
}
|
||||
func (m *JsonnetVar) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1013,7 +1013,7 @@ var xxx_messageInfo_JsonnetVar proto.InternalMessageInfo
|
||||
func (m *KsonnetParameter) Reset() { *m = KsonnetParameter{} }
|
||||
func (*KsonnetParameter) ProtoMessage() {}
|
||||
func (*KsonnetParameter) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{35}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{35}
|
||||
}
|
||||
func (m *KsonnetParameter) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1041,7 +1041,7 @@ var xxx_messageInfo_KsonnetParameter proto.InternalMessageInfo
|
||||
func (m *KustomizeOptions) Reset() { *m = KustomizeOptions{} }
|
||||
func (*KustomizeOptions) ProtoMessage() {}
|
||||
func (*KustomizeOptions) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{36}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{36}
|
||||
}
|
||||
func (m *KustomizeOptions) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1069,7 +1069,7 @@ var xxx_messageInfo_KustomizeOptions proto.InternalMessageInfo
|
||||
func (m *Operation) Reset() { *m = Operation{} }
|
||||
func (*Operation) ProtoMessage() {}
|
||||
func (*Operation) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{37}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{37}
|
||||
}
|
||||
func (m *Operation) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1097,7 +1097,7 @@ var xxx_messageInfo_Operation proto.InternalMessageInfo
|
||||
func (m *OperationState) Reset() { *m = OperationState{} }
|
||||
func (*OperationState) ProtoMessage() {}
|
||||
func (*OperationState) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{38}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{38}
|
||||
}
|
||||
func (m *OperationState) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1125,7 +1125,7 @@ var xxx_messageInfo_OperationState proto.InternalMessageInfo
|
||||
func (m *OrphanedResourcesMonitorSettings) Reset() { *m = OrphanedResourcesMonitorSettings{} }
|
||||
func (*OrphanedResourcesMonitorSettings) ProtoMessage() {}
|
||||
func (*OrphanedResourcesMonitorSettings) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{39}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{39}
|
||||
}
|
||||
func (m *OrphanedResourcesMonitorSettings) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1153,7 +1153,7 @@ var xxx_messageInfo_OrphanedResourcesMonitorSettings proto.InternalMessageInfo
|
||||
func (m *ProjectRole) Reset() { *m = ProjectRole{} }
|
||||
func (*ProjectRole) ProtoMessage() {}
|
||||
func (*ProjectRole) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{40}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{40}
|
||||
}
|
||||
func (m *ProjectRole) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1181,7 +1181,7 @@ var xxx_messageInfo_ProjectRole proto.InternalMessageInfo
|
||||
func (m *Repository) Reset() { *m = Repository{} }
|
||||
func (*Repository) ProtoMessage() {}
|
||||
func (*Repository) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{41}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{41}
|
||||
}
|
||||
func (m *Repository) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1209,7 +1209,7 @@ var xxx_messageInfo_Repository proto.InternalMessageInfo
|
||||
func (m *RepositoryCertificate) Reset() { *m = RepositoryCertificate{} }
|
||||
func (*RepositoryCertificate) ProtoMessage() {}
|
||||
func (*RepositoryCertificate) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{42}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{42}
|
||||
}
|
||||
func (m *RepositoryCertificate) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1237,7 +1237,7 @@ var xxx_messageInfo_RepositoryCertificate proto.InternalMessageInfo
|
||||
func (m *RepositoryCertificateList) Reset() { *m = RepositoryCertificateList{} }
|
||||
func (*RepositoryCertificateList) ProtoMessage() {}
|
||||
func (*RepositoryCertificateList) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{43}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{43}
|
||||
}
|
||||
func (m *RepositoryCertificateList) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1265,7 +1265,7 @@ var xxx_messageInfo_RepositoryCertificateList proto.InternalMessageInfo
|
||||
func (m *RepositoryList) Reset() { *m = RepositoryList{} }
|
||||
func (*RepositoryList) ProtoMessage() {}
|
||||
func (*RepositoryList) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{44}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{44}
|
||||
}
|
||||
func (m *RepositoryList) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1293,7 +1293,7 @@ var xxx_messageInfo_RepositoryList proto.InternalMessageInfo
|
||||
func (m *ResourceAction) Reset() { *m = ResourceAction{} }
|
||||
func (*ResourceAction) ProtoMessage() {}
|
||||
func (*ResourceAction) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{45}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{45}
|
||||
}
|
||||
func (m *ResourceAction) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1321,7 +1321,7 @@ var xxx_messageInfo_ResourceAction proto.InternalMessageInfo
|
||||
func (m *ResourceActionDefinition) Reset() { *m = ResourceActionDefinition{} }
|
||||
func (*ResourceActionDefinition) ProtoMessage() {}
|
||||
func (*ResourceActionDefinition) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{46}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{46}
|
||||
}
|
||||
func (m *ResourceActionDefinition) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1349,7 +1349,7 @@ var xxx_messageInfo_ResourceActionDefinition proto.InternalMessageInfo
|
||||
func (m *ResourceActionParam) Reset() { *m = ResourceActionParam{} }
|
||||
func (*ResourceActionParam) ProtoMessage() {}
|
||||
func (*ResourceActionParam) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{47}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{47}
|
||||
}
|
||||
func (m *ResourceActionParam) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1377,7 +1377,7 @@ var xxx_messageInfo_ResourceActionParam proto.InternalMessageInfo
|
||||
func (m *ResourceActions) Reset() { *m = ResourceActions{} }
|
||||
func (*ResourceActions) ProtoMessage() {}
|
||||
func (*ResourceActions) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{48}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{48}
|
||||
}
|
||||
func (m *ResourceActions) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1405,7 +1405,7 @@ var xxx_messageInfo_ResourceActions proto.InternalMessageInfo
|
||||
func (m *ResourceDiff) Reset() { *m = ResourceDiff{} }
|
||||
func (*ResourceDiff) ProtoMessage() {}
|
||||
func (*ResourceDiff) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{49}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{49}
|
||||
}
|
||||
func (m *ResourceDiff) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1433,7 +1433,7 @@ var xxx_messageInfo_ResourceDiff proto.InternalMessageInfo
|
||||
func (m *ResourceIgnoreDifferences) Reset() { *m = ResourceIgnoreDifferences{} }
|
||||
func (*ResourceIgnoreDifferences) ProtoMessage() {}
|
||||
func (*ResourceIgnoreDifferences) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{50}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{50}
|
||||
}
|
||||
func (m *ResourceIgnoreDifferences) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1461,7 +1461,7 @@ var xxx_messageInfo_ResourceIgnoreDifferences proto.InternalMessageInfo
|
||||
func (m *ResourceNetworkingInfo) Reset() { *m = ResourceNetworkingInfo{} }
|
||||
func (*ResourceNetworkingInfo) ProtoMessage() {}
|
||||
func (*ResourceNetworkingInfo) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{51}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{51}
|
||||
}
|
||||
func (m *ResourceNetworkingInfo) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1489,7 +1489,7 @@ var xxx_messageInfo_ResourceNetworkingInfo proto.InternalMessageInfo
|
||||
func (m *ResourceNode) Reset() { *m = ResourceNode{} }
|
||||
func (*ResourceNode) ProtoMessage() {}
|
||||
func (*ResourceNode) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{52}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{52}
|
||||
}
|
||||
func (m *ResourceNode) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1517,7 +1517,7 @@ var xxx_messageInfo_ResourceNode proto.InternalMessageInfo
|
||||
func (m *ResourceOverride) Reset() { *m = ResourceOverride{} }
|
||||
func (*ResourceOverride) ProtoMessage() {}
|
||||
func (*ResourceOverride) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{53}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{53}
|
||||
}
|
||||
func (m *ResourceOverride) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1545,7 +1545,7 @@ var xxx_messageInfo_ResourceOverride proto.InternalMessageInfo
|
||||
func (m *ResourceRef) Reset() { *m = ResourceRef{} }
|
||||
func (*ResourceRef) ProtoMessage() {}
|
||||
func (*ResourceRef) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{54}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{54}
|
||||
}
|
||||
func (m *ResourceRef) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1573,7 +1573,7 @@ var xxx_messageInfo_ResourceRef proto.InternalMessageInfo
|
||||
func (m *ResourceResult) Reset() { *m = ResourceResult{} }
|
||||
func (*ResourceResult) ProtoMessage() {}
|
||||
func (*ResourceResult) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{55}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{55}
|
||||
}
|
||||
func (m *ResourceResult) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1601,7 +1601,7 @@ var xxx_messageInfo_ResourceResult proto.InternalMessageInfo
|
||||
func (m *ResourceStatus) Reset() { *m = ResourceStatus{} }
|
||||
func (*ResourceStatus) ProtoMessage() {}
|
||||
func (*ResourceStatus) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{56}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{56}
|
||||
}
|
||||
func (m *ResourceStatus) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1629,7 +1629,7 @@ var xxx_messageInfo_ResourceStatus proto.InternalMessageInfo
|
||||
func (m *RevisionHistory) Reset() { *m = RevisionHistory{} }
|
||||
func (*RevisionHistory) ProtoMessage() {}
|
||||
func (*RevisionHistory) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{57}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{57}
|
||||
}
|
||||
func (m *RevisionHistory) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1657,7 +1657,7 @@ var xxx_messageInfo_RevisionHistory proto.InternalMessageInfo
|
||||
func (m *RevisionMetadata) Reset() { *m = RevisionMetadata{} }
|
||||
func (*RevisionMetadata) ProtoMessage() {}
|
||||
func (*RevisionMetadata) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{58}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{58}
|
||||
}
|
||||
func (m *RevisionMetadata) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1685,7 +1685,7 @@ var xxx_messageInfo_RevisionMetadata proto.InternalMessageInfo
|
||||
func (m *SyncOperation) Reset() { *m = SyncOperation{} }
|
||||
func (*SyncOperation) ProtoMessage() {}
|
||||
func (*SyncOperation) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{59}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{59}
|
||||
}
|
||||
func (m *SyncOperation) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1713,7 +1713,7 @@ var xxx_messageInfo_SyncOperation proto.InternalMessageInfo
|
||||
func (m *SyncOperationResource) Reset() { *m = SyncOperationResource{} }
|
||||
func (*SyncOperationResource) ProtoMessage() {}
|
||||
func (*SyncOperationResource) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{60}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{60}
|
||||
}
|
||||
func (m *SyncOperationResource) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1741,7 +1741,7 @@ var xxx_messageInfo_SyncOperationResource proto.InternalMessageInfo
|
||||
func (m *SyncOperationResult) Reset() { *m = SyncOperationResult{} }
|
||||
func (*SyncOperationResult) ProtoMessage() {}
|
||||
func (*SyncOperationResult) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{61}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{61}
|
||||
}
|
||||
func (m *SyncOperationResult) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1769,7 +1769,7 @@ var xxx_messageInfo_SyncOperationResult proto.InternalMessageInfo
|
||||
func (m *SyncPolicy) Reset() { *m = SyncPolicy{} }
|
||||
func (*SyncPolicy) ProtoMessage() {}
|
||||
func (*SyncPolicy) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{62}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{62}
|
||||
}
|
||||
func (m *SyncPolicy) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1797,7 +1797,7 @@ var xxx_messageInfo_SyncPolicy proto.InternalMessageInfo
|
||||
func (m *SyncPolicyAutomated) Reset() { *m = SyncPolicyAutomated{} }
|
||||
func (*SyncPolicyAutomated) ProtoMessage() {}
|
||||
func (*SyncPolicyAutomated) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{63}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{63}
|
||||
}
|
||||
func (m *SyncPolicyAutomated) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1825,7 +1825,7 @@ var xxx_messageInfo_SyncPolicyAutomated proto.InternalMessageInfo
|
||||
func (m *SyncStatus) Reset() { *m = SyncStatus{} }
|
||||
func (*SyncStatus) ProtoMessage() {}
|
||||
func (*SyncStatus) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{64}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{64}
|
||||
}
|
||||
func (m *SyncStatus) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1853,7 +1853,7 @@ var xxx_messageInfo_SyncStatus proto.InternalMessageInfo
|
||||
func (m *SyncStrategy) Reset() { *m = SyncStrategy{} }
|
||||
func (*SyncStrategy) ProtoMessage() {}
|
||||
func (*SyncStrategy) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{65}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{65}
|
||||
}
|
||||
func (m *SyncStrategy) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1881,7 +1881,7 @@ var xxx_messageInfo_SyncStrategy proto.InternalMessageInfo
|
||||
func (m *SyncStrategyApply) Reset() { *m = SyncStrategyApply{} }
|
||||
func (*SyncStrategyApply) ProtoMessage() {}
|
||||
func (*SyncStrategyApply) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{66}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{66}
|
||||
}
|
||||
func (m *SyncStrategyApply) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1909,7 +1909,7 @@ var xxx_messageInfo_SyncStrategyApply proto.InternalMessageInfo
|
||||
func (m *SyncStrategyHook) Reset() { *m = SyncStrategyHook{} }
|
||||
func (*SyncStrategyHook) ProtoMessage() {}
|
||||
func (*SyncStrategyHook) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{67}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{67}
|
||||
}
|
||||
func (m *SyncStrategyHook) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1937,7 +1937,7 @@ var xxx_messageInfo_SyncStrategyHook proto.InternalMessageInfo
|
||||
func (m *SyncWindow) Reset() { *m = SyncWindow{} }
|
||||
func (*SyncWindow) ProtoMessage() {}
|
||||
func (*SyncWindow) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{68}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{68}
|
||||
}
|
||||
func (m *SyncWindow) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -1965,7 +1965,7 @@ var xxx_messageInfo_SyncWindow proto.InternalMessageInfo
|
||||
func (m *TLSClientConfig) Reset() { *m = TLSClientConfig{} }
|
||||
func (*TLSClientConfig) ProtoMessage() {}
|
||||
func (*TLSClientConfig) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_generated_622b11e42e23cc72, []int{69}
|
||||
return fileDescriptor_generated_b34c2064a02cfa33, []int{69}
|
||||
}
|
||||
func (m *TLSClientConfig) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -18635,10 +18635,10 @@ var (
|
||||
)
|
||||
|
||||
func init() {
|
||||
proto.RegisterFile("github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1/generated.proto", fileDescriptor_generated_622b11e42e23cc72)
|
||||
proto.RegisterFile("github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1/generated.proto", fileDescriptor_generated_b34c2064a02cfa33)
|
||||
}
|
||||
|
||||
var fileDescriptor_generated_622b11e42e23cc72 = []byte{
|
||||
var fileDescriptor_generated_b34c2064a02cfa33 = []byte{
|
||||
// 4679 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x3c, 0x5b, 0x8c, 0x1c, 0xd9,
|
||||
0x55, 0xae, 0x7e, 0xcf, 0x99, 0x87, 0x3d, 0x77, 0xd7, 0x9b, 0xce, 0x68, 0xe3, 0xb1, 0xca, 0x4a,
|
||||
|
||||
@@ -236,10 +236,12 @@ message ApplicationStatus {
|
||||
|
||||
repeated ApplicationCondition conditions = 5;
|
||||
|
||||
// ReconciledAt indicates when the application state was reconciled using the latest git version
|
||||
optional k8s.io.apimachinery.pkg.apis.meta.v1.Time reconciledAt = 6;
|
||||
|
||||
optional OperationState operationState = 7;
|
||||
|
||||
// ObservedAt indicates when the application state was updated without querying latest git state
|
||||
optional k8s.io.apimachinery.pkg.apis.meta.v1.Time observedAt = 8;
|
||||
|
||||
optional string sourceType = 9;
|
||||
|
||||
@@ -895,7 +895,8 @@ func schema_pkg_apis_application_v1alpha1_ApplicationStatus(ref common.Reference
|
||||
},
|
||||
"reconciledAt": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"),
|
||||
Description: "ReconciledAt indicates when the application state was reconciled using the latest git version",
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"),
|
||||
},
|
||||
},
|
||||
"operationState": {
|
||||
@@ -905,7 +906,8 @@ func schema_pkg_apis_application_v1alpha1_ApplicationStatus(ref common.Reference
|
||||
},
|
||||
"observedAt": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"),
|
||||
Description: "ObservedAt indicates when the application state was updated without querying latest git state",
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"),
|
||||
},
|
||||
},
|
||||
"sourceType": {
|
||||
|
||||
@@ -331,16 +331,18 @@ type ApplicationDestination struct {
|
||||
|
||||
// ApplicationStatus contains information about application sync, health status
|
||||
type ApplicationStatus struct {
|
||||
Resources []ResourceStatus `json:"resources,omitempty" protobuf:"bytes,1,opt,name=resources"`
|
||||
Sync SyncStatus `json:"sync,omitempty" protobuf:"bytes,2,opt,name=sync"`
|
||||
Health HealthStatus `json:"health,omitempty" protobuf:"bytes,3,opt,name=health"`
|
||||
History []RevisionHistory `json:"history,omitempty" protobuf:"bytes,4,opt,name=history"`
|
||||
Conditions []ApplicationCondition `json:"conditions,omitempty" protobuf:"bytes,5,opt,name=conditions"`
|
||||
ReconciledAt *metav1.Time `json:"reconciledAt,omitempty" protobuf:"bytes,6,opt,name=reconciledAt"`
|
||||
OperationState *OperationState `json:"operationState,omitempty" protobuf:"bytes,7,opt,name=operationState"`
|
||||
ObservedAt *metav1.Time `json:"observedAt,omitempty" protobuf:"bytes,8,opt,name=observedAt"`
|
||||
SourceType ApplicationSourceType `json:"sourceType,omitempty" protobuf:"bytes,9,opt,name=sourceType"`
|
||||
Summary ApplicationSummary `json:"summary,omitempty" protobuf:"bytes,10,opt,name=summary"`
|
||||
Resources []ResourceStatus `json:"resources,omitempty" protobuf:"bytes,1,opt,name=resources"`
|
||||
Sync SyncStatus `json:"sync,omitempty" protobuf:"bytes,2,opt,name=sync"`
|
||||
Health HealthStatus `json:"health,omitempty" protobuf:"bytes,3,opt,name=health"`
|
||||
History []RevisionHistory `json:"history,omitempty" protobuf:"bytes,4,opt,name=history"`
|
||||
Conditions []ApplicationCondition `json:"conditions,omitempty" protobuf:"bytes,5,opt,name=conditions"`
|
||||
// ReconciledAt indicates when the application state was reconciled using the latest git version
|
||||
ReconciledAt *metav1.Time `json:"reconciledAt,omitempty" protobuf:"bytes,6,opt,name=reconciledAt"`
|
||||
OperationState *OperationState `json:"operationState,omitempty" protobuf:"bytes,7,opt,name=operationState"`
|
||||
// ObservedAt indicates when the application state was updated without querying latest git state
|
||||
ObservedAt *metav1.Time `json:"observedAt,omitempty" protobuf:"bytes,8,opt,name=observedAt"`
|
||||
SourceType ApplicationSourceType `json:"sourceType,omitempty" protobuf:"bytes,9,opt,name=sourceType"`
|
||||
Summary ApplicationSummary `json:"summary,omitempty" protobuf:"bytes,10,opt,name=summary"`
|
||||
}
|
||||
|
||||
// Operation contains requested operation parameters.
|
||||
@@ -1250,8 +1252,20 @@ var validActions = map[string]bool{
|
||||
"*": true,
|
||||
}
|
||||
|
||||
var validActionPatterns = []*regexp.Regexp{
|
||||
regexp.MustCompile("action/.*"),
|
||||
}
|
||||
|
||||
func isValidAction(action string) bool {
|
||||
return validActions[action]
|
||||
if validActions[action] {
|
||||
return true
|
||||
}
|
||||
for i := range validActionPatterns {
|
||||
if validActionPatterns[i].MatchString(action) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func validatePolicy(proj string, role string, policy string) error {
|
||||
@@ -1277,7 +1291,7 @@ func validatePolicy(proj string, role string, policy string) error {
|
||||
}
|
||||
// object
|
||||
object := strings.Trim(policyComponents[4], " ")
|
||||
objectRegexp, err := regexp.Compile(fmt.Sprintf(`^%s/[*\w-]+$`, proj))
|
||||
objectRegexp, err := regexp.Compile(fmt.Sprintf(`^%s/[*\w-.]+$`, proj))
|
||||
if err != nil || !objectRegexp.MatchString(object) {
|
||||
return status.Errorf(codes.InvalidArgument, "invalid policy rule '%s': object must be of form '%s/*' or '%s/<APPNAME>', not '%s'", policy, proj, proj, object)
|
||||
}
|
||||
|
||||
@@ -257,8 +257,6 @@ func TestAppProject_InvalidPolicyRules(t *testing.T) {
|
||||
errmsg string
|
||||
}
|
||||
badPolicies := []badPolicy{
|
||||
// should have spaces
|
||||
{"p,proj:my-proj:my-role,applications,get,my-proj/*,allow", "syntax"},
|
||||
// incorrect form
|
||||
{"g, proj:my-proj:my-role, applications, get, my-proj/*, allow", "must be of the form: 'p, sub, res, act, obj, eft'"},
|
||||
{"p, not, enough, parts", "must be of the form: 'p, sub, res, act, obj, eft'"},
|
||||
@@ -305,11 +303,14 @@ func TestAppProject_ValidPolicyRules(t *testing.T) {
|
||||
"p, proj:my-proj:my-role, applications, get, my-proj/*-foo, allow",
|
||||
"p, proj:my-proj:my-role, applications, get, my-proj/foo-*, allow",
|
||||
"p, proj:my-proj:my-role, applications, get, my-proj/*-*, allow",
|
||||
"p, proj:my-proj:my-role, applications, get, my-proj/*.*, allow",
|
||||
"p, proj:my-proj:my-role, applications, *, my-proj/foo, allow",
|
||||
"p, proj:my-proj:my-role, applications, create, my-proj/foo, allow",
|
||||
"p, proj:my-proj:my-role, applications, update, my-proj/foo, allow",
|
||||
"p, proj:my-proj:my-role, applications, sync, my-proj/foo, allow",
|
||||
"p, proj:my-proj:my-role, applications, delete, my-proj/foo, allow",
|
||||
"p, proj:my-proj:my-role, applications, action/*, my-proj/foo, allow",
|
||||
"p, proj:my-proj:my-role, applications, action/apps/Deployment/restart, my-proj/foo, allow",
|
||||
}
|
||||
for _, good := range goodPolicies {
|
||||
p.Spec.Roles[0].Policies = []string{good}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"crypto/tls"
|
||||
"time"
|
||||
|
||||
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc"
|
||||
@@ -28,13 +29,16 @@ func (c *clientSet) NewRepoServerClient() (util.Closer, RepoServerServiceClient,
|
||||
grpc_retry.WithMax(3),
|
||||
grpc_retry.WithBackoff(grpc_retry.BackoffLinear(1000 * time.Millisecond)),
|
||||
}
|
||||
unaryInterceptors := []grpc.UnaryClientInterceptor{grpc_retry.UnaryClientInterceptor(retryOpts...)}
|
||||
if c.timeoutSeconds > 0 {
|
||||
unaryInterceptors = append(unaryInterceptors, argogrpc.WithTimeout(time.Duration(c.timeoutSeconds)*time.Second))
|
||||
}
|
||||
opts := []grpc.DialOption{
|
||||
grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{InsecureSkipVerify: true})),
|
||||
grpc.WithStreamInterceptor(grpc_retry.StreamClientInterceptor(retryOpts...)),
|
||||
grpc.WithUnaryInterceptor(grpc_retry.UnaryClientInterceptor(retryOpts...))}
|
||||
if c.timeoutSeconds > 0 {
|
||||
opts = append(opts, grpc.WithUnaryInterceptor(argogrpc.WithTimeout(time.Duration(c.timeoutSeconds)*time.Second)))
|
||||
grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(unaryInterceptors...)),
|
||||
}
|
||||
|
||||
conn, err := grpc.Dial(c.address, opts...)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to connect to repository service with address %s", c.address)
|
||||
|
||||
@@ -8,8 +8,9 @@ import (
|
||||
)
|
||||
|
||||
type MetricsServer struct {
|
||||
handler http.Handler
|
||||
gitRequestCounter *prometheus.CounterVec
|
||||
handler http.Handler
|
||||
gitRequestCounter *prometheus.CounterVec
|
||||
repoPendingRequestsGauge *prometheus.GaugeVec
|
||||
}
|
||||
|
||||
type GitRequestType string
|
||||
@@ -34,9 +35,19 @@ func NewMetricsServer() *MetricsServer {
|
||||
)
|
||||
registry.MustRegister(gitRequestCounter)
|
||||
|
||||
repoPendingRequestsGauge := prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "argocd_repo_pending_request_total",
|
||||
Help: "Number of pending requests requiring repository lock",
|
||||
},
|
||||
[]string{"repo"},
|
||||
)
|
||||
registry.MustRegister(repoPendingRequestsGauge)
|
||||
|
||||
return &MetricsServer{
|
||||
handler: promhttp.HandlerFor(registry, promhttp.HandlerOpts{}),
|
||||
gitRequestCounter: gitRequestCounter,
|
||||
handler: promhttp.HandlerFor(registry, promhttp.HandlerOpts{}),
|
||||
gitRequestCounter: gitRequestCounter,
|
||||
repoPendingRequestsGauge: repoPendingRequestsGauge,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,3 +59,11 @@ func (m *MetricsServer) GetHandler() http.Handler {
|
||||
func (m *MetricsServer) IncGitRequest(repo string, requestType GitRequestType) {
|
||||
m.gitRequestCounter.WithLabelValues(repo, string(requestType)).Inc()
|
||||
}
|
||||
|
||||
func (m *MetricsServer) IncPendingRepoRequest(repo string) {
|
||||
m.repoPendingRequestsGauge.WithLabelValues(repo).Inc()
|
||||
}
|
||||
|
||||
func (m *MetricsServer) DecPendingRepoRequest(repo string) {
|
||||
m.repoPendingRequestsGauge.WithLabelValues(repo).Dec()
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -12,7 +13,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/TomOnTime/utfutil"
|
||||
argoexec "github.com/argoproj/pkg/exec"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/google/go-jsonnet"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -23,6 +23,9 @@ import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
executil "github.com/argoproj/argo-cd/util/exec"
|
||||
"github.com/argoproj/argo-cd/util/security"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/reposerver/apiclient"
|
||||
@@ -31,7 +34,6 @@ import (
|
||||
"github.com/argoproj/argo-cd/util/app/discovery"
|
||||
argopath "github.com/argoproj/argo-cd/util/app/path"
|
||||
"github.com/argoproj/argo-cd/util/cache"
|
||||
"github.com/argoproj/argo-cd/util/config"
|
||||
"github.com/argoproj/argo-cd/util/git"
|
||||
"github.com/argoproj/argo-cd/util/helm"
|
||||
"github.com/argoproj/argo-cd/util/ksonnet"
|
||||
@@ -110,18 +112,19 @@ type operationSettings struct {
|
||||
|
||||
// runRepoOperation downloads either git folder or helm chart and executes specified operation
|
||||
func (s *Service) runRepoOperation(
|
||||
c context.Context,
|
||||
ctx context.Context,
|
||||
revision string,
|
||||
repo *v1alpha1.Repository,
|
||||
source *v1alpha1.ApplicationSource,
|
||||
getCached func(revision string) bool,
|
||||
operation func(appPath string, revision string) error,
|
||||
operation func(appPath, repoRoot, revision string) error,
|
||||
settings operationSettings) error {
|
||||
|
||||
var gitClient git.Client
|
||||
var err error
|
||||
revision := source.TargetRevision
|
||||
revision = util.FirstNonEmpty(revision, source.TargetRevision)
|
||||
if !source.IsHelm() {
|
||||
gitClient, revision, err = s.newClientResolveRevision(repo, source.TargetRevision)
|
||||
gitClient, revision, err = s.newClientResolveRevision(repo, revision)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -131,8 +134,11 @@ func (s *Service) runRepoOperation(
|
||||
return nil
|
||||
}
|
||||
|
||||
s.metricsServer.IncPendingRepoRequest(repo.Repo)
|
||||
defer s.metricsServer.DecPendingRepoRequest(repo.Repo)
|
||||
|
||||
if settings.sem != nil {
|
||||
err = settings.sem.Acquire(c, 1)
|
||||
err = settings.sem.Acquire(ctx, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -152,28 +158,28 @@ func (s *Service) runRepoOperation(
|
||||
return err
|
||||
}
|
||||
defer util.Close(closer)
|
||||
return operation(chartPath, revision)
|
||||
return operation(chartPath, chartPath, revision)
|
||||
} else {
|
||||
s.repoLock.Lock(gitClient.Root())
|
||||
defer s.repoLock.Unlock(gitClient.Root())
|
||||
// double-check locking
|
||||
if !settings.noCache && getCached(revision) {
|
||||
return nil
|
||||
}
|
||||
revision, err = checkoutRevision(gitClient, revision)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appPath, err := argopath.Path(gitClient.Root(), source.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return operation(appPath, gitClient.Root(), revision)
|
||||
}
|
||||
s.repoLock.Lock(gitClient.Root())
|
||||
defer s.repoLock.Unlock(gitClient.Root())
|
||||
|
||||
if !settings.noCache && getCached(revision) {
|
||||
return nil
|
||||
}
|
||||
|
||||
revision, err = checkoutRevision(gitClient, revision)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appPath, err := argopath.Path(gitClient.Root(), source.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return operation(appPath, revision)
|
||||
}
|
||||
|
||||
func (s *Service) GenerateManifest(c context.Context, q *apiclient.ManifestRequest) (*apiclient.ManifestResponse, error) {
|
||||
var res *apiclient.ManifestResponse
|
||||
func (s *Service) GenerateManifest(ctx context.Context, q *apiclient.ManifestRequest) (*apiclient.ManifestResponse, error) {
|
||||
res := &apiclient.ManifestResponse{}
|
||||
|
||||
getCached := func(revision string) bool {
|
||||
err := s.cache.GetManifests(revision, q.ApplicationSource, q.Namespace, q.AppLabelKey, q.AppLabelValue, &res)
|
||||
@@ -188,9 +194,9 @@ func (s *Service) GenerateManifest(c context.Context, q *apiclient.ManifestReque
|
||||
}
|
||||
return false
|
||||
}
|
||||
err := s.runRepoOperation(c, q.Repo, q.ApplicationSource, getCached, func(appPath string, revision string) error {
|
||||
err := s.runRepoOperation(ctx, q.Revision, q.Repo, q.ApplicationSource, getCached, func(appPath, repoRoot, revision string) error {
|
||||
var err error
|
||||
res, err = GenerateManifests(appPath, q)
|
||||
res, err = GenerateManifests(appPath, repoRoot, q)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -212,7 +218,7 @@ func getHelmRepos(repositories []*v1alpha1.Repository) []helm.HelmRepository {
|
||||
return repos
|
||||
}
|
||||
|
||||
func helmTemplate(appPath string, q *apiclient.ManifestRequest) ([]*unstructured.Unstructured, error) {
|
||||
func helmTemplate(appPath string, repoRoot string, q *apiclient.ManifestRequest) ([]*unstructured.Unstructured, error) {
|
||||
templateOpts := &helm.TemplateOpts{
|
||||
Name: q.AppLabelValue,
|
||||
Namespace: q.Namespace,
|
||||
@@ -220,14 +226,43 @@ func helmTemplate(appPath string, q *apiclient.ManifestRequest) ([]*unstructured
|
||||
Set: map[string]string{},
|
||||
SetString: map[string]string{},
|
||||
}
|
||||
|
||||
appHelm := q.ApplicationSource.Helm
|
||||
if appHelm != nil {
|
||||
if appHelm.ReleaseName != "" {
|
||||
templateOpts.Name = appHelm.ReleaseName
|
||||
}
|
||||
templateOpts.Values = appHelm.ValueFiles
|
||||
|
||||
for _, val := range appHelm.ValueFiles {
|
||||
// If val is not a URL, run it against the directory enforcer. If it is a URL, use it without checking
|
||||
if _, err := url.ParseRequestURI(val); err != nil {
|
||||
|
||||
// Ensure that the repo root provided is absolute
|
||||
absRepoPath, err := filepath.Abs(repoRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If the path to the file is relative, join it with the current working directory (appPath)
|
||||
path := val
|
||||
if !filepath.IsAbs(path) {
|
||||
absWorkDir, err := filepath.Abs(appPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path = filepath.Join(absWorkDir, path)
|
||||
}
|
||||
|
||||
_, err = security.EnforceToCurrentRoot(absRepoPath, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
templateOpts.Values = append(templateOpts.Values, val)
|
||||
}
|
||||
|
||||
if appHelm.Values != "" {
|
||||
file, err := ioutil.TempFile(appPath, "values-*.yaml")
|
||||
file, err := ioutil.TempFile("", "values-*.yaml")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -239,6 +274,7 @@ func helmTemplate(appPath string, q *apiclient.ManifestRequest) ([]*unstructured
|
||||
}
|
||||
templateOpts.Values = append(templateOpts.Values, p)
|
||||
}
|
||||
|
||||
for _, p := range appHelm.Parameters {
|
||||
if p.ForceString {
|
||||
templateOpts.SetString[p.Name] = p.Value
|
||||
@@ -278,7 +314,7 @@ func helmTemplate(appPath string, q *apiclient.ManifestRequest) ([]*unstructured
|
||||
}
|
||||
|
||||
// GenerateManifests generates manifests from a path
|
||||
func GenerateManifests(appPath string, q *apiclient.ManifestRequest) (*apiclient.ManifestResponse, error) {
|
||||
func GenerateManifests(appPath, repoRoot string, q *apiclient.ManifestRequest) (*apiclient.ManifestResponse, error) {
|
||||
var targetObjs []*unstructured.Unstructured
|
||||
var dest *v1alpha1.ApplicationDestination
|
||||
|
||||
@@ -291,7 +327,7 @@ func GenerateManifests(appPath string, q *apiclient.ManifestRequest) (*apiclient
|
||||
case v1alpha1.ApplicationSourceTypeKsonnet:
|
||||
targetObjs, dest, err = ksShow(q.AppLabelKey, appPath, q.ApplicationSource.Ksonnet)
|
||||
case v1alpha1.ApplicationSourceTypeHelm:
|
||||
targetObjs, err = helmTemplate(appPath, q)
|
||||
targetObjs, err = helmTemplate(appPath, repoRoot, q)
|
||||
case v1alpha1.ApplicationSourceTypeKustomize:
|
||||
k := kustomize.NewKustomizeApp(appPath, q.Repo.GetGitCreds(), repoURL)
|
||||
targetObjs, _, err = k.Build(q.ApplicationSource.Kustomize, q.KustomizeOptions)
|
||||
@@ -527,7 +563,7 @@ func runCommand(command v1alpha1.Command, path string, env []string) (string, er
|
||||
cmd := exec.Command(command.Command[0], append(command.Command[1:], command.Args...)...)
|
||||
cmd.Env = env
|
||||
cmd.Dir = path
|
||||
return argoexec.RunCommandExt(cmd, config.CmdOpts())
|
||||
return executil.Run(cmd)
|
||||
}
|
||||
|
||||
func findPlugin(plugins []*v1alpha1.ConfigManagementPlugin, name string) *v1alpha1.ConfigManagementPlugin {
|
||||
@@ -568,23 +604,23 @@ func runConfigManagementPlugin(appPath string, q *apiclient.ManifestRequest, cre
|
||||
}
|
||||
|
||||
func (s *Service) GetAppDetails(ctx context.Context, q *apiclient.RepoServerAppDetailsQuery) (*apiclient.RepoAppDetailsResponse, error) {
|
||||
var res apiclient.RepoAppDetailsResponse
|
||||
res := &apiclient.RepoAppDetailsResponse{}
|
||||
getCached := func(revision string) bool {
|
||||
err := s.cache.GetAppDetails(revision, q.Source, &res)
|
||||
if err == nil {
|
||||
log.Infof("manifest cache hit: %s/%s", revision, q.Source.Path)
|
||||
log.Infof("app details cache hit: %s/%s", revision, q.Source.Path)
|
||||
return true
|
||||
} else {
|
||||
if err != cache.ErrCacheMiss {
|
||||
log.Warnf("manifest cache error %s: %v", revision, q.Source)
|
||||
log.Warnf("app details cache error %s: %v", revision, q.Source)
|
||||
} else {
|
||||
log.Infof("manifest cache miss: %s/%s", revision, q.Source)
|
||||
log.Infof("app details cache miss: %s/%s", revision, q.Source)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
err := s.runRepoOperation(ctx, q.Repo, q.Source, getCached, func(appPath string, revision string) error {
|
||||
err := s.runRepoOperation(ctx, q.Source.TargetRevision, q.Repo, q.Source, getCached, func(appPath, repoRoot, revision string) error {
|
||||
appSourceType, err := GetAppSourceType(q.Source, appPath)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -673,32 +709,33 @@ func (s *Service) GetAppDetails(ctx context.Context, q *apiclient.RepoServerAppD
|
||||
return nil
|
||||
}, operationSettings{})
|
||||
|
||||
return &res, err
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (s *Service) GetRevisionMetadata(ctx context.Context, q *apiclient.RepoServerRevisionMetadataRequest) (*v1alpha1.RevisionMetadata, error) {
|
||||
gitClient, commitSHA, err := s.newClientResolveRevision(q.Repo, q.Revision)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if !git.IsCommitSHA(q.Revision) {
|
||||
return nil, fmt.Errorf("revision %s must be resolved", q.Revision)
|
||||
}
|
||||
|
||||
metadata, err := s.cache.GetRevisionMetadata(q.Repo.Repo, commitSHA)
|
||||
metadata, err := s.cache.GetRevisionMetadata(q.Repo.Repo, q.Revision)
|
||||
if err == nil {
|
||||
log.Infof("manifest cache hit: %s/%s", q.Repo.Repo, commitSHA)
|
||||
log.Infof("revision metadata cache hit: %s/%s", q.Repo.Repo, q.Revision)
|
||||
return metadata, nil
|
||||
} else {
|
||||
if err != cache.ErrCacheMiss {
|
||||
log.Warnf("manifest cache error %s/%s: %v", q.Repo.Repo, commitSHA, err)
|
||||
log.Warnf("revision metadata cache error %s/%s: %v", q.Repo.Repo, q.Revision, err)
|
||||
} else {
|
||||
log.Infof("manifest cache miss: %s/%s", q.Repo.Repo, commitSHA)
|
||||
log.Infof("revision metadata cache miss: %s/%s", q.Repo.Repo, q.Revision)
|
||||
}
|
||||
}
|
||||
|
||||
commitSHA, err = checkoutRevision(gitClient, commitSHA)
|
||||
gitClient, _, err := s.newClientResolveRevision(q.Repo, q.Revision)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m, err := gitClient.RevisionMetadata(commitSHA)
|
||||
_, err = checkoutRevision(gitClient, q.Revision)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m, err := gitClient.RevisionMetadata(q.Revision)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ func newServiceWithMocks(root string) (*Service, *gitmocks.Client, *helmmocks.Cl
|
||||
gitClient.On("CommitSHA").Return(mock.Anything, nil)
|
||||
gitClient.On("Root").Return(root)
|
||||
|
||||
helmClient.On("CleanChartCache", mock.Anything, mock.Anything).Return(nil)
|
||||
helmClient.On("ExtractChart", mock.Anything, mock.Anything).Return(func(chart string, version string) string {
|
||||
return path.Join(root, chart)
|
||||
}, util.NewCloser(func() error {
|
||||
@@ -81,13 +82,26 @@ func TestGenerateYamlManifestInDir(t *testing.T) {
|
||||
assert.Equal(t, countOfManifests, len(res1.Manifests))
|
||||
|
||||
// this will test concatenated manifests to verify we split YAMLs correctly
|
||||
res2, err := GenerateManifests("./testdata/concatenated", &q)
|
||||
assert.Nil(t, err)
|
||||
res2, err := GenerateManifests("./testdata/concatenated", "/", &q)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, len(res2.Manifests))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateManifestsUseExactRevision(t *testing.T) {
|
||||
service, gitClient, _ := newServiceWithMocks(".")
|
||||
|
||||
src := argoappv1.ApplicationSource{Path: "./testdata/recurse", Directory: &argoappv1.ApplicationSourceDirectory{Recurse: true}}
|
||||
|
||||
q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src, Revision: "abc"}
|
||||
|
||||
res1, err := service.GenerateManifest(context.Background(), &q)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, len(res1.Manifests))
|
||||
assert.Equal(t, gitClient.Calls[0].Arguments[0], "abc")
|
||||
}
|
||||
|
||||
func TestRecurseManifestsInDir(t *testing.T) {
|
||||
service := newService(".")
|
||||
|
||||
@@ -193,11 +207,10 @@ func TestGenerateHelmWithValues(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
// This tests against a path traversal attack. The requested value file (`../minio/values.yaml`) is outside the
|
||||
// app path (`./util/helm/testdata/redis`)
|
||||
// The requested value file (`../minio/values.yaml`) is outside the app path (`./util/helm/testdata/redis`), however
|
||||
// since the requested value is sill under the repo directory (`~/go/src/github.com/argoproj/argo-cd`), it is allowed
|
||||
func TestGenerateHelmWithValuesDirectoryTraversal(t *testing.T) {
|
||||
service := newService("../..")
|
||||
|
||||
_, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
|
||||
Repo: &argoappv1.Repository{},
|
||||
AppLabelValue: "test",
|
||||
@@ -209,7 +222,106 @@ func TestGenerateHelmWithValuesDirectoryTraversal(t *testing.T) {
|
||||
},
|
||||
},
|
||||
})
|
||||
assert.Error(t, err)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test the case where the path is "."
|
||||
service = newService("./testdata/my-chart")
|
||||
_, err = service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
|
||||
Repo: &argoappv1.Repository{},
|
||||
AppLabelValue: "test",
|
||||
ApplicationSource: &argoappv1.ApplicationSource{
|
||||
Path: ".",
|
||||
},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// This is a Helm first-class app with a values file inside the repo directory
|
||||
// (`~/go/src/github.com/argoproj/argo-cd/reposerver/repository`), so it is allowed
|
||||
func TestHelmManifestFromChartRepoWithValueFile(t *testing.T) {
|
||||
service := newService(".")
|
||||
source := &argoappv1.ApplicationSource{
|
||||
Chart: "./testdata/my-chart",
|
||||
TargetRevision: "1.1.0",
|
||||
Helm: &argoappv1.ApplicationSourceHelm{
|
||||
ValueFiles: []string{"./my-chart-values.yaml"},
|
||||
},
|
||||
}
|
||||
request := &apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: source, NoCache: true}
|
||||
response, err := service.GenerateManifest(context.Background(), request)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, response)
|
||||
assert.Equal(t, &apiclient.ManifestResponse{
|
||||
Manifests: []string{"{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"my-map\"}}"},
|
||||
Namespace: "",
|
||||
Server: "",
|
||||
Revision: "1.1.0",
|
||||
SourceType: "Helm",
|
||||
}, response)
|
||||
}
|
||||
|
||||
// This is a Helm first-class app with a values file outside the repo directory
|
||||
// (`~/go/src/github.com/argoproj/argo-cd/reposerver/repository`), so it is not allowed
|
||||
func TestHelmManifestFromChartRepoWithValueFileOutsideRepo(t *testing.T) {
|
||||
service := newService(".")
|
||||
source := &argoappv1.ApplicationSource{
|
||||
Chart: "./testdata/my-chart",
|
||||
TargetRevision: "1.0.0",
|
||||
Helm: &argoappv1.ApplicationSourceHelm{
|
||||
ValueFiles: []string{"../my-chart-2/my-chart-2-values.yaml"},
|
||||
},
|
||||
}
|
||||
request := &apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: source, NoCache: true}
|
||||
_, err := service.GenerateManifest(context.Background(), request)
|
||||
assert.Error(t, err, "should be on or under current directory")
|
||||
}
|
||||
|
||||
func TestGenerateHelmWithURL(t *testing.T) {
|
||||
service := newService("../..")
|
||||
|
||||
_, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
|
||||
Repo: &argoappv1.Repository{},
|
||||
AppLabelValue: "test",
|
||||
ApplicationSource: &argoappv1.ApplicationSource{
|
||||
Path: "./util/helm/testdata/redis",
|
||||
Helm: &argoappv1.ApplicationSourceHelm{
|
||||
ValueFiles: []string{"https://raw.githubusercontent.com/argoproj/argocd-example-apps/master/helm-guestbook/values.yaml"},
|
||||
Values: `cluster: {slaveCount: 2}`,
|
||||
},
|
||||
},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// The requested value file (`../../../../../minio/values.yaml`) is outside the repo directory
|
||||
// (`~/go/src/github.com/argoproj/argo-cd`), so it is blocked
|
||||
func TestGenerateHelmWithValuesDirectoryTraversalOutsideRepo(t *testing.T) {
|
||||
service := newService("../..")
|
||||
_, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
|
||||
Repo: &argoappv1.Repository{},
|
||||
AppLabelValue: "test",
|
||||
ApplicationSource: &argoappv1.ApplicationSource{
|
||||
Path: "./util/helm/testdata/redis",
|
||||
Helm: &argoappv1.ApplicationSourceHelm{
|
||||
ValueFiles: []string{"../../../../../minio/values.yaml"},
|
||||
Values: `cluster: {slaveCount: 2}`,
|
||||
},
|
||||
},
|
||||
})
|
||||
assert.Error(t, err, "should be on or under current directory")
|
||||
|
||||
service = newService("./testdata/my-chart")
|
||||
_, err = service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
|
||||
Repo: &argoappv1.Repository{},
|
||||
AppLabelValue: "test",
|
||||
ApplicationSource: &argoappv1.ApplicationSource{
|
||||
Path: ".",
|
||||
Helm: &argoappv1.ApplicationSourceHelm{
|
||||
ValueFiles: []string{"../my-chart-2/values.yaml"},
|
||||
Values: `cluster: {slaveCount: 2}`,
|
||||
},
|
||||
},
|
||||
})
|
||||
assert.Error(t, err, "should be on or under current directory")
|
||||
}
|
||||
|
||||
@@ -294,7 +406,7 @@ func TestGenerateFromUTF16(t *testing.T) {
|
||||
q := apiclient.ManifestRequest{
|
||||
ApplicationSource: &argoappv1.ApplicationSource{},
|
||||
}
|
||||
res1, err := GenerateManifests("./testdata/utf-16", &q)
|
||||
res1, err := GenerateManifests("./testdata/utf-16", "/", &q)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, len(res1.Manifests))
|
||||
}
|
||||
@@ -311,6 +423,8 @@ func TestListApps(t *testing.T) {
|
||||
"invalid-kustomize": "Kustomize",
|
||||
"kustomization_yaml": "Kustomize",
|
||||
"kustomization_yml": "Kustomize",
|
||||
"my-chart": "Helm",
|
||||
"my-chart-2": "Helm",
|
||||
}
|
||||
assert.Equal(t, expectedApps, res.Apps)
|
||||
}
|
||||
@@ -395,7 +509,7 @@ func TestGetRevisionMetadata(t *testing.T) {
|
||||
|
||||
res, err := service.GetRevisionMetadata(context.Background(), &apiclient.RepoServerRevisionMetadataRequest{
|
||||
Repo: &argoappv1.Repository{},
|
||||
Revision: "123",
|
||||
Revision: "c0b400fc458875d925171398f9ba9eabd5529923",
|
||||
})
|
||||
|
||||
assert.NoError(t, err)
|
||||
|
||||
2
reposerver/repository/testdata/my-chart-2/Chart.yaml
vendored
Normal file
2
reposerver/repository/testdata/my-chart-2/Chart.yaml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
name: my-chart
|
||||
version: 1.1.0
|
||||
1
reposerver/repository/testdata/my-chart-2/my-chart-2-values.yaml
vendored
Normal file
1
reposerver/repository/testdata/my-chart-2/my-chart-2-values.yaml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
app: go
|
||||
4
reposerver/repository/testdata/my-chart-2/templates/my-map.yaml
vendored
Normal file
4
reposerver/repository/testdata/my-chart-2/templates/my-map.yaml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: my-map
|
||||
2
reposerver/repository/testdata/my-chart/Chart.yaml
vendored
Normal file
2
reposerver/repository/testdata/my-chart/Chart.yaml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
name: my-chart
|
||||
version: 1.1.0
|
||||
0
reposerver/repository/testdata/my-chart/my-chart-values.yaml
vendored
Normal file
0
reposerver/repository/testdata/my-chart/my-chart-values.yaml
vendored
Normal file
4
reposerver/repository/testdata/my-chart/templates/my-map.yaml
vendored
Normal file
4
reposerver/repository/testdata/my-chart/templates/my-map.yaml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: my-map
|
||||
32
resource_customizations/argoproj.io/AnalysisRun/health.lua
Normal file
32
resource_customizations/argoproj.io/AnalysisRun/health.lua
Normal file
@@ -0,0 +1,32 @@
|
||||
hs = {}
|
||||
if obj.status ~= nil then
|
||||
if obj.status.phase == "Pending" then
|
||||
hs.status = "Progressing"
|
||||
hs.message = "Analysis run is running"
|
||||
end
|
||||
if obj.status.phase == "Running" then
|
||||
hs.status = "Progressing"
|
||||
hs.message = "Analysis run is running"
|
||||
end
|
||||
if obj.status.phase == "Successful" then
|
||||
hs.status = "Healthy"
|
||||
hs.message = "Analysis run completed successfully"
|
||||
end
|
||||
if obj.status.phase == "Failed" then
|
||||
hs.status = "Degraded"
|
||||
hs.message = "Analysis run failed"
|
||||
end
|
||||
if obj.status.phase == "Error" then
|
||||
hs.status = "Degraded"
|
||||
hs.message = "Analysis run had an error"
|
||||
end
|
||||
if obj.status.phase == "Inconclusive" then
|
||||
hs.status = "Unknown"
|
||||
hs.message = "Analysis run was inconclusive"
|
||||
end
|
||||
return hs
|
||||
end
|
||||
|
||||
hs.status = "Progressing"
|
||||
hs.message = "Waiting for analysis run to finish: status has not been reconciled."
|
||||
return hs
|
||||
@@ -0,0 +1,29 @@
|
||||
tests:
|
||||
- healthStatus:
|
||||
status: Progressing
|
||||
message: "Analysis run is running"
|
||||
inputPath: testdata/pendingAnalysisRun.yaml
|
||||
- healthStatus:
|
||||
status: Progressing
|
||||
message: "Waiting for analysis run to finish: status has not been reconciled."
|
||||
inputPath: testdata/noStatusAnalysisRun.yaml
|
||||
- healthStatus:
|
||||
status: Progressing
|
||||
message: "Analysis run is running"
|
||||
inputPath: testdata/runningAnalysisRun.yaml
|
||||
- healthStatus:
|
||||
status: Healthy
|
||||
message: "Analysis run completed successfully"
|
||||
inputPath: testdata/successfulAnalysisRun.yaml
|
||||
- healthStatus:
|
||||
status: Degraded
|
||||
message: "Analysis run failed"
|
||||
inputPath: testdata/failedAnalysisRun.yaml
|
||||
- healthStatus:
|
||||
status: Degraded
|
||||
message: "Analysis run had an error"
|
||||
inputPath: testdata/errorAnalysisRun.yaml
|
||||
- healthStatus:
|
||||
status: Unknown
|
||||
message: "Analysis run was inconclusive"
|
||||
inputPath: testdata/inconclusiveAnalysisRun.yaml
|
||||
47
resource_customizations/argoproj.io/AnalysisRun/testdata/errorAnalysisRun.yaml
vendored
Normal file
47
resource_customizations/argoproj.io/AnalysisRun/testdata/errorAnalysisRun.yaml
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: AnalysisRun
|
||||
metadata:
|
||||
name: canary-demo-analysis-template-6c6bb7cf6f-btpgc
|
||||
namespace: default
|
||||
spec:
|
||||
analysisSpec:
|
||||
metrics:
|
||||
- failureCondition: result < 92
|
||||
interval: 10
|
||||
name: memory-usage
|
||||
provider:
|
||||
prometheus:
|
||||
address: 'http://prometheus-operator-prometheus.prometheus-operator:9090'
|
||||
query: >
|
||||
sum(rate(nginx_ingress_controller_requests{ingress="canary-demo-preview",status!~"[4-5].*"}[2m]))
|
||||
/
|
||||
sum(rate(nginx_ingress_controller_requests{ingress="canary-demo-preview"}[2m]))
|
||||
successCondition: result > 95
|
||||
status:
|
||||
metricResults:
|
||||
- consecutiveError: 5
|
||||
error: 5
|
||||
measurements:
|
||||
- finishedAt: '2019-10-28T18:13:01Z'
|
||||
startedAt: '2019-10-28T18:13:01Z'
|
||||
phase: Error
|
||||
value: '[0.9832775919732442]'
|
||||
- finishedAt: '2019-10-28T18:13:11Z'
|
||||
startedAt: '2019-10-28T18:13:11Z'
|
||||
phase: Error
|
||||
value: '[0.9832775919732442]'
|
||||
- finishedAt: '2019-10-28T18:13:21Z'
|
||||
startedAt: '2019-10-28T18:13:21Z'
|
||||
phase: Error
|
||||
value: '[0.9722530521642618]'
|
||||
- finishedAt: '2019-10-28T18:13:31Z'
|
||||
startedAt: '2019-10-28T18:13:31Z'
|
||||
phase: Error
|
||||
value: '[0.9722530521642618]'
|
||||
- finishedAt: '2019-10-28T18:13:41Z'
|
||||
startedAt: '2019-10-28T18:13:41Z'
|
||||
phase: Error
|
||||
value: '[0.9722530521642618]'
|
||||
name: memory-usage
|
||||
phase: Error
|
||||
phase: Error
|
||||
31
resource_customizations/argoproj.io/AnalysisRun/testdata/failedAnalysisRun.yaml
vendored
Normal file
31
resource_customizations/argoproj.io/AnalysisRun/testdata/failedAnalysisRun.yaml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: AnalysisRun
|
||||
metadata:
|
||||
name: canary-demo-analysis-template-6c6bb7cf6f-9k5rj
|
||||
namespace: default
|
||||
spec:
|
||||
analysisSpec:
|
||||
metrics:
|
||||
- failureCondition: len(result) > 0
|
||||
interval: 10
|
||||
name: memory-usage
|
||||
provider:
|
||||
prometheus:
|
||||
address: 'http://prometheus-operator-prometheus.prometheus-operator:9090'
|
||||
query: >
|
||||
sum(rate(nginx_ingress_controller_requests{ingress="canary-demo-preview",status!~"[4-5].*"}[2m]))
|
||||
/
|
||||
sum(rate(nginx_ingress_controller_requests{ingress="canary-demo-preview"}[2m]))
|
||||
successCondition: len(result) > 0
|
||||
status:
|
||||
metricResults:
|
||||
- count: 1
|
||||
failed: 1
|
||||
measurements:
|
||||
- finishedAt: '2019-10-28T18:23:23Z'
|
||||
startedAt: '2019-10-28T18:23:23Z'
|
||||
phase: Failed
|
||||
value: '[0.9768211920529802]'
|
||||
name: memory-usage
|
||||
phase: Failed
|
||||
phase: Failed
|
||||
31
resource_customizations/argoproj.io/AnalysisRun/testdata/inconclusiveAnalysisRun.yaml
vendored
Normal file
31
resource_customizations/argoproj.io/AnalysisRun/testdata/inconclusiveAnalysisRun.yaml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: AnalysisRun
|
||||
metadata:
|
||||
name: canary-demo-analysis-template-6c6bb7cf6f-ddvn8
|
||||
namespace: default
|
||||
spec:
|
||||
analysisSpec:
|
||||
metrics:
|
||||
- failureCondition: len(result) == 0
|
||||
interval: 10
|
||||
name: memory-usage
|
||||
provider:
|
||||
prometheus:
|
||||
address: 'http://prometheus-operator-prometheus.prometheus-operator:9090'
|
||||
query: >
|
||||
sum(rate(nginx_ingress_controller_requests{ingress="canary-demo-preview",status!~"[4-5].*"}[2m]))
|
||||
/
|
||||
sum(rate(nginx_ingress_controller_requests{ingress="canary-demo-preview"}[2m]))
|
||||
successCondition: len(result) == 0
|
||||
status:
|
||||
metricResults:
|
||||
- count: 1
|
||||
inconclusive: 1
|
||||
measurements:
|
||||
- finishedAt: '2019-10-28T18:24:31Z'
|
||||
startedAt: '2019-10-28T18:24:31Z'
|
||||
phase: Inconclusive
|
||||
value: '[0.9744444444444443]'
|
||||
name: memory-usage
|
||||
phase: Inconclusive
|
||||
phase: Inconclusive
|
||||
19
resource_customizations/argoproj.io/AnalysisRun/testdata/noStatusAnalysisRun.yaml
vendored
Normal file
19
resource_customizations/argoproj.io/AnalysisRun/testdata/noStatusAnalysisRun.yaml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: AnalysisRun
|
||||
metadata:
|
||||
name: canary-demo-analysis-template-6c6bb7cf6f-9k5rj
|
||||
namespace: default
|
||||
spec:
|
||||
analysisSpec:
|
||||
metrics:
|
||||
- failureCondition: len(result) > 0
|
||||
interval: 10
|
||||
name: memory-usage
|
||||
provider:
|
||||
prometheus:
|
||||
address: 'http://prometheus-operator-prometheus.prometheus-operator:9090'
|
||||
query: >
|
||||
sum(rate(nginx_ingress_controller_requests{ingress="canary-demo-preview",status!~"[4-5].*"}[2m]))
|
||||
/
|
||||
sum(rate(nginx_ingress_controller_requests{ingress="canary-demo-preview"}[2m]))
|
||||
successCondition: len(result) > 0
|
||||
17
resource_customizations/argoproj.io/AnalysisRun/testdata/pendingAnalysisRun.yaml
vendored
Normal file
17
resource_customizations/argoproj.io/AnalysisRun/testdata/pendingAnalysisRun.yaml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: AnalysisRun
|
||||
metadata:
|
||||
name: analysis-template
|
||||
spec:
|
||||
metrics:
|
||||
- name: memory-usage
|
||||
interval: 10
|
||||
successCondition: result > 95
|
||||
failureCondition: result < 92
|
||||
provider:
|
||||
prometheus:
|
||||
address: http://prometheus-operator-prometheus.prometheus-operator:9090
|
||||
query: |
|
||||
sum(rate(nginx_ingress_controller_requests{ingress="canary-demo-preview",status!~"[4-5].*"}[2m])) / sum(rate(nginx_ingress_controller_requests{ingress="canary-demo-preview"}[2m]))
|
||||
status:
|
||||
phase: Pending
|
||||
35
resource_customizations/argoproj.io/AnalysisRun/testdata/runningAnalysisRun.yaml
vendored
Normal file
35
resource_customizations/argoproj.io/AnalysisRun/testdata/runningAnalysisRun.yaml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: AnalysisRun
|
||||
metadata:
|
||||
name: canary-demo-analysis-template-6c6bb7cf6f-5bpxj
|
||||
namespace: default
|
||||
spec:
|
||||
analysisSpec:
|
||||
metrics:
|
||||
- failureCondition: len(result) == 0
|
||||
interval: 10
|
||||
name: memory-usage
|
||||
provider:
|
||||
prometheus:
|
||||
address: 'http://prometheus-operator-prometheus.prometheus-operator:9090'
|
||||
query: >
|
||||
sum(rate(nginx_ingress_controller_requests{ingress="canary-demo-preview",status!~"[4-5].*"}[2m]))
|
||||
/
|
||||
sum(rate(nginx_ingress_controller_requests{ingress="canary-demo-preview"}[2m]))
|
||||
successCondition: len(result) > 0
|
||||
status:
|
||||
metricResults:
|
||||
- count: 2
|
||||
measurements:
|
||||
- finishedAt: '2019-10-28T18:22:05Z'
|
||||
startedAt: '2019-10-28T18:22:05Z'
|
||||
phase: Successful
|
||||
value: '[0.9721293199554069]'
|
||||
- finishedAt: '2019-10-28T18:22:15Z'
|
||||
startedAt: '2019-10-28T18:22:15Z'
|
||||
phase: Successful
|
||||
value: '[0.9721293199554069]'
|
||||
name: memory-usage
|
||||
phase: Running
|
||||
successful: 2
|
||||
phase: Running
|
||||
30
resource_customizations/argoproj.io/AnalysisRun/testdata/successfulAnalysisRun.yaml
vendored
Normal file
30
resource_customizations/argoproj.io/AnalysisRun/testdata/successfulAnalysisRun.yaml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: AnalysisRun
|
||||
metadata:
|
||||
name: canary-demo-analysis-template-6c6bb7cf6f-zvcmx
|
||||
namespace: default
|
||||
spec:
|
||||
analysisSpec:
|
||||
metrics:
|
||||
- failureCondition: len(result) == 0
|
||||
name: memory-usage
|
||||
provider:
|
||||
prometheus:
|
||||
address: 'http://prometheus-operator-prometheus.prometheus-operator:9090'
|
||||
query: >
|
||||
sum(rate(nginx_ingress_controller_requests{ingress="canary-demo-preview",status!~"[4-5].*"}[2m]))
|
||||
/
|
||||
sum(rate(nginx_ingress_controller_requests{ingress="canary-demo-preview"}[2m]))
|
||||
successCondition: len(result) > 0
|
||||
status:
|
||||
metricResults:
|
||||
- count: 1
|
||||
measurements:
|
||||
- finishedAt: '2019-10-28T18:20:37Z'
|
||||
startedAt: '2019-10-28T18:20:37Z'
|
||||
phase: Successful
|
||||
value: '[0.965324384787472]'
|
||||
name: memory-usage
|
||||
phase: Successful
|
||||
successful: 1
|
||||
phase: Successful
|
||||
28
resource_customizations/argoproj.io/Experiment/health.lua
Normal file
28
resource_customizations/argoproj.io/Experiment/health.lua
Normal file
@@ -0,0 +1,28 @@
|
||||
hs = {}
|
||||
if obj.status ~= nil then
|
||||
if obj.status.phase == "Pending" then
|
||||
hs.status = "Progressing"
|
||||
hs.message = "Experiment is pending"
|
||||
end
|
||||
if obj.status.phase == "Running" then
|
||||
hs.status = "Progressing"
|
||||
hs.message = "Experiment is running"
|
||||
end
|
||||
if obj.status.phase == "Successful" then
|
||||
hs.status = "Healthy"
|
||||
hs.message = "Experiment is successful"
|
||||
end
|
||||
if obj.status.phase == "Failed" then
|
||||
hs.status = "Degraded"
|
||||
hs.message = "Experiment has failed"
|
||||
end
|
||||
if obj.status.phase == "Error" then
|
||||
hs.status = "Degraded"
|
||||
hs.message = "Experiment had an error"
|
||||
end
|
||||
return hs
|
||||
end
|
||||
|
||||
hs.status = "Progressing"
|
||||
hs.message = "Waiting for experiment to finish: status has not been reconciled."
|
||||
return hs
|
||||
@@ -0,0 +1,25 @@
|
||||
tests:
|
||||
- healthStatus:
|
||||
status: Progressing
|
||||
message: "Experiment is pending"
|
||||
inputPath: testdata/pendingExperiment.yaml
|
||||
- healthStatus:
|
||||
status: Progressing
|
||||
message: "Waiting for experiment to finish: status has not been reconciled."
|
||||
inputPath: testdata/noStatusExperiment.yaml
|
||||
- healthStatus:
|
||||
status: Progressing
|
||||
message: "Experiment is running"
|
||||
inputPath: testdata/runningExperiment.yaml
|
||||
- healthStatus:
|
||||
status: Healthy
|
||||
message: "Experiment is successful"
|
||||
inputPath: testdata/successfulExperiment.yaml
|
||||
- healthStatus:
|
||||
status: Degraded
|
||||
message: "Experiment has failed"
|
||||
inputPath: testdata/failedExperiment.yaml
|
||||
- healthStatus:
|
||||
status: Degraded
|
||||
message: "Experiment had an error"
|
||||
inputPath: testdata/errorExperiment.yaml
|
||||
49
resource_customizations/argoproj.io/Experiment/testdata/errorExperiment.yaml
vendored
Normal file
49
resource_customizations/argoproj.io/Experiment/testdata/errorExperiment.yaml
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Experiment
|
||||
metadata:
|
||||
name: experiment-error-template-missing
|
||||
namespace: jesse-test
|
||||
spec:
|
||||
analyses:
|
||||
- name: does-not-exist
|
||||
templateName: does-not-exist
|
||||
templates:
|
||||
- name: baseline
|
||||
selector:
|
||||
matchLabels:
|
||||
app: rollouts-demo
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: rollouts-demo
|
||||
spec:
|
||||
containers:
|
||||
- image: argoproj/rollouts-demo:blue
|
||||
name: rollouts-demo
|
||||
status:
|
||||
analysisRuns:
|
||||
- analysisRun: ""
|
||||
message: 'AnalysisTemplate verification failed for analysis ''does-not-exist'':
|
||||
analysistemplate.argoproj.io "does-not-exist" not found'
|
||||
name: does-not-exist
|
||||
phase: Error
|
||||
availableAt: "2019-10-27T23:13:10Z"
|
||||
conditions:
|
||||
- lastTransitionTime: "2019-10-27T23:13:07Z"
|
||||
lastUpdateTime: "2019-10-28T05:59:33Z"
|
||||
message: Experiment "experiment-error-template-missing" is running.
|
||||
reason: NewReplicaSetAvailable
|
||||
phase: "True"
|
||||
type: Progressing
|
||||
message: 'AnalysisTemplate verification failed for analysis ''does-not-exist'':
|
||||
analysistemplate.argoproj.io "does-not-exist" not found'
|
||||
running: true
|
||||
phase: Error
|
||||
templateStatuses:
|
||||
- availableReplicas: 0
|
||||
lastTransitionTime: "2019-10-28T05:59:33Z"
|
||||
name: baseline
|
||||
readyReplicas: 0
|
||||
replicas: 0
|
||||
phase: Successful
|
||||
updatedReplicas: 0
|
||||
54
resource_customizations/argoproj.io/Experiment/testdata/failedExperiment.yaml
vendored
Normal file
54
resource_customizations/argoproj.io/Experiment/testdata/failedExperiment.yaml
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Experiment
|
||||
metadata:
|
||||
name: example-experiment
|
||||
namespace: default
|
||||
spec:
|
||||
analyses:
|
||||
- name: test
|
||||
templateName: analysis-template
|
||||
duration: 60
|
||||
templates:
|
||||
- name: baseline
|
||||
selector:
|
||||
matchLabels:
|
||||
app: rollouts-demo
|
||||
color: blue
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: rollouts-demo
|
||||
color: blue
|
||||
spec:
|
||||
containers:
|
||||
- image: 'argoproj/rollouts-demo:blue'
|
||||
name: guestbook
|
||||
status:
|
||||
analysisRuns:
|
||||
- analysisRun: example-experiment-test-57vl8
|
||||
name: test
|
||||
phase: Failed
|
||||
availableAt: '2019-10-28T20:58:00Z'
|
||||
conditions:
|
||||
- lastTransitionTime: '2019-10-28T20:57:58Z'
|
||||
lastUpdateTime: '2019-10-28T20:58:01Z'
|
||||
message: Experiment "example-experiment" is running.
|
||||
reason: NewReplicaSetAvailable
|
||||
phase: 'True'
|
||||
type: Progressing
|
||||
phase: Failed
|
||||
templateStatuses:
|
||||
- availableReplicas: 0
|
||||
lastTransitionTime: '2019-10-28T20:58:01Z'
|
||||
name: baseline
|
||||
readyReplicas: 0
|
||||
replicas: 0
|
||||
phase: Successful
|
||||
updatedReplicas: 0
|
||||
- availableReplicas: 0
|
||||
lastTransitionTime: '2019-10-28T20:58:01Z'
|
||||
name: canary
|
||||
readyReplicas: 0
|
||||
replicas: 0
|
||||
phase: Successful
|
||||
updatedReplicas: 0
|
||||
33
resource_customizations/argoproj.io/Experiment/testdata/noStatusExperiment.yaml
vendored
Normal file
33
resource_customizations/argoproj.io/Experiment/testdata/noStatusExperiment.yaml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Experiment
|
||||
metadata:
|
||||
annotations:
|
||||
kubectl.kubernetes.io/last-applied-configuration: >
|
||||
{"apiVersion":"argoproj.io/v1alpha1","kind":"Experiment","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"rollouts-canary"},"name":"example-experiment","namespace":"argo-rollouts"},"spec":{"duration":60,"templates":[{"name":"baseline","selector":{"matchLabels":{"app":"rollouts-demo","color":"blue"}},"template":{"metadata":{"labels":{"app":"rollouts-demo","color":"blue"}},"spec":{"containers":[{"image":"argoproj/rollouts-demo:blue","name":"guestbook"}]}}},{"name":"canary","selector":{"matchLabels":{"app":"rollouts-demo","color":"yellow"}},"template":{"metadata":{"labels":{"app":"rollouts-demo","color":"yellow"}},"spec":{"containers":[{"image":"argoproj/rollouts-demo:yellow","name":"guestbook"}]}}}]}}
|
||||
creationTimestamp: '2019-10-28T20:13:28Z'
|
||||
generation: 1
|
||||
labels:
|
||||
app.kubernetes.io/instance: rollouts-canary
|
||||
name: example-experiment
|
||||
namespace: argo-rollouts
|
||||
resourceVersion: '28562006'
|
||||
selfLink: >-
|
||||
/apis/argoproj.io/v1alpha1/namespaces/argo-rollouts/experiments/example-experiment
|
||||
uid: 67792f8a-f9bf-11e9-a15b-42010aa80033
|
||||
spec:
|
||||
duration: 60
|
||||
templates:
|
||||
- name: baseline
|
||||
selector:
|
||||
matchLabels:
|
||||
app: rollouts-demo
|
||||
color: blue
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: rollouts-demo
|
||||
color: blue
|
||||
spec:
|
||||
containers:
|
||||
- image: 'argoproj/rollouts-demo:blue'
|
||||
name: guestbook
|
||||
47
resource_customizations/argoproj.io/Experiment/testdata/pendingExperiment.yaml
vendored
Normal file
47
resource_customizations/argoproj.io/Experiment/testdata/pendingExperiment.yaml
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Experiment
|
||||
metadata:
|
||||
name: experiment-with-analysis-5hm74
|
||||
namespace: default
|
||||
spec:
|
||||
analyses:
|
||||
- name: job
|
||||
templateName: job
|
||||
duration: 3600
|
||||
templates:
|
||||
- name: baseline
|
||||
selector:
|
||||
matchLabels:
|
||||
app: rollouts-demo
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: rollouts-demo
|
||||
spec:
|
||||
containers:
|
||||
- image: argoproj/rollouts-demo:blue
|
||||
name: rollouts-demo
|
||||
status:
|
||||
analysisRuns:
|
||||
- analysisRun: experiment-with-analysis-5hm74-job-h4bgb
|
||||
name: job
|
||||
phase: Running
|
||||
availableAt: "2019-10-21T03:40:28Z"
|
||||
conditions:
|
||||
- lastTransitionTime: "2019-10-21T03:40:28Z"
|
||||
lastUpdateTime: "2019-10-21T03:40:28Z"
|
||||
message: Experiment "experiment-with-analysis-5hm74" has successfully ran and
|
||||
completed.
|
||||
reason: ExperimentCompleted
|
||||
phase: "False"
|
||||
type: Progressing
|
||||
running: false
|
||||
phase: Pending
|
||||
templateStatuses:
|
||||
- availableReplicas: 0
|
||||
lastTransitionTime: "2019-10-28T20:22:01Z"
|
||||
name: baseline
|
||||
readyReplicas: 0
|
||||
replicas: 0
|
||||
phase: Progressing
|
||||
updatedReplicas: 0
|
||||
40
resource_customizations/argoproj.io/Experiment/testdata/runningExperiment.yaml
vendored
Normal file
40
resource_customizations/argoproj.io/Experiment/testdata/runningExperiment.yaml
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Experiment
|
||||
metadata:
|
||||
name: example-experiment
|
||||
namespace: argo-rollouts
|
||||
spec:
|
||||
duration: 60
|
||||
templates:
|
||||
- name: baseline
|
||||
selector:
|
||||
matchLabels:
|
||||
app: rollouts-demo
|
||||
color: blue
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: rollouts-demo
|
||||
color: blue
|
||||
spec:
|
||||
containers:
|
||||
- image: 'argoproj/rollouts-demo:blue'
|
||||
name: guestbook
|
||||
status:
|
||||
availableAt: '2019-10-28T20:15:02Z'
|
||||
conditions:
|
||||
- lastTransitionTime: '2019-10-28T20:14:59Z'
|
||||
lastUpdateTime: '2019-10-28T20:15:02Z'
|
||||
message: Experiment "example-experiment" is running.
|
||||
reason: NewReplicaSetAvailable
|
||||
phase: 'True'
|
||||
type: Progressing
|
||||
phase: Running
|
||||
templateStatuses:
|
||||
- availableReplicas: 1
|
||||
lastTransitionTime: '2019-10-28T20:15:02Z'
|
||||
name: baseline
|
||||
readyReplicas: 1
|
||||
replicas: 1
|
||||
phase: Running
|
||||
updatedReplicas: 1
|
||||
61
resource_customizations/argoproj.io/Experiment/testdata/successfulExperiment.yaml
vendored
Normal file
61
resource_customizations/argoproj.io/Experiment/testdata/successfulExperiment.yaml
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Experiment
|
||||
metadata:
|
||||
name: example-experiment
|
||||
namespace: argo-rollouts
|
||||
spec:
|
||||
duration: 60
|
||||
templates:
|
||||
- name: baseline
|
||||
selector:
|
||||
matchLabels:
|
||||
app: rollouts-demo
|
||||
color: blue
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: rollouts-demo
|
||||
color: blue
|
||||
spec:
|
||||
containers:
|
||||
- image: 'argoproj/rollouts-demo:blue'
|
||||
name: guestbook
|
||||
- name: canary
|
||||
selector:
|
||||
matchLabels:
|
||||
app: rollouts-demo
|
||||
color: yellow
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: rollouts-demo
|
||||
color: yellow
|
||||
spec:
|
||||
containers:
|
||||
- image: 'argoproj/rollouts-demo:yellow'
|
||||
name: guestbook
|
||||
status:
|
||||
availableAt: '2019-10-28T20:15:02Z'
|
||||
conditions:
|
||||
- lastTransitionTime: '2019-10-28T20:20:54Z'
|
||||
lastUpdateTime: '2019-10-28T20:20:54Z'
|
||||
message: Experiment "example-experiment" has successfully ran and completed.
|
||||
reason: ExperimentCompleted
|
||||
phase: 'False'
|
||||
type: Progressing
|
||||
phase: Successful
|
||||
templateStatuses:
|
||||
- availableReplicas: 1
|
||||
lastTransitionTime: '2019-10-28T20:15:02Z'
|
||||
name: baseline
|
||||
readyReplicas: 1
|
||||
replicas: 1
|
||||
phase: Successful
|
||||
updatedReplicas: 1
|
||||
- availableReplicas: 1
|
||||
lastTransitionTime: '2019-10-28T20:15:01Z'
|
||||
name: canary
|
||||
readyReplicas: 1
|
||||
replicas: 1
|
||||
phase: Successful
|
||||
updatedReplicas: 1
|
||||
@@ -1,24 +1,28 @@
|
||||
discoveryTests:
|
||||
- inputPath: testdata/paused_rollout.yaml
|
||||
- inputPath: testdata/pre_v0.6_paused_rollout.yaml
|
||||
result:
|
||||
- name: resume
|
||||
disabled: false
|
||||
- inputPath: testdata/v0.2_paused_rollout.yaml
|
||||
result:
|
||||
- name: resume
|
||||
disabled: false
|
||||
- inputPath: testdata/not_paused_rollout.yaml
|
||||
- inputPath: testdata/pre_v0.6_not_paused_rollout.yaml
|
||||
result:
|
||||
- name: resume
|
||||
disabled: true
|
||||
- inputPath: testdata/nil_paused_rollout.yaml
|
||||
- inputPath: testdata/pre_v0.6_nil_paused_rollout.yaml
|
||||
result:
|
||||
- name: resume
|
||||
disabled: true
|
||||
- inputPath: testdata/has_pause_condition_rollout.yaml
|
||||
result:
|
||||
- name: resume
|
||||
disabled: false
|
||||
- inputPath: testdata/no_pause_condition_rollout.yaml
|
||||
result:
|
||||
- name: resume
|
||||
disabled: true
|
||||
actionTests:
|
||||
- action: resume
|
||||
inputPath: testdata/paused_rollout.yaml
|
||||
expectedOutputPath: testdata/not_paused_rollout.yaml
|
||||
inputPath: testdata/pre_v0.6_paused_rollout.yaml
|
||||
expectedOutputPath: testdata/pre_v0.6_not_paused_rollout.yaml
|
||||
- action: resume
|
||||
inputPath: testdata/v0.2_paused_rollout.yaml
|
||||
expectedOutputPath: testdata/v0.2_not_paused_rollout.yaml
|
||||
inputPath: testdata/has_pause_condition_rollout.yaml
|
||||
expectedOutputPath: testdata/no_pause_condition_rollout.yaml
|
||||
@@ -3,8 +3,8 @@ actions["resume"] = {["disabled"] = false}
|
||||
|
||||
local paused = false
|
||||
|
||||
if obj.status ~= nil and obj.status.verifyingPreview ~= nil then
|
||||
paused = obj.status.verifyingPreview
|
||||
if obj.status ~= nil and obj.status.pauseConditions ~= nil then
|
||||
paused = table.getn(obj.status.pauseConditions) > 0
|
||||
elseif obj.spec.paused ~= nil then
|
||||
paused = obj.spec.paused
|
||||
end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
if obj.status.verifyingPreview ~= nil and obj.status.verifyingPreview then
|
||||
obj.status.verifyingPreview = false
|
||||
if obj.status.pauseConditions ~= nil and table.getn(obj.status.pauseConditions) > 0 then
|
||||
obj.status.pauseConditions = nil
|
||||
end
|
||||
|
||||
if obj.spec.paused ~= nil and obj.spec.paused then
|
||||
|
||||
61
resource_customizations/argoproj.io/Rollout/actions/testdata/has_pause_condition_rollout.yaml
vendored
Normal file
61
resource_customizations/argoproj.io/Rollout/actions/testdata/has_pause_condition_rollout.yaml
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Rollout
|
||||
metadata:
|
||||
name: canary-demo
|
||||
namespace: default
|
||||
spec:
|
||||
replicas: 5
|
||||
revisionHistoryLimit: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: canary-demo
|
||||
strategy:
|
||||
canary:
|
||||
analysis:
|
||||
name: analysis
|
||||
templateName: analysis-template
|
||||
canaryService: canary-demo-preview
|
||||
steps:
|
||||
- setWeight: 40
|
||||
- pause: {}
|
||||
- setWeight: 60
|
||||
- pause: {}
|
||||
- setWeight: 80
|
||||
- pause:
|
||||
duration: 10
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: canary-demo
|
||||
spec:
|
||||
containers:
|
||||
- image: argoproj/rollouts-demo:yellow
|
||||
imagePullPolicy: Always
|
||||
name: canary-demo
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: http
|
||||
protocol: TCP
|
||||
resources:
|
||||
requests:
|
||||
cpu: 5m
|
||||
memory: 32Mi
|
||||
status:
|
||||
HPAReplicas: 5
|
||||
availableReplicas: 5
|
||||
blueGreen: {}
|
||||
canary:
|
||||
currentBackgroundAnalysisRun: canary-demo-6758949f55-6-analysis
|
||||
stableRS: 645d5dbc4c
|
||||
controllerPause: true
|
||||
currentPodHash: 6758949f55
|
||||
currentStepHash: 59f8666948
|
||||
currentStepIndex: 1
|
||||
observedGeneration: 58b949649c
|
||||
pauseConditions:
|
||||
- reason: CanaryPauseStep
|
||||
startTime: "2019-11-05T18:10:29Z"
|
||||
readyReplicas: 5
|
||||
replicas: 5
|
||||
selector: app=canary-demo
|
||||
updatedReplicas: 2
|
||||
58
resource_customizations/argoproj.io/Rollout/actions/testdata/no_pause_condition_rollout.yaml
vendored
Normal file
58
resource_customizations/argoproj.io/Rollout/actions/testdata/no_pause_condition_rollout.yaml
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Rollout
|
||||
metadata:
|
||||
name: canary-demo
|
||||
namespace: default
|
||||
spec:
|
||||
replicas: 5
|
||||
revisionHistoryLimit: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: canary-demo
|
||||
strategy:
|
||||
canary:
|
||||
analysis:
|
||||
name: analysis
|
||||
templateName: analysis-template
|
||||
canaryService: canary-demo-preview
|
||||
steps:
|
||||
- setWeight: 40
|
||||
- pause: {}
|
||||
- setWeight: 60
|
||||
- pause: {}
|
||||
- setWeight: 80
|
||||
- pause:
|
||||
duration: 10
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: canary-demo
|
||||
spec:
|
||||
containers:
|
||||
- image: argoproj/rollouts-demo:yellow
|
||||
imagePullPolicy: Always
|
||||
name: canary-demo
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: http
|
||||
protocol: TCP
|
||||
resources:
|
||||
requests:
|
||||
cpu: 5m
|
||||
memory: 32Mi
|
||||
status:
|
||||
HPAReplicas: 5
|
||||
availableReplicas: 5
|
||||
blueGreen: {}
|
||||
canary:
|
||||
currentBackgroundAnalysisRun: canary-demo-6758949f55-6-analysis
|
||||
stableRS: 645d5dbc4c
|
||||
controllerPause: true
|
||||
currentPodHash: 6758949f55
|
||||
currentStepHash: 59f8666948
|
||||
currentStepIndex: 1
|
||||
observedGeneration: 58b949649c
|
||||
readyReplicas: 5
|
||||
replicas: 5
|
||||
selector: app=canary-demo
|
||||
updatedReplicas: 2
|
||||
@@ -1,55 +0,0 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Rollout
|
||||
metadata:
|
||||
annotations:
|
||||
rollout.argoproj.io/revision: "7"
|
||||
clusterName: ""
|
||||
creationTimestamp: 2019-01-22T16:52:54Z
|
||||
generation: 1
|
||||
labels:
|
||||
app.kubernetes.io/instance: guestbook-default
|
||||
name: ks-guestbook-ui
|
||||
namespace: default
|
||||
resourceVersion: "164113"
|
||||
selfLink: /apis/argoproj.io/v1alpha1/namespaces/default/rollouts/ks-guestbook-ui
|
||||
uid: 29802403-1e66-11e9-a6a4-025000000001
|
||||
spec:
|
||||
minReadySeconds: 30
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: ks-guestbook-ui
|
||||
strategy:
|
||||
blueGreen:
|
||||
activeService: ks-guestbook-ui-active
|
||||
previewService: ks-guestbook-ui-preview
|
||||
type: BlueGreenUpdate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: ks-guestbook-ui
|
||||
spec:
|
||||
containers:
|
||||
- image: gcr.io/heptio-images/ks-guestbook-demo:0.1
|
||||
name: ks-guestbook-ui
|
||||
ports:
|
||||
- containerPort: 83
|
||||
resources: {}
|
||||
status:
|
||||
blueGreen:
|
||||
activeSelector: 85f9884f5d
|
||||
previewSelector: 697fb9575c
|
||||
availableReplicas: 6
|
||||
conditions:
|
||||
- lastTransitionTime: 2019-01-25T07:44:26Z
|
||||
lastUpdateTime: 2019-01-25T07:44:26Z
|
||||
message: Rollout is serving traffic from the active service.
|
||||
reason: Available
|
||||
status: "True"
|
||||
type: Available
|
||||
currentPodHash: 697fb9575c
|
||||
observedGeneration: 767f98959f
|
||||
readyReplicas: 6
|
||||
replicas: 6
|
||||
updatedReplicas: 3
|
||||
verifyingPreview: false
|
||||
@@ -1,55 +0,0 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Rollout
|
||||
metadata:
|
||||
annotations:
|
||||
rollout.argoproj.io/revision: "7"
|
||||
clusterName: ""
|
||||
creationTimestamp: 2019-01-22T16:52:54Z
|
||||
generation: 1
|
||||
labels:
|
||||
app.kubernetes.io/instance: guestbook-default
|
||||
name: ks-guestbook-ui
|
||||
namespace: default
|
||||
resourceVersion: "164113"
|
||||
selfLink: /apis/argoproj.io/v1alpha1/namespaces/default/rollouts/ks-guestbook-ui
|
||||
uid: 29802403-1e66-11e9-a6a4-025000000001
|
||||
spec:
|
||||
minReadySeconds: 30
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: ks-guestbook-ui
|
||||
strategy:
|
||||
blueGreen:
|
||||
activeService: ks-guestbook-ui-active
|
||||
previewService: ks-guestbook-ui-preview
|
||||
type: BlueGreenUpdate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: ks-guestbook-ui
|
||||
spec:
|
||||
containers:
|
||||
- image: gcr.io/heptio-images/ks-guestbook-demo:0.1
|
||||
name: ks-guestbook-ui
|
||||
ports:
|
||||
- containerPort: 83
|
||||
resources: {}
|
||||
status:
|
||||
blueGreen:
|
||||
activeSelector: 85f9884f5d
|
||||
previewSelector: 697fb9575c
|
||||
availableReplicas: 6
|
||||
conditions:
|
||||
- lastTransitionTime: 2019-01-25T07:44:26Z
|
||||
lastUpdateTime: 2019-01-25T07:44:26Z
|
||||
message: Rollout is serving traffic from the active service.
|
||||
reason: Available
|
||||
status: "True"
|
||||
type: Available
|
||||
currentPodHash: 697fb9575c
|
||||
observedGeneration: 767f98959f
|
||||
readyReplicas: 6
|
||||
replicas: 6
|
||||
updatedReplicas: 3
|
||||
verifyingPreview: true
|
||||
@@ -10,22 +10,9 @@ function checkReplicasStatus(obj)
|
||||
hs.message = "Waiting for roll out to finish: More replicas need to be updated"
|
||||
return hs
|
||||
end
|
||||
if replicasStatus > updatedReplicas then
|
||||
hs.status = "Progressing"
|
||||
hs.message = "Waiting for roll out to finish: old replicas are pending termination"
|
||||
return hs
|
||||
end
|
||||
if availableReplicas < updatedReplicas then
|
||||
hs.status = "Progressing"
|
||||
hs.message = "Waiting for roll out to finish: updated replicas are still becoming available"
|
||||
return hs
|
||||
end
|
||||
if updatedReplicas < replicasCount then
|
||||
hs.status = "Progressing"
|
||||
hs.message = "Waiting for roll out to finish: More replicas need to be updated"
|
||||
return hs
|
||||
end
|
||||
if replicasStatus > updatedReplicas then
|
||||
-- Since the scale down delay can be very high, BlueGreen does not wait for all the old replicas to scale
|
||||
-- down before marking itself healthy. As a result, only evaluate this condition if the strategy is canary.
|
||||
if obj.spec.strategy.canary ~= nil and replicasStatus > updatedReplicas then
|
||||
hs.status = "Progressing"
|
||||
hs.message = "Waiting for roll out to finish: old replicas are pending termination"
|
||||
return hs
|
||||
@@ -71,6 +58,11 @@ if obj.status ~= nil then
|
||||
hs.message = condition.message
|
||||
return hs
|
||||
end
|
||||
if condition.type == "Progressing" and condition.reason == "RolloutAborted" then
|
||||
hs.status = "Degraded"
|
||||
hs.message = condition.message
|
||||
return hs
|
||||
end
|
||||
if condition.type == "Progressing" and condition.reason == "ProgressDeadlineExceeded" then
|
||||
hs.status = "Degraded"
|
||||
hs.message = condition.message
|
||||
@@ -88,7 +80,7 @@ if obj.status ~= nil then
|
||||
if replicasHS ~= nil then
|
||||
return replicasHS
|
||||
end
|
||||
if obj.status.blueGreen ~= nil and obj.status.blueGreen.activeSelector ~= nil and obj.status.currentPodHash ~= nil and obj.status.blueGreen.activeSelector == obj.status.currentPodHash then
|
||||
if obj.status.blueGreen ~= nil and obj.status.blueGreen.activeSelector ~= nil and obj.status.blueGreen.activeSelector == obj.status.currentPodHash then
|
||||
hs.status = "Healthy"
|
||||
hs.message = "The active Service is serving traffic to the current pod spec"
|
||||
return hs
|
||||
|
||||
@@ -11,6 +11,10 @@ tests:
|
||||
status: Degraded
|
||||
message: ReplicaSet "guestbook-bluegreen-helm-guestbook-6b8cf6f7db" has timed out progressing.
|
||||
inputPath: testdata/degraded_rolloutTimeout.yaml
|
||||
- healthStatus:
|
||||
status: Degraded
|
||||
message: Rollout is aborted
|
||||
inputPath: testdata/degraded_abortedRollout.yaml
|
||||
#BlueGreen
|
||||
- healthStatus:
|
||||
status: Healthy
|
||||
@@ -28,10 +32,6 @@ tests:
|
||||
status: Progressing
|
||||
message: "Waiting for roll out to finish: More replicas need to be updated"
|
||||
inputPath: testdata/bluegreen/progressing_addingMoreReplicas.yaml
|
||||
- healthStatus:
|
||||
status: Progressing
|
||||
message: "Waiting for roll out to finish: old replicas are pending termination"
|
||||
inputPath: testdata/bluegreen/progressing_killingOldReplicas.yaml
|
||||
- healthStatus:
|
||||
status: Progressing
|
||||
message: "Waiting for roll out to finish: updated replicas are still becoming available"
|
||||
@@ -41,6 +41,10 @@ tests:
|
||||
status: Progressing
|
||||
message: Waiting for rollout to finish steps
|
||||
inputPath: testdata/canary/progressing_setWeightStep.yaml
|
||||
- healthStatus:
|
||||
status: Progressing
|
||||
message: "Waiting for roll out to finish: old replicas are pending termination"
|
||||
inputPath: testdata/canary/progressing_killingOldReplicas.yaml
|
||||
- healthStatus:
|
||||
status: Suspended
|
||||
message: Rollout is paused
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Rollout
|
||||
metadata:
|
||||
annotations:
|
||||
kubectl.kubernetes.io/last-applied-configuration: |
|
||||
{"apiVersion":"argoproj.io/v1alpha1","kind":"Rollout","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"guestbook-default","ksonnet.io/component":"guestbook-ui"},"name":"ks-guestbook-ui","namespace":"default"},"spec":{"minReadySeconds":30,"replicas":3,"selector":{"matchLabels":{"app":"ks-guestbook-ui"}},"strategy":{"blueGreen":{"activeService":"ks-guestbook-ui-active","previewService":"ks-guestbook-ui-preview"},"type":"BlueGreenUpdate"},"template":{"metadata":{"labels":{"app":"ks-guestbook-ui"}},"spec":{"containers":[{"image":"gcr.io/heptio-images/ks-guestbook-demo:0.1","name":"ks-guestbook-ui","ports":[{"containerPort":83}]}]}}}}
|
||||
rollout.argoproj.io/revision: "7"
|
||||
clusterName: ""
|
||||
creationTimestamp: 2019-01-22T16:52:54Z
|
||||
generation: 1
|
||||
labels:
|
||||
app.kubernetes.io/instance: guestbook-default
|
||||
ksonnet.io/component: guestbook-ui
|
||||
name: ks-guestbook-ui
|
||||
namespace: default
|
||||
resourceVersion: "164141"
|
||||
selfLink: /apis/argoproj.io/v1alpha1/namespaces/default/rollouts/ks-guestbook-ui
|
||||
uid: 29802403-1e66-11e9-a6a4-025000000001
|
||||
spec:
|
||||
minReadySeconds: 30
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: ks-guestbook-ui
|
||||
strategy:
|
||||
blueGreen:
|
||||
activeService: ks-guestbook-ui-active
|
||||
previewService: ks-guestbook-ui-preview
|
||||
type: BlueGreenUpdate
|
||||
template:
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
app: ks-guestbook-ui
|
||||
spec:
|
||||
containers:
|
||||
- image: gcr.io/heptio-images/ks-guestbook-demo:0.1
|
||||
name: ks-guestbook-ui
|
||||
ports:
|
||||
- containerPort: 83
|
||||
resources: {}
|
||||
status:
|
||||
activeSelector: 697fb9575c
|
||||
availableReplicas: 6
|
||||
conditions:
|
||||
- lastTransitionTime: 2019-01-25T07:44:26Z
|
||||
lastUpdateTime: 2019-01-25T07:44:26Z
|
||||
message: Rollout is serving traffic from the active service.
|
||||
reason: Available
|
||||
status: "True"
|
||||
type: Available
|
||||
currentPodHash: 697fb9575c
|
||||
observedGeneration: 767f98959f
|
||||
previewSelector: ""
|
||||
readyReplicas: 6
|
||||
replicas: 6
|
||||
updatedReplicas: 3
|
||||
61
resource_customizations/argoproj.io/Rollout/testdata/canary/progressing_killingOldReplicas.yaml
vendored
Normal file
61
resource_customizations/argoproj.io/Rollout/testdata/canary/progressing_killingOldReplicas.yaml
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Rollout
|
||||
metadata:
|
||||
annotations:
|
||||
kubectl.kubernetes.io/last-applied-configuration: |
|
||||
{"apiVersion":"argoproj.io/v1alpha1","kind":"Rollout","metadata":{"annotations":{},"name":"example-rollout-canary","namespace":"default"},"spec":{"minReadySeconds":30,"replicas":5,"revisionHistoryLimit":3,"selector":{"matchLabels":{"app":"guestbook"}},"strategy":{"canary":{"steps":[{"setWeight":20},{"pause":{"duration":20}},{"setWeight":40},{"pause":{}}]}},"template":{"metadata":{"labels":{"app":"guestbook"}},"spec":{"containers":[{"image":"gcr.io/heptio-images/ks-guestbook-demo:0.1","name":"guestbook","ports":[{"containerPort":80}]}]}}}}
|
||||
rollout.argoproj.io/revision: "3"
|
||||
creationTimestamp: "2019-10-20T15:42:26Z"
|
||||
generation: 101
|
||||
name: example-rollout-canary
|
||||
namespace: default
|
||||
resourceVersion: "1779901"
|
||||
selfLink: /apis/argoproj.io/v1alpha1/namespaces/default/rollouts/example-rollout-canary
|
||||
uid: f8ebf794-8b4e-4a1f-a2d1-a85bc1d206ef
|
||||
spec:
|
||||
minReadySeconds: 30
|
||||
replicas: 5
|
||||
revisionHistoryLimit: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: guestbook
|
||||
strategy:
|
||||
canary: {}
|
||||
template:
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
app: guestbook
|
||||
spec:
|
||||
containers:
|
||||
- image: gcr.io/heptio-images/ks-guestbook-demo:0.1
|
||||
name: guestbook
|
||||
ports:
|
||||
- containerPort: 80
|
||||
resources: {}
|
||||
status:
|
||||
HPAReplicas: 6
|
||||
availableReplicas: 5
|
||||
blueGreen: {}
|
||||
canary:
|
||||
stableRS: 74d6dc8544
|
||||
conditions:
|
||||
- lastTransitionTime: "2019-10-25T16:08:02Z"
|
||||
lastUpdateTime: "2019-10-25T16:08:02Z"
|
||||
message: Rollout has minimum availability
|
||||
reason: AvailableReason
|
||||
status: "True"
|
||||
type: Available
|
||||
- lastTransitionTime: "2019-10-25T16:50:19Z"
|
||||
lastUpdateTime: "2019-11-07T18:19:25Z"
|
||||
message: ReplicaSet "example-rollout-canary-694fb7759c" is progressing.
|
||||
reason: ReplicaSetUpdated
|
||||
status: "True"
|
||||
type: Progressing
|
||||
currentPodHash: 694fb7759c
|
||||
currentStepHash: 5ffbfbbd64
|
||||
observedGeneration: 7fcc96c7b7
|
||||
readyReplicas: 6
|
||||
replicas: 6
|
||||
selector: app=guestbook
|
||||
updatedReplicas: 5
|
||||
70
resource_customizations/argoproj.io/Rollout/testdata/degraded_abortedRollout.yaml
vendored
Normal file
70
resource_customizations/argoproj.io/Rollout/testdata/degraded_abortedRollout.yaml
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Rollout
|
||||
metadata:
|
||||
name: canary-demo
|
||||
namespace: default
|
||||
spec:
|
||||
replicas: 5
|
||||
revisionHistoryLimit: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: canary-demo
|
||||
strategy:
|
||||
canary:
|
||||
analysis:
|
||||
name: analysis
|
||||
templateName: analysis-template
|
||||
canaryService: canary-demo-preview
|
||||
steps:
|
||||
- setWeight: 40
|
||||
- pause: {}
|
||||
- setWeight: 60
|
||||
- pause: {}
|
||||
- setWeight: 80
|
||||
- pause:
|
||||
duration: 10
|
||||
template:
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
app: canary-demo
|
||||
spec:
|
||||
containers:
|
||||
- image: argoproj/rollouts-demo:yellow
|
||||
imagePullPolicy: Always
|
||||
name: canary-demo
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: http
|
||||
protocol: TCP
|
||||
resources:
|
||||
requests:
|
||||
cpu: 5m
|
||||
memory: 32Mi
|
||||
status:
|
||||
HPAReplicas: 5
|
||||
abort: true
|
||||
availableReplicas: 5
|
||||
blueGreen: {}
|
||||
canary:
|
||||
stableRS: 645d5dbc4c
|
||||
conditions:
|
||||
- lastTransitionTime: "2019-11-03T01:32:46Z"
|
||||
lastUpdateTime: "2019-11-03T01:32:46Z"
|
||||
message: Rollout has minimum availability
|
||||
reason: AvailableReason
|
||||
status: "True"
|
||||
type: Available
|
||||
- lastTransitionTime: "2019-11-05T18:20:12Z"
|
||||
lastUpdateTime: "2019-11-05T18:20:12Z"
|
||||
message: Rollout is aborted
|
||||
reason: RolloutAborted
|
||||
status: "False"
|
||||
type: Progressing
|
||||
currentPodHash: 6758949f55
|
||||
currentStepHash: 59f8666948
|
||||
currentStepIndex: 0
|
||||
observedGeneration: 58b949649c
|
||||
readyReplicas: 5
|
||||
replicas: 5
|
||||
selector: app=canary-demo
|
||||
@@ -1037,24 +1037,29 @@ func (s *Server) resolveRevision(ctx context.Context, app *appv1.Application, sy
|
||||
if ambiguousRevision == "" {
|
||||
ambiguousRevision = app.Spec.Source.TargetRevision
|
||||
}
|
||||
if git.IsCommitSHA(ambiguousRevision) {
|
||||
// If it's already a commit SHA, then no need to look it up
|
||||
|
||||
if app.Spec.Source.IsHelm() {
|
||||
return ambiguousRevision, ambiguousRevision, nil
|
||||
} else {
|
||||
if git.IsCommitSHA(ambiguousRevision) {
|
||||
// If it's already a commit SHA, then no need to look it up
|
||||
return ambiguousRevision, ambiguousRevision, nil
|
||||
}
|
||||
repo, err := s.db.GetRepository(ctx, app.Spec.Source.RepoURL)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
gitClient, err := git.NewClient(repo.Repo, repo.GetGitCreds(), repo.IsInsecure(), repo.IsLFSEnabled())
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
commitSHA, err := gitClient.LsRemote(ambiguousRevision)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
displayRevision := fmt.Sprintf("%s (%s)", ambiguousRevision, commitSHA)
|
||||
return commitSHA, displayRevision, nil
|
||||
}
|
||||
repo, err := s.db.GetRepository(ctx, app.Spec.Source.RepoURL)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
gitClient, err := git.NewClient(repo.Repo, repo.GetGitCreds(), repo.IsInsecure(), repo.IsLFSEnabled())
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
commitSHA, err := gitClient.LsRemote(ambiguousRevision)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
displayRevision := fmt.Sprintf("%s (%s)", ambiguousRevision, commitSHA)
|
||||
return commitSHA, displayRevision, nil
|
||||
}
|
||||
|
||||
func (s *Server) TerminateOperation(ctx context.Context, termOpReq *application.OperationTerminateRequest) (*application.OperationTerminateResponse, error) {
|
||||
|
||||
@@ -41,37 +41,38 @@ func NewServer(db db.ArgoDB, enf *rbac.Enforcer, cache *cache.Cache, kubectl kub
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) getConnectionState(cluster appv1.Cluster, errorMessage string) appv1.ConnectionState {
|
||||
if connectionState, err := s.cache.GetClusterConnectionState(cluster.Server); err == nil {
|
||||
return connectionState
|
||||
func (s *Server) getConnectionState(cluster appv1.Cluster, errorMessage string) (appv1.ConnectionState, string) {
|
||||
if clusterInfo, err := s.cache.GetClusterInfo(cluster.Server); err == nil {
|
||||
return clusterInfo.ConnectionState, clusterInfo.Version
|
||||
}
|
||||
now := v1.Now()
|
||||
connectionState := appv1.ConnectionState{
|
||||
Status: appv1.ConnectionStatusSuccessful,
|
||||
ModifiedAt: &now,
|
||||
clusterInfo := cache.ClusterInfo{
|
||||
ConnectionState: appv1.ConnectionState{
|
||||
Status: appv1.ConnectionStatusSuccessful,
|
||||
ModifiedAt: &now,
|
||||
},
|
||||
}
|
||||
|
||||
config := cluster.RESTConfig()
|
||||
config.Timeout = time.Second
|
||||
kubeClientset, err := kubernetes.NewForConfig(config)
|
||||
if err == nil {
|
||||
_, err = kubeClientset.Discovery().ServerVersion()
|
||||
}
|
||||
version, err := s.kubectl.GetServerVersion(config)
|
||||
if err != nil {
|
||||
connectionState.Status = appv1.ConnectionStatusFailed
|
||||
connectionState.Message = fmt.Sprintf("Unable to connect to cluster: %v", err)
|
||||
clusterInfo.Status = appv1.ConnectionStatusFailed
|
||||
clusterInfo.Message = fmt.Sprintf("Unable to connect to cluster: %v", err)
|
||||
} else {
|
||||
clusterInfo.Version = version
|
||||
}
|
||||
|
||||
if errorMessage != "" {
|
||||
connectionState.Status = appv1.ConnectionStatusFailed
|
||||
connectionState.Message = fmt.Sprintf("%s %s", errorMessage, connectionState.Message)
|
||||
clusterInfo.Status = appv1.ConnectionStatusFailed
|
||||
clusterInfo.Message = fmt.Sprintf("%s %s", errorMessage, clusterInfo.Message)
|
||||
}
|
||||
|
||||
err = s.cache.SetClusterConnectionState(cluster.Server, &connectionState)
|
||||
err = s.cache.SetClusterInfo(cluster.Server, &clusterInfo)
|
||||
if err != nil {
|
||||
log.Warnf("getConnectionState cache set error %s: %v", cluster.Server, err)
|
||||
log.Warnf("getClusterInfo cache set error %s: %v", cluster.Server, err)
|
||||
}
|
||||
return connectionState
|
||||
return clusterInfo.ConnectionState, clusterInfo.Version
|
||||
}
|
||||
|
||||
// List returns list of clusters
|
||||
@@ -100,11 +101,9 @@ func (s *Server) List(ctx context.Context, q *cluster.ClusterQuery) (*appv1.Clus
|
||||
warningMessage = fmt.Sprintf("There are %d credentials configured this cluster.", len(clusters))
|
||||
}
|
||||
if clust.ConnectionState.Status == "" {
|
||||
clust.ConnectionState = s.getConnectionState(clust, warningMessage)
|
||||
}
|
||||
clust.ServerVersion, err = s.kubectl.GetServerVersion(clust.RESTConfig())
|
||||
if err != nil {
|
||||
return err
|
||||
state, serverVersion := s.getConnectionState(clust, warningMessage)
|
||||
clust.ConnectionState = state
|
||||
clust.ServerVersion = serverVersion
|
||||
}
|
||||
items[i] = *redact(&clust)
|
||||
return nil
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"google.golang.org/grpc/status"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
repositorypkg "github.com/argoproj/argo-cd/pkg/apiclient/repository"
|
||||
appsv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/reposerver/apiclient"
|
||||
@@ -82,10 +83,15 @@ func (s *Server) List(ctx context.Context, q *repositorypkg.RepoQuery) (*appsv1.
|
||||
items := appsv1.Repositories{}
|
||||
for _, repo := range repos {
|
||||
if s.enf.Enforce(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionGet, repo.Repo) {
|
||||
// For backwards compatibility, if we have no repo type set assume a default
|
||||
rType := repo.Type
|
||||
if rType == "" {
|
||||
rType = common.DefaultRepoType
|
||||
}
|
||||
// remove secrets
|
||||
items = append(items, &appsv1.Repository{
|
||||
Repo: repo.Repo,
|
||||
Type: repo.Type,
|
||||
Type: rType,
|
||||
Name: repo.Name,
|
||||
Username: repo.Username,
|
||||
Insecure: repo.IsInsecure(),
|
||||
@@ -222,7 +228,10 @@ func (s *Server) Create(ctx context.Context, q *repositorypkg.RepoCreateRequest)
|
||||
return nil, status.Errorf(codes.InvalidArgument, "existing repository spec is different; use upsert flag to force update")
|
||||
}
|
||||
}
|
||||
return &appsv1.Repository{Repo: repo.Repo, Type: repo.Type, Name: repo.Name}, err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &appsv1.Repository{Repo: repo.Repo, Type: repo.Type, Name: repo.Name}, nil
|
||||
}
|
||||
|
||||
// Update updates a repository
|
||||
|
||||
@@ -450,7 +450,7 @@ func (a *ArgoCDServer) newGRPCServer() *grpc.Server {
|
||||
)))
|
||||
grpcS := grpc.NewServer(sOpts...)
|
||||
db := db.NewDB(a.Namespace, a.settingsMgr, a.KubeClientset)
|
||||
kubectl := kube.KubectlCmd{}
|
||||
kubectl := &kube.KubectlCmd{}
|
||||
clusterService := cluster.NewServer(db, a.enf, a.Cache, kubectl)
|
||||
repoService := repository.NewServer(a.RepoClientset, db, a.enf, a.Cache, a.settingsMgr)
|
||||
sessionService := session.NewServer(a.sessionMgr, a)
|
||||
@@ -478,22 +478,23 @@ func (a *ArgoCDServer) newGRPCServer() *grpc.Server {
|
||||
// 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.(*sessionpkg.SessionResponse); ok {
|
||||
flags := []string{"path=/"}
|
||||
flags := []string{"path=/", "SameSite=lax", "httpOnly"}
|
||||
if !a.Insecure {
|
||||
flags = append(flags, "Secure")
|
||||
}
|
||||
token := sessionResp.Token
|
||||
if token != "" {
|
||||
token, err := zjwt.ZJWT(token)
|
||||
var err error
|
||||
token, err = zjwt.ZJWT(token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cookie, err := httputil.MakeCookieMetadata(common.AuthCookieName, token, flags...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.Header().Set("Set-Cookie", cookie)
|
||||
}
|
||||
cookie, err := httputil.MakeCookieMetadata(common.AuthCookieName, token, flags...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.Header().Set("Set-Cookie", cookie)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -551,6 +552,7 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl
|
||||
mustRegisterGWHandler(sessionpkg.RegisterSessionServiceHandlerFromEndpoint, ctx, gwmux, endpoint, dOpts)
|
||||
mustRegisterGWHandler(settingspkg.RegisterSettingsServiceHandlerFromEndpoint, ctx, gwmux, endpoint, dOpts)
|
||||
mustRegisterGWHandler(projectpkg.RegisterProjectServiceHandlerFromEndpoint, ctx, gwmux, endpoint, dOpts)
|
||||
mustRegisterGWHandler(accountpkg.RegisterAccountServiceHandlerFromEndpoint, ctx, gwmux, endpoint, dOpts)
|
||||
mustRegisterGWHandler(certificatepkg.RegisterCertificateServiceHandlerFromEndpoint, ctx, gwmux, endpoint, dOpts)
|
||||
|
||||
// Swagger UI
|
||||
|
||||
@@ -4,10 +4,13 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/argo-cd/pkg/apiclient/session"
|
||||
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
@@ -479,3 +482,31 @@ func Test_getToken(t *testing.T) {
|
||||
assert.Equal(t, token, getToken(metadata.New(map[string]string{"grpcgateway-cookie": "argocd.token=" + token})))
|
||||
})
|
||||
}
|
||||
|
||||
func TestTranslateGrpcCookieHeader(t *testing.T) {
|
||||
argoCDOpts := ArgoCDServerOpts{
|
||||
Namespace: test.FakeArgoCDNamespace,
|
||||
KubeClientset: fake.NewSimpleClientset(test.NewFakeConfigMap(), test.NewFakeSecret()),
|
||||
AppClientset: apps.NewSimpleClientset(),
|
||||
}
|
||||
argocd := NewServer(context.Background(), argoCDOpts)
|
||||
|
||||
t.Run("TokenIsNotEmpty", func(t *testing.T) {
|
||||
recorder := httptest.NewRecorder()
|
||||
err := argocd.translateGrpcCookieHeader(context.Background(), recorder, &session.SessionResponse{
|
||||
Token: "xyz",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "argocd.token=xyz; path=/; SameSite=lax; httpOnly; Secure", recorder.Result().Header.Get("Set-Cookie"))
|
||||
})
|
||||
|
||||
t.Run("TokenIsEmpty", func(t *testing.T) {
|
||||
recorder := httptest.NewRecorder()
|
||||
err := argocd.translateGrpcCookieHeader(context.Background(), recorder, &session.SessionResponse{
|
||||
Token: "",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "argocd.token=; path=/; SameSite=lax; httpOnly; Secure", recorder.Result().Header.Get("Set-Cookie"))
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -25,31 +25,36 @@ func (s *Server) Version(context.Context, *empty.Empty) (*version.VersionMessage
|
||||
vers := common.GetVersion()
|
||||
if s.ksonnetVersion == "" {
|
||||
ksonnetVersion, err := ksutil.Version()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if err == nil {
|
||||
s.ksonnetVersion = ksonnetVersion
|
||||
} else {
|
||||
s.ksonnetVersion = err.Error()
|
||||
}
|
||||
s.ksonnetVersion = ksonnetVersion
|
||||
}
|
||||
if s.kustomizeVersion == "" {
|
||||
kustomizeVersion, err := kustomize.Version()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if err == nil {
|
||||
s.kustomizeVersion = kustomizeVersion
|
||||
} else {
|
||||
s.kustomizeVersion = err.Error()
|
||||
}
|
||||
s.kustomizeVersion = kustomizeVersion
|
||||
|
||||
}
|
||||
if s.helmVersion == "" {
|
||||
helmVersion, err := helm.Version()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if err == nil {
|
||||
s.helmVersion = helmVersion
|
||||
} else {
|
||||
s.helmVersion = err.Error()
|
||||
}
|
||||
s.helmVersion = helmVersion
|
||||
}
|
||||
if s.kubectlVersion == "" {
|
||||
kubectlVersion, err := kube.Version()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if err == nil {
|
||||
s.kubectlVersion = kubectlVersion
|
||||
} else {
|
||||
s.kubectlVersion = err.Error()
|
||||
}
|
||||
s.kubectlVersion = kubectlVersion
|
||||
}
|
||||
return &version.VersionMessage{
|
||||
Version: vers.Version,
|
||||
|
||||
@@ -567,6 +567,18 @@ func TestLocalManifestSync(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestLocalSync(t *testing.T) {
|
||||
Given(t).
|
||||
// we've got to use Helm as this uses kubeVersion
|
||||
Path("helm").
|
||||
When().
|
||||
Create().
|
||||
Then().
|
||||
And(func(app *Application) {
|
||||
FailOnErr(RunCli("app", "sync", app.Name, "--local", "testdata/helm"))
|
||||
})
|
||||
}
|
||||
|
||||
func TestNoLocalSyncWithAutosyncEnabled(t *testing.T) {
|
||||
Given(t).
|
||||
Path(guestbookPath).
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -24,7 +22,7 @@ func (c *Consequences) Expect(e Expectation) *Consequences {
|
||||
c.context.t.Helper()
|
||||
var message string
|
||||
var state state
|
||||
timeout := c.timeout()
|
||||
timeout := time.Duration(15) * time.Second
|
||||
for start := time.Now(); time.Since(start) < timeout; time.Sleep(3 * time.Second) {
|
||||
state, message = e(c)
|
||||
switch state {
|
||||
@@ -78,12 +76,3 @@ func (c *Consequences) resource(kind, name string) ResourceStatus {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Consequences) timeout() time.Duration {
|
||||
value := os.Getenv("ARGOCD_E2E_EXPECT_TIMEOUT")
|
||||
timeout, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
timeout = 15
|
||||
}
|
||||
return time.Duration(timeout) * time.Second
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ func TestDeclarativeHelmInvalidValuesFile(t *testing.T) {
|
||||
func TestHelmRepo(t *testing.T) {
|
||||
Given(t).
|
||||
CustomCACertAdded().
|
||||
HelmRepoAdded("").
|
||||
HelmRepoAdded("custom-repo").
|
||||
RepoURLType(RepoURLTypeHelm).
|
||||
Chart("helm").
|
||||
Revision("1.0.0").
|
||||
@@ -184,6 +184,20 @@ func TestKubeVersion(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestHelmValuesHiddenDirectory(t *testing.T) {
|
||||
Given(t).
|
||||
Path(".hidden-helm").
|
||||
When().
|
||||
AddFile("foo.yaml", "").
|
||||
Create().
|
||||
AppSet("--values", "foo.yaml").
|
||||
Sync().
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(HealthIs(HealthStatusHealthy)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced))
|
||||
}
|
||||
|
||||
func TestHelmWithDependencies(t *testing.T) {
|
||||
testHelmWithDependencies(t, false)
|
||||
}
|
||||
@@ -193,7 +207,10 @@ func TestHelmWithDependenciesLegacyRepo(t *testing.T) {
|
||||
}
|
||||
|
||||
func testHelmWithDependencies(t *testing.T, legacyRepo bool) {
|
||||
ctx := Given(t).CustomCACertAdded()
|
||||
ctx := Given(t).
|
||||
CustomCACertAdded().
|
||||
// these are slow tests
|
||||
Timeout(30)
|
||||
if legacyRepo {
|
||||
ctx.And(func() {
|
||||
errors.FailOnErr(fixture.Run("", "kubectl", "create", "secret", "generic", "helm-repo",
|
||||
|
||||
2
test/e2e/testdata/.hidden-helm/Chart.yaml
vendored
Normal file
2
test/e2e/testdata/.hidden-helm/Chart.yaml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
version: 1.0.0
|
||||
name: helm
|
||||
6
test/e2e/testdata/.hidden-helm/templates/config-map.yaml
vendored
Normal file
6
test/e2e/testdata/.hidden-helm/templates/config-map.yaml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: my-map
|
||||
data:
|
||||
foo: bar
|
||||
0
test/e2e/testdata/.hidden-helm/values.yaml
vendored
Normal file
0
test/e2e/testdata/.hidden-helm/values.yaml
vendored
Normal file
@@ -4,21 +4,24 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
. "strings"
|
||||
"testing"
|
||||
|
||||
argoexec "github.com/argoproj/pkg/exec"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/argoproj/argo-cd/test/fixture/test"
|
||||
)
|
||||
|
||||
func TestKustomizeVersion(t *testing.T) {
|
||||
test.CIOnly(t)
|
||||
out, err := argoexec.RunCommand("kustomize", argoexec.CmdOpts{}, "version")
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, out, "Version:kustomize/v3", "kustomize should be version 3")
|
||||
}
|
||||
|
||||
// TestBuildManifests makes sure we are consistent in naming, and all kustomization.yamls are buildable
|
||||
func TestBuildManifests(t *testing.T) {
|
||||
|
||||
out, err := argoexec.RunCommand("kustomize", argoexec.CmdOpts{}, "version")
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, Contains(out, "KustomizeVersion:3") || Contains(out, "KustomizeVersion:v3"), "kustomize should be version 3")
|
||||
|
||||
err = filepath.Walk("../manifests", func(path string, f os.FileInfo, err error) error {
|
||||
err := filepath.Walk("../manifests", func(path string, f os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -23,7 +20,7 @@ const (
|
||||
FakeClusterURL = "https://fake-cluster:443"
|
||||
)
|
||||
|
||||
var PodManifest = []byte(`
|
||||
var PodManifest = `
|
||||
{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Pod",
|
||||
@@ -44,19 +41,14 @@ var PodManifest = []byte(`
|
||||
]
|
||||
}
|
||||
}
|
||||
`)
|
||||
`
|
||||
|
||||
func NewPod() *unstructured.Unstructured {
|
||||
var un unstructured.Unstructured
|
||||
err := json.Unmarshal(PodManifest, &un)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &un
|
||||
return Unstructured(PodManifest)
|
||||
}
|
||||
|
||||
func NewCRD() *unstructured.Unstructured {
|
||||
var un unstructured.Unstructured
|
||||
err := yaml.Unmarshal([]byte(`apiVersion: apiextensions.k8s.io/v1beta1
|
||||
return Unstructured(`apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: testcrds.argoproj.io
|
||||
@@ -66,11 +58,7 @@ spec:
|
||||
scope: Namespaced
|
||||
names:
|
||||
plural: testcrds
|
||||
kind: TestCrd`), &un)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &un
|
||||
kind: TestCrd`)
|
||||
}
|
||||
|
||||
// DEPRECATED
|
||||
@@ -97,7 +85,7 @@ func Annotate(obj *unstructured.Unstructured, key, val string) *unstructured.Uns
|
||||
return obj
|
||||
}
|
||||
|
||||
var ServiceManifest = []byte(`
|
||||
var ServiceManifest = `
|
||||
{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Service",
|
||||
@@ -118,18 +106,13 @@ var ServiceManifest = []byte(`
|
||||
}
|
||||
}
|
||||
}
|
||||
`)
|
||||
`
|
||||
|
||||
func NewService() *unstructured.Unstructured {
|
||||
var un unstructured.Unstructured
|
||||
err := json.Unmarshal(ServiceManifest, &un)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &un
|
||||
return Unstructured(ServiceManifest)
|
||||
}
|
||||
|
||||
var DeploymentManifest = []byte(`
|
||||
var DeploymentManifest = `
|
||||
{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
@@ -168,15 +151,10 @@ var DeploymentManifest = []byte(`
|
||||
}
|
||||
}
|
||||
}
|
||||
`)
|
||||
`
|
||||
|
||||
func NewDeployment() *unstructured.Unstructured {
|
||||
var un unstructured.Unstructured
|
||||
err := json.Unmarshal(DeploymentManifest, &un)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &un
|
||||
return Unstructured(DeploymentManifest)
|
||||
}
|
||||
|
||||
func DemoDeployment() *appsv1.Deployment {
|
||||
|
||||
32
test/unstructured.go
Normal file
32
test/unstructured.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
func UnstructuredFromFile(path string) *unstructured.Unstructured {
|
||||
file, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return Unstructured(string(file))
|
||||
}
|
||||
|
||||
func Unstructured(text string) *unstructured.Unstructured {
|
||||
un := &unstructured.Unstructured{}
|
||||
var err error
|
||||
if strings.HasPrefix(text, "{") {
|
||||
err = json.Unmarshal([]byte(text), &un)
|
||||
} else {
|
||||
err = yaml.Unmarshal([]byte(text), &un)
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return un
|
||||
}
|
||||
9
ui/.prettierrc
Normal file
9
ui/.prettierrc
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"bracketSpacing": false,
|
||||
"jsxSingleQuote": true,
|
||||
"printWidth": 180,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 4,
|
||||
"jsxBracketSameLine": true,
|
||||
"quoteProps": "consistent"
|
||||
}
|
||||
@@ -72,15 +72,26 @@ async function isExpiredSSO() {
|
||||
|
||||
requests.onError.subscribe(async (err) => {
|
||||
if (err.status === 401) {
|
||||
if (!history.location.pathname.startsWith('/login')) {
|
||||
// Query for basehref and remove trailing /.
|
||||
// If basehref is the default `/` it will become an empty string.
|
||||
const basehref = document.querySelector('head > base').getAttribute('href').replace(/\/$/, '');
|
||||
if (await isExpiredSSO()) {
|
||||
window.location.href = `${basehref}/auth/login?return_url=${encodeURIComponent(location.href)}`;
|
||||
} else {
|
||||
history.push(`${basehref}/login?return_url=${encodeURIComponent(location.href)}`);
|
||||
}
|
||||
if (history.location.pathname.startsWith('/login')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isSSO = await isExpiredSSO();
|
||||
|
||||
// location might change after async method call, so we need to check again.
|
||||
if (history.location.pathname.startsWith('/login')) {
|
||||
return;
|
||||
}
|
||||
// Query for basehref and remove trailing /.
|
||||
// If basehref is the default `/` it will become an empty string.
|
||||
const basehref = document
|
||||
.querySelector('head > base')
|
||||
.getAttribute('href')
|
||||
.replace(/\/$/, '');
|
||||
if (isSSO) {
|
||||
window.location.href = `${basehref}/auth/login?return_url=${encodeURIComponent(location.href)}`;
|
||||
} else {
|
||||
history.push(`${basehref}/login?return_url=${encodeURIComponent(location.href)}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -522,7 +522,7 @@ export class ApplicationDetails extends React.Component<RouteComponentProps<{ na
|
||||
const resourceActions = services.applications.getResourceActions(application.metadata.name, resource)
|
||||
.then((actions) => items.concat(actions.map((action) => ({
|
||||
title: action.name,
|
||||
disabled: !action.available,
|
||||
disabled: !!action.disabled,
|
||||
action: async () => {
|
||||
try {
|
||||
const confirmed = await this.appContext.apis.popup.confirm(
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
import { ErrorNotification, MockupList, NotificationType, SlidingPanel } from 'argo-ui';
|
||||
import {ErrorNotification, MockupList, NotificationType, SlidingPanel} from 'argo-ui';
|
||||
import * as classNames from 'classnames';
|
||||
import * as minimatch from 'minimatch';
|
||||
import * as React from 'react';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
import { Observable } from 'rxjs';
|
||||
import {RouteComponentProps} from 'react-router';
|
||||
import {Observable} from 'rxjs';
|
||||
|
||||
import { Autocomplete, ClusterCtx, DataLoader, EmptyState, ObservableQuery, Page, Paginate, Query } from '../../../shared/components';
|
||||
import { Consumer } from '../../../shared/context';
|
||||
import {Autocomplete, ClusterCtx, DataLoader, EmptyState, ObservableQuery, Page, Paginate, Query} from '../../../shared/components';
|
||||
import {Consumer} from '../../../shared/context';
|
||||
import * as models from '../../../shared/models';
|
||||
import { AppsListPreferences, AppsListViewType, services } from '../../../shared/services';
|
||||
import { ApplicationCreatePanel } from '../application-create-panel/application-create-panel';
|
||||
import { ApplicationSyncPanel } from '../application-sync-panel/application-sync-panel';
|
||||
import {AppsListPreferences, AppsListViewType, services} from '../../../shared/services';
|
||||
import {ApplicationCreatePanel} from '../application-create-panel/application-create-panel';
|
||||
import {ApplicationSyncPanel} from '../application-sync-panel/application-sync-panel';
|
||||
import {ApplicationsSyncPanel} from '../applications-sync-panel/applications-sync-panel';
|
||||
import * as LabelSelector from '../label-selector';
|
||||
import * as AppUtils from '../utils';
|
||||
import { ApplicationsFilter } from './applications-filter';
|
||||
import { ApplicationsSummary } from './applications-summary';
|
||||
import { ApplicationsTable } from './applications-table';
|
||||
import { ApplicationTiles } from './applications-tiles';
|
||||
import {ApplicationsFilter} from './applications-filter';
|
||||
import {ApplicationsSummary} from './applications-summary';
|
||||
import {ApplicationsTable} from './applications-table';
|
||||
import {ApplicationTiles} from './applications-tiles';
|
||||
|
||||
require('./applications-list.scss');
|
||||
|
||||
@@ -25,93 +27,125 @@ const APP_FIELDS = [
|
||||
'metadata.labels',
|
||||
'metadata.resourceVersion',
|
||||
'metadata.creationTimestamp',
|
||||
'metadata.deletionTimestamp',
|
||||
'spec',
|
||||
'operation.sync',
|
||||
'status.sync.status',
|
||||
'status.health',
|
||||
'status.summary'];
|
||||
'status.operationState.phase',
|
||||
'status.operationState.operation.sync',
|
||||
'status.summary',
|
||||
];
|
||||
const APP_LIST_FIELDS = APP_FIELDS.map((field) => `items.${field}`);
|
||||
const APP_WATCH_FIELDS = ['result.type', ...APP_FIELDS.map((field) => `result.application.${field}`)];
|
||||
|
||||
function loadApplications(selector: string): Observable<models.Application[]> {
|
||||
return Observable.fromPromise(services.applications.list([], { fields: APP_LIST_FIELDS, selector })).flatMap((applications) =>
|
||||
function loadApplications(): Observable<models.Application[]> {
|
||||
return Observable.fromPromise(services.applications.list([], {fields: APP_LIST_FIELDS})).flatMap((applications) =>
|
||||
Observable.merge(
|
||||
Observable.from([applications]),
|
||||
services.applications.watch(null, { fields: APP_WATCH_FIELDS, selector }).map((appChange) => {
|
||||
const index = applications.findIndex((item) => item.metadata.name === appChange.application.metadata.name);
|
||||
if (index > -1 && appChange.application.metadata.resourceVersion === applications[index].metadata.resourceVersion) {
|
||||
return {applications, updated: false};
|
||||
}
|
||||
switch (appChange.type) {
|
||||
case 'DELETED':
|
||||
if (index > -1) {
|
||||
applications.splice(index, 1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (index > -1) {
|
||||
applications[index] = appChange.application;
|
||||
} else {
|
||||
applications.unshift(appChange.application);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return {applications, updated: true};
|
||||
}).filter((item) => item.updated).map((item) => item.applications),
|
||||
services.applications
|
||||
.watch(null, {fields: APP_WATCH_FIELDS})
|
||||
.map((appChange) => {
|
||||
const index = applications.findIndex((item) => item.metadata.name === appChange.application.metadata.name);
|
||||
if (index > -1 && appChange.application.metadata.resourceVersion === applications[index].metadata.resourceVersion) {
|
||||
return {applications, updated: false};
|
||||
}
|
||||
switch (appChange.type) {
|
||||
case 'DELETED':
|
||||
if (index > -1) {
|
||||
applications.splice(index, 1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (index > -1) {
|
||||
applications[index] = appChange.application;
|
||||
} else {
|
||||
applications.unshift(appChange.application);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return {applications, updated: true};
|
||||
})
|
||||
.filter((item) => item.updated)
|
||||
.map((item) => item.applications),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
const ViewPref = ({children}: { children: (pref: AppsListPreferences & { page: number, search: string }) => React.ReactNode }) => (
|
||||
const ViewPref = ({children}: {children: (pref: AppsListPreferences & {page: number; search: string}) => React.ReactNode}) => (
|
||||
<ObservableQuery>
|
||||
{(q) => (
|
||||
<DataLoader load={() => Observable.combineLatest(
|
||||
services.viewPreferences.getPreferences().map((item) => item.appList), q).map((items) => {
|
||||
const params = items[1];
|
||||
const viewPref: AppsListPreferences = {...items[0]};
|
||||
if (params.get('proj') != null) {
|
||||
viewPref.projectsFilter = params.get('proj').split(',').filter((item) => !!item);
|
||||
}
|
||||
if (params.get('sync') != null) {
|
||||
viewPref.syncFilter = params.get('sync').split(',').filter((item) => !!item);
|
||||
}
|
||||
if (params.get('health') != null) {
|
||||
viewPref.healthFilter = params.get('health').split(',').filter((item) => !!item);
|
||||
}
|
||||
if (params.get('namespace') != null) {
|
||||
viewPref.namespacesFilter = params.get('namespace').split(',').filter((item) => !!item);
|
||||
}
|
||||
if (params.get('cluster') != null) {
|
||||
viewPref.clustersFilter = params.get('cluster').split(',').filter((item) => !!item);
|
||||
}
|
||||
if (params.get('view') != null) {
|
||||
viewPref.view = params.get('view') as AppsListViewType;
|
||||
}
|
||||
if (params.get('labels') != null) {
|
||||
viewPref.labelsFilter = params.get('labels').split(',').filter((item) => !!item);
|
||||
}
|
||||
return {...viewPref, page: parseInt(params.get('page') || '0', 10), search: params.get('search') || ''};
|
||||
})}>
|
||||
{(pref) => children(pref)}
|
||||
<DataLoader
|
||||
load={() =>
|
||||
Observable.combineLatest(services.viewPreferences.getPreferences().map((item) => item.appList), q).map((items) => {
|
||||
const params = items[1];
|
||||
const viewPref: AppsListPreferences = {...items[0]};
|
||||
if (params.get('proj') != null) {
|
||||
viewPref.projectsFilter = params
|
||||
.get('proj')
|
||||
.split(',')
|
||||
.filter((item) => !!item);
|
||||
}
|
||||
if (params.get('sync') != null) {
|
||||
viewPref.syncFilter = params
|
||||
.get('sync')
|
||||
.split(',')
|
||||
.filter((item) => !!item);
|
||||
}
|
||||
if (params.get('health') != null) {
|
||||
viewPref.healthFilter = params
|
||||
.get('health')
|
||||
.split(',')
|
||||
.filter((item) => !!item);
|
||||
}
|
||||
if (params.get('namespace') != null) {
|
||||
viewPref.namespacesFilter = params
|
||||
.get('namespace')
|
||||
.split(',')
|
||||
.filter((item) => !!item);
|
||||
}
|
||||
if (params.get('cluster') != null) {
|
||||
viewPref.clustersFilter = params
|
||||
.get('cluster')
|
||||
.split(',')
|
||||
.filter((item) => !!item);
|
||||
}
|
||||
if (params.get('view') != null) {
|
||||
viewPref.view = params.get('view') as AppsListViewType;
|
||||
}
|
||||
if (params.get('labels') != null) {
|
||||
viewPref.labelsFilter = params
|
||||
.get('labels')
|
||||
.split(',')
|
||||
.map(decodeURIComponent)
|
||||
.filter((item) => !!item);
|
||||
}
|
||||
return {...viewPref, page: parseInt(params.get('page') || '0', 10), search: params.get('search') || ''};
|
||||
})
|
||||
}>
|
||||
{(pref) => children(pref)}
|
||||
</DataLoader>
|
||||
)}
|
||||
</ObservableQuery>
|
||||
);
|
||||
|
||||
function filterApps(applications: models.Application[], pref: AppsListPreferences, search: string) {
|
||||
return applications.filter((app) =>
|
||||
(search === '' || app.metadata.name.includes(search)) &&
|
||||
(pref.projectsFilter.length === 0 || pref.projectsFilter.includes(app.spec.project)) &&
|
||||
(pref.reposFilter.length === 0 || pref.reposFilter.includes(app.spec.source.repoURL)) &&
|
||||
(pref.syncFilter.length === 0 || pref.syncFilter.includes(app.status.sync.status)) &&
|
||||
(pref.healthFilter.length === 0 || pref.healthFilter.includes(app.status.health.status)) &&
|
||||
(pref.namespacesFilter.length === 0 || pref.namespacesFilter.some((ns) => minimatch(app.spec.destination.namespace, ns))) &&
|
||||
(pref.clustersFilter.length === 0 || pref.clustersFilter.some((server) => minimatch(app.spec.destination.server, server))),
|
||||
return applications.filter(
|
||||
(app) =>
|
||||
(search === '' || app.metadata.name.includes(search)) &&
|
||||
(pref.projectsFilter.length === 0 || pref.projectsFilter.includes(app.spec.project)) &&
|
||||
(pref.reposFilter.length === 0 || pref.reposFilter.includes(app.spec.source.repoURL)) &&
|
||||
(pref.syncFilter.length === 0 || pref.syncFilter.includes(app.status.sync.status)) &&
|
||||
(pref.healthFilter.length === 0 || pref.healthFilter.includes(app.status.health.status)) &&
|
||||
(pref.namespacesFilter.length === 0 || pref.namespacesFilter.some((ns) => minimatch(app.spec.destination.namespace, ns))) &&
|
||||
(pref.clustersFilter.length === 0 || pref.clustersFilter.some((server) => minimatch(app.spec.destination.server, server))) &&
|
||||
(pref.labelsFilter.length === 0 || pref.labelsFilter.every((selector) => LabelSelector.match(selector, app.metadata.labels))),
|
||||
);
|
||||
}
|
||||
|
||||
function tryJsonParse(input: string) {
|
||||
try {
|
||||
return input && JSON.parse(input) || null;
|
||||
return (input && JSON.parse(input)) || null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
@@ -120,179 +154,234 @@ function tryJsonParse(input: string) {
|
||||
export const ApplicationsList = (props: RouteComponentProps<{}>) => {
|
||||
const query = new URLSearchParams(props.location.search);
|
||||
const appInput = tryJsonParse(query.get('new'));
|
||||
const syncAppsInput = tryJsonParse(query.get('syncApps'));
|
||||
const [createApi, setCreateApi] = React.useState(null);
|
||||
const clusters = React.useMemo(() => services.clusters.list(), []);
|
||||
|
||||
return (
|
||||
<ClusterCtx.Provider value={clusters}>
|
||||
<Consumer>{
|
||||
(ctx) => (
|
||||
<Page title='Applications' toolbar={services.viewPreferences.getPreferences().map((pref) => ({
|
||||
breadcrumbs: [{ title: 'Applications', path: '/applications' }],
|
||||
tools: (
|
||||
<React.Fragment key='app-list-tools'>
|
||||
<span className='applications-list__view-type'>
|
||||
<i className={classNames('fa fa-th', {selected: pref.appList.view === 'tiles'})} onClick={() => {
|
||||
ctx.navigation.goto('.', { view: 'tiles'});
|
||||
services.viewPreferences.updatePreferences({ appList: {...pref.appList, view: 'tiles'} });
|
||||
}} />
|
||||
<i className={classNames('fa fa-th-list', {selected: pref.appList.view === 'list'})} onClick={() => {
|
||||
ctx.navigation.goto('.', { view: 'list'});
|
||||
services.viewPreferences.updatePreferences({ appList: {...pref.appList, view: 'list'} });
|
||||
}} />
|
||||
<i className={classNames('fa fa-chart-pie', {selected: pref.appList.view === 'summary'})} onClick={() => {
|
||||
ctx.navigation.goto('.', { view: 'summary'});
|
||||
services.viewPreferences.updatePreferences({ appList: {...pref.appList, view: 'summary'} });
|
||||
}} />
|
||||
</span>
|
||||
</React.Fragment>
|
||||
),
|
||||
actionMenu: {
|
||||
className: 'fa fa-plus',
|
||||
items: [{
|
||||
title: 'New Application',
|
||||
action: () => ctx.navigation.goto('.', { new: '{}' }),
|
||||
}],
|
||||
},
|
||||
}))}>
|
||||
<div className='applications-list'>
|
||||
<ViewPref>
|
||||
{(pref) =>
|
||||
<DataLoader
|
||||
input={(pref.labelsFilter || []).join(',')}
|
||||
load={(selector) => loadApplications(selector)}
|
||||
loadingRenderer={() => (<div className='argo-container'><MockupList height={100} marginTop={30}/></div>)}>
|
||||
{(applications: models.Application[]) => (
|
||||
applications.length === 0 && (pref.labelsFilter || []).length === 0 ? (
|
||||
<EmptyState icon='argo-icon-application'>
|
||||
<h4>No applications yet</h4>
|
||||
<h5>Create new application to start managing resources in your cluster</h5>
|
||||
<button className='argo-button argo-button--base' onClick={() => ctx.navigation.goto('.', { new: JSON.stringify({}) })}>Create application</button>
|
||||
</EmptyState>
|
||||
) : (
|
||||
<div className='row'>
|
||||
<div className='columns small-12 xxlarge-2'>
|
||||
<Query>
|
||||
{(q) => (
|
||||
<div className='applications-list__search'>
|
||||
<i className='fa fa-search'/>
|
||||
{q.get('search') && (
|
||||
<i className='fa fa-times' onClick={() => ctx.navigation.goto('.', { search: null }, { replace: true })}/>
|
||||
)}
|
||||
<Autocomplete
|
||||
filterSuggestions={true}
|
||||
renderInput={(inputProps) => (
|
||||
<input {...inputProps} onFocus={(e) => {
|
||||
e.target.select();
|
||||
if (inputProps.onFocus) {
|
||||
inputProps.onFocus(e);
|
||||
}
|
||||
}} className='argo-field' />
|
||||
)}
|
||||
renderItem={(item) => (
|
||||
<React.Fragment>
|
||||
<i className='icon argo-icon-application'/> {item.label}
|
||||
</React.Fragment>
|
||||
)}
|
||||
onSelect={(val) => {
|
||||
ctx.navigation.goto(`./${val}`);
|
||||
<ClusterCtx.Provider value={clusters}>
|
||||
<Consumer>
|
||||
{(ctx) => (
|
||||
<Page
|
||||
title='Applications'
|
||||
toolbar={services.viewPreferences.getPreferences().map((pref) => ({
|
||||
breadcrumbs: [{title: 'Applications', path: '/applications'}],
|
||||
tools: (
|
||||
<React.Fragment key='app-list-tools'>
|
||||
<span className='applications-list__view-type'>
|
||||
<i
|
||||
className={classNames('fa fa-th', {selected: pref.appList.view === 'tiles'})}
|
||||
onClick={() => {
|
||||
ctx.navigation.goto('.', {view: 'tiles'});
|
||||
services.viewPreferences.updatePreferences({appList: {...pref.appList, view: 'tiles'}});
|
||||
}}
|
||||
onChange={(e) => ctx.navigation.goto('.', { search: e.target.value }, { replace: true })}
|
||||
value={q.get('search') || ''} items={applications.map((app) => app.metadata.name)}/>
|
||||
</div>
|
||||
)}
|
||||
</Query>
|
||||
<ApplicationsFilter applications={applications} pref={pref} onChange={(newPref) => {
|
||||
services.viewPreferences.updatePreferences({appList: newPref});
|
||||
ctx.navigation.goto('.', {
|
||||
proj: newPref.projectsFilter.join(','),
|
||||
sync: newPref.syncFilter.join(','),
|
||||
health: newPref.healthFilter.join(','),
|
||||
namespace: newPref.namespacesFilter.join(','),
|
||||
cluster: newPref.clustersFilter.join(','),
|
||||
labels: newPref.labelsFilter.join(','),
|
||||
});
|
||||
}} />
|
||||
</div>
|
||||
<div className='columns small-12 xxlarge-10'>
|
||||
{pref.view === 'summary' && (
|
||||
<ApplicationsSummary applications={filterApps(applications, pref, pref.search)} />
|
||||
) || (
|
||||
<Paginate
|
||||
preferencesKey='applications-list'
|
||||
page={pref.page}
|
||||
emptyState={() => (
|
||||
<EmptyState icon='fa fa-search'>
|
||||
<h4>No applications found</h4>
|
||||
<h5>Try to change filter criteria</h5>
|
||||
</EmptyState>
|
||||
)}
|
||||
data={filterApps(applications, pref, pref.search)} onPageChange={(page) => ctx.navigation.goto('.', { page })} >
|
||||
{(data) => (
|
||||
pref.view === 'tiles' && (
|
||||
<ApplicationTiles
|
||||
applications={data}
|
||||
syncApplication={(appName) => ctx.navigation.goto('.', { syncApp: appName })}
|
||||
refreshApplication={(appName) => services.applications.get(appName, 'normal')}
|
||||
deleteApplication={(appName) => AppUtils.deleteApplication(appName, ctx)}
|
||||
/>
|
||||
) || (
|
||||
<ApplicationsTable applications={data}
|
||||
syncApplication={(appName) => ctx.navigation.goto('.', { syncApp: appName })}
|
||||
refreshApplication={(appName) => services.applications.get(appName, 'normal')}
|
||||
deleteApplication={(appName) => AppUtils.deleteApplication(appName, ctx)}
|
||||
<i
|
||||
className={classNames('fa fa-th-list', {selected: pref.appList.view === 'list'})}
|
||||
onClick={() => {
|
||||
ctx.navigation.goto('.', {view: 'list'});
|
||||
services.viewPreferences.updatePreferences({appList: {...pref.appList, view: 'list'}});
|
||||
}}
|
||||
/>
|
||||
)
|
||||
<i
|
||||
className={classNames('fa fa-chart-pie', {selected: pref.appList.view === 'summary'})}
|
||||
onClick={() => {
|
||||
ctx.navigation.goto('.', {view: 'summary'});
|
||||
services.viewPreferences.updatePreferences({appList: {...pref.appList, view: 'summary'}});
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
</React.Fragment>
|
||||
),
|
||||
actionMenu: {
|
||||
items: [
|
||||
{
|
||||
title: 'New App',
|
||||
iconClassName: 'fa fa-plus',
|
||||
action: () => ctx.navigation.goto('.', {new: '{}'}),
|
||||
},
|
||||
{
|
||||
title: 'Sync Apps',
|
||||
iconClassName: 'fa fa-sync',
|
||||
action: () => ctx.navigation.goto('.', {syncApps: true}),
|
||||
},
|
||||
],
|
||||
},
|
||||
}))}>
|
||||
<div className='applications-list'>
|
||||
<ViewPref>
|
||||
{(pref) => (
|
||||
<DataLoader
|
||||
load={() => loadApplications()}
|
||||
loadingRenderer={() => (
|
||||
<div className='argo-container'>
|
||||
<MockupList height={100} marginTop={30} />
|
||||
</div>
|
||||
)}>
|
||||
{(applications: models.Application[]) =>
|
||||
applications.length === 0 && (pref.labelsFilter || []).length === 0 ? (
|
||||
<EmptyState icon='argo-icon-application'>
|
||||
<h4>No applications yet</h4>
|
||||
<h5>Create new application to start managing resources in your cluster</h5>
|
||||
<button className='argo-button argo-button--base' onClick={() => ctx.navigation.goto('.', {new: JSON.stringify({})})}>
|
||||
Create application
|
||||
</button>
|
||||
</EmptyState>
|
||||
) : (
|
||||
<div className='row'>
|
||||
<div className='columns small-12 xxlarge-2'>
|
||||
<Query>
|
||||
{(q) => (
|
||||
<div className='applications-list__search'>
|
||||
<i className='fa fa-search' />
|
||||
{q.get('search') && (
|
||||
<i className='fa fa-times' onClick={() => ctx.navigation.goto('.', {search: null}, {replace: true})} />
|
||||
)}
|
||||
<Autocomplete
|
||||
filterSuggestions={true}
|
||||
renderInput={(inputProps) => (
|
||||
<input
|
||||
{...inputProps}
|
||||
onFocus={(e) => {
|
||||
e.target.select();
|
||||
if (inputProps.onFocus) {
|
||||
inputProps.onFocus(e);
|
||||
}
|
||||
}}
|
||||
className='argo-field'
|
||||
/>
|
||||
)}
|
||||
renderItem={(item) => (
|
||||
<React.Fragment>
|
||||
<i className='icon argo-icon-application' /> {item.label}
|
||||
</React.Fragment>
|
||||
)}
|
||||
onSelect={(val) => {
|
||||
ctx.navigation.goto(`./${val}`);
|
||||
}}
|
||||
onChange={(e) => ctx.navigation.goto('.', {search: e.target.value}, {replace: true})}
|
||||
value={q.get('search') || ''}
|
||||
items={applications.map((app) => app.metadata.name)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Query>
|
||||
<ApplicationsFilter
|
||||
applications={applications}
|
||||
pref={pref}
|
||||
onChange={(newPref) => {
|
||||
services.viewPreferences.updatePreferences({appList: newPref});
|
||||
ctx.navigation.goto('.', {
|
||||
proj: newPref.projectsFilter.join(','),
|
||||
sync: newPref.syncFilter.join(','),
|
||||
health: newPref.healthFilter.join(','),
|
||||
namespace: newPref.namespacesFilter.join(','),
|
||||
cluster: newPref.clustersFilter.join(','),
|
||||
labels: newPref.labelsFilter.map(encodeURIComponent).join(','),
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{syncAppsInput && (
|
||||
<ApplicationsSyncPanel
|
||||
key='syncsPanel'
|
||||
show={syncAppsInput}
|
||||
hide={() => ctx.navigation.goto('.', {syncApps: null})}
|
||||
apps={filterApps(applications, pref, pref.search)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className='columns small-12 xxlarge-10'>
|
||||
{(pref.view === 'summary' && <ApplicationsSummary applications={filterApps(applications, pref, pref.search)} />) || (
|
||||
<Paginate
|
||||
preferencesKey='applications-list'
|
||||
page={pref.page}
|
||||
emptyState={() => (
|
||||
<EmptyState icon='fa fa-search'>
|
||||
<h4>No applications found</h4>
|
||||
<h5>Try to change filter criteria</h5>
|
||||
</EmptyState>
|
||||
)}
|
||||
data={filterApps(applications, pref, pref.search)}
|
||||
onPageChange={(page) => ctx.navigation.goto('.', {page})}>
|
||||
{(data) =>
|
||||
(pref.view === 'tiles' && (
|
||||
<ApplicationTiles
|
||||
applications={data}
|
||||
syncApplication={(appName) => ctx.navigation.goto('.', {syncApp: appName})}
|
||||
refreshApplication={(appName) => services.applications.get(appName, 'normal')}
|
||||
deleteApplication={(appName) => AppUtils.deleteApplication(appName, ctx)}
|
||||
/>
|
||||
)) || (
|
||||
<ApplicationsTable
|
||||
applications={data}
|
||||
syncApplication={(appName) => ctx.navigation.goto('.', {syncApp: appName})}
|
||||
refreshApplication={(appName) => services.applications.get(appName, 'normal')}
|
||||
deleteApplication={(appName) => AppUtils.deleteApplication(appName, ctx)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</Paginate>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</DataLoader>
|
||||
)}
|
||||
</Paginate>
|
||||
)}
|
||||
</div>
|
||||
</ViewPref>
|
||||
</div>
|
||||
)
|
||||
<ObservableQuery>
|
||||
{(q) => (
|
||||
<DataLoader
|
||||
load={() =>
|
||||
q.flatMap((params) => {
|
||||
const syncApp = params.get('syncApp');
|
||||
return (syncApp && Observable.fromPromise(services.applications.get(syncApp))) || Observable.from([null]);
|
||||
})
|
||||
}>
|
||||
{(app) => (
|
||||
<ApplicationSyncPanel key='syncPanel' application={app} selectedResource={'all'} hide={() => ctx.navigation.goto('.', {syncApp: null})} />
|
||||
)}
|
||||
</DataLoader>
|
||||
)}
|
||||
</ObservableQuery>
|
||||
<SlidingPanel
|
||||
isShown={!!appInput}
|
||||
onClose={() => ctx.navigation.goto('.', {new: null})}
|
||||
header={
|
||||
<div>
|
||||
<button className='argo-button argo-button--base' onClick={() => createApi && createApi.submitForm(null)}>
|
||||
Create
|
||||
</button>{' '}
|
||||
<button onClick={() => ctx.navigation.goto('.', {new: null})} className='argo-button argo-button--base-o'>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
}>
|
||||
{appInput && (
|
||||
<ApplicationCreatePanel
|
||||
getFormApi={(api) => {
|
||||
setCreateApi(api);
|
||||
}}
|
||||
createApp={async (app) => {
|
||||
try {
|
||||
await services.applications.create(app);
|
||||
ctx.navigation.goto('.', {new: null});
|
||||
} catch (e) {
|
||||
ctx.notifications.show({
|
||||
content: <ErrorNotification title='Unable to create application' e={e} />,
|
||||
type: NotificationType.Error,
|
||||
});
|
||||
}
|
||||
}}
|
||||
app={appInput}
|
||||
onAppChanged={(app) => ctx.navigation.goto('.', {new: JSON.stringify(app)}, {replace: true})}
|
||||
/>
|
||||
)}
|
||||
</SlidingPanel>
|
||||
</Page>
|
||||
)}
|
||||
</DataLoader>
|
||||
}</ViewPref>
|
||||
</div>
|
||||
<ObservableQuery>
|
||||
{(q) => (
|
||||
<DataLoader load={() => q.flatMap((params) => {
|
||||
const syncApp = params.get('syncApp');
|
||||
return syncApp && Observable.fromPromise(services.applications.get(syncApp)) || Observable.from([null]);
|
||||
}) }>
|
||||
{(app) => (
|
||||
<ApplicationSyncPanel key='syncPanel' application={app} selectedResource={'all'} hide={() => ctx.navigation.goto('.', { syncApp: null })} />
|
||||
)}
|
||||
</DataLoader>
|
||||
)}
|
||||
</ObservableQuery>
|
||||
<SlidingPanel isShown={!!appInput} onClose={() => ctx.navigation.goto('.', { new: null })} header={
|
||||
<div>
|
||||
<button className='argo-button argo-button--base'
|
||||
onClick={() => createApi && createApi.submitForm(null)}>
|
||||
Create
|
||||
</button> <button onClick={() => ctx.navigation.goto('.', { new: null })} className='argo-button argo-button--base-o'>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
}>
|
||||
{appInput && <ApplicationCreatePanel getFormApi={(api) => {
|
||||
setCreateApi(api);
|
||||
}} createApp={ async (app) => {
|
||||
try {
|
||||
await services.applications.create(app);
|
||||
ctx.navigation.goto('.', { new: null });
|
||||
} catch (e) {
|
||||
ctx.notifications.show({
|
||||
content: <ErrorNotification title='Unable to create application' e={e}/>,
|
||||
type: NotificationType.Error,
|
||||
});
|
||||
}
|
||||
}}
|
||||
app={appInput}
|
||||
onAppChanged={(app) => ctx.navigation.goto('.', { new: JSON.stringify(app) }, { replace: true })} />}
|
||||
</SlidingPanel>
|
||||
</Page>
|
||||
)}
|
||||
</Consumer>
|
||||
</ClusterCtx.Provider>);
|
||||
</Consumer>
|
||||
</ClusterCtx.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { DropDownMenu, Tooltip } from 'argo-ui';
|
||||
import {DropDownMenu} from 'argo-ui';
|
||||
import * as React from 'react';
|
||||
const GitUrlParse = require('git-url-parse');
|
||||
|
||||
import { Cluster } from '../../../shared/components';
|
||||
import { Consumer } from '../../../shared/context';
|
||||
import * as models from '../../../shared/models';
|
||||
@@ -10,11 +8,6 @@ import * as AppUtils from '../utils';
|
||||
|
||||
require('./applications-table.scss');
|
||||
|
||||
function shortRepo(repo: string) {
|
||||
const url = GitUrlParse(repo);
|
||||
return <Tooltip content={repo}><span>{url.pathname.slice(1)}</span></Tooltip>;
|
||||
}
|
||||
|
||||
export const ApplicationsTable = (props: {
|
||||
applications: models.Application[];
|
||||
syncApplication: (appName: string) => any;
|
||||
@@ -44,7 +37,7 @@ export const ApplicationsTable = (props: {
|
||||
<div className='row'>
|
||||
<div className='show-for-xxlarge columns small-2'>Source:</div>
|
||||
<div className='columns small-12 xxlarge-10' style={{position: 'relative'}}>
|
||||
{shortRepo(app.spec.source.repoURL)}/{app.spec.source.path}
|
||||
{app.spec.source.repoURL}/{app.spec.source.path || app.spec.source.chart}
|
||||
<div className='applications-table__meta'>
|
||||
<span>{app.spec.source.targetRevision || 'HEAD'}</span>
|
||||
{Object.keys(app.metadata.labels || {}).map((label) => <span key={label}>{`${label}=${app.metadata.labels[label]}`}</span>)}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user