mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-03-07 08:58:47 +01:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5bcd846fa1 | ||
|
|
7af7aaa08f | ||
|
|
ccb64f1c7e | ||
|
|
6d4de2ec5d | ||
|
|
bc13533afa | ||
|
|
3260ecc729 | ||
|
|
d3f81decf3 | ||
|
|
25823b88d9 | ||
|
|
56a8ce5ff2 | ||
|
|
153bf967e9 | ||
|
|
2c10c033db | ||
|
|
e883e7498f | ||
|
|
0c3c7e2fa9 | ||
|
|
de63eb4e52 | ||
|
|
a119a5cc93 | ||
|
|
dc744bb21d | ||
|
|
4d5f9bdb5d | ||
|
|
60104aca6f | ||
|
|
3ea15f05f2 | ||
|
|
d557447214 | ||
|
|
67379d881b | ||
|
|
7d335432cd | ||
|
|
f7b6b82a04 | ||
|
|
e81b22bc61 | ||
|
|
65d43364ec |
45
.github/workflows/ci-build.yaml
vendored
45
.github/workflows/ci-build.yaml
vendored
@@ -9,6 +9,7 @@ on:
|
||||
pull_request:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'release-*'
|
||||
|
||||
env:
|
||||
# Golang version to use across CI steps
|
||||
@@ -27,9 +28,9 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
|
||||
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Download all Go modules
|
||||
@@ -45,13 +46,13 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
|
||||
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # v3.2.6
|
||||
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -69,9 +70,9 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
|
||||
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Run golangci-lint
|
||||
@@ -92,11 +93,11 @@ jobs:
|
||||
- name: Create checkout directory
|
||||
run: mkdir -p ~/go/src/github.com/argoproj
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
|
||||
- name: Create symlink in GOPATH
|
||||
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
|
||||
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Install required packages
|
||||
@@ -116,7 +117,7 @@ jobs:
|
||||
run: |
|
||||
echo "/usr/local/bin" >> $GITHUB_PATH
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # v3.2.6
|
||||
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -159,11 +160,11 @@ jobs:
|
||||
- name: Create checkout directory
|
||||
run: mkdir -p ~/go/src/github.com/argoproj
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
|
||||
- name: Create symlink in GOPATH
|
||||
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
|
||||
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Install required packages
|
||||
@@ -183,7 +184,7 @@ jobs:
|
||||
run: |
|
||||
echo "/usr/local/bin" >> $GITHUB_PATH
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # v3.2.6
|
||||
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -214,9 +215,9 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
|
||||
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Create symlink in GOPATH
|
||||
@@ -262,14 +263,14 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
|
||||
- name: Setup NodeJS
|
||||
uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
|
||||
with:
|
||||
node-version: '12.18.4'
|
||||
- name: Restore node dependency cache
|
||||
id: cache-dependencies
|
||||
uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # v3.2.6
|
||||
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
|
||||
with:
|
||||
path: ui/node_modules
|
||||
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
|
||||
@@ -299,12 +300,12 @@ jobs:
|
||||
sonar_secret: ${{ secrets.SONAR_TOKEN }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Restore node dependency cache
|
||||
id: cache-dependencies
|
||||
uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # v3.2.6
|
||||
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
|
||||
with:
|
||||
path: ui/node_modules
|
||||
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
|
||||
@@ -378,9 +379,9 @@ jobs:
|
||||
GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
|
||||
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: GH actions workaround - Kill XSP4 process
|
||||
@@ -398,7 +399,7 @@ jobs:
|
||||
sudo chown runner $HOME/.kube/config
|
||||
kubectl version
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # v3.2.6
|
||||
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
|
||||
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
|
||||
4
.github/workflows/image.yaml
vendored
4
.github/workflows/image.yaml
vendored
@@ -29,10 +29,10 @@ jobs:
|
||||
env:
|
||||
GOPATH: /home/runner/work/argo-cd/argo-cd
|
||||
steps:
|
||||
- uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
|
||||
- uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
- uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
|
||||
with:
|
||||
path: src/github.com/argoproj/argo-cd
|
||||
|
||||
|
||||
4
.github/workflows/release.yaml
vendored
4
.github/workflows/release.yaml
vendored
@@ -43,7 +43,7 @@ jobs:
|
||||
GIT_EMAIL: argoproj@gmail.com
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -147,7 +147,7 @@ jobs:
|
||||
echo "RELEASE_NOTES=${RELEASE_NOTES}" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
|
||||
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
|
||||
|
||||
2
.github/workflows/update-snyk.yaml
vendored
2
.github/workflows/update-snyk.yaml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build reports
|
||||
|
||||
1
USERS.md
1
USERS.md
@@ -188,6 +188,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [RapidAPI](https://www.rapidapi.com/)
|
||||
1. [Recreation.gov](https://www.recreation.gov/)
|
||||
1. [Red Hat](https://www.redhat.com/)
|
||||
1. [Redpill Linpro](https://www.redpill-linpro.com/)
|
||||
1. [reev.com](https://www.reev.com/)
|
||||
1. [RightRev](https://rightrev.com/)
|
||||
1. [Rise](https://www.risecard.eu/)
|
||||
|
||||
@@ -2,7 +2,6 @@ package generators
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
||||
@@ -25,7 +24,7 @@ type TransformResult struct {
|
||||
Template argoprojiov1alpha1.ApplicationSetTemplate
|
||||
}
|
||||
|
||||
//Transform a spec generator to list of paramSets and a template
|
||||
// Transform a spec generator to list of paramSets and a template
|
||||
func Transform(requestedGenerator argoprojiov1alpha1.ApplicationSetGenerator, allGenerators map[string]Generator, baseTemplate argoprojiov1alpha1.ApplicationSetTemplate, appSet *argoprojiov1alpha1.ApplicationSet, genParams map[string]interface{}) ([]TransformResult, error) {
|
||||
selector, err := metav1.LabelSelectorAsSelector(requestedGenerator.Selector)
|
||||
if err != nil {
|
||||
@@ -132,27 +131,15 @@ func mergeGeneratorTemplate(g Generator, requestedGenerator *argoprojiov1alpha1.
|
||||
return *dest, err
|
||||
}
|
||||
|
||||
// Currently for Matrix Generator. Allows interpolating the matrix's 2nd child generator with values from the 1st child generator
|
||||
// InterpolateGenerator allows interpolating the matrix's 2nd child generator with values from the 1st child generator
|
||||
// "params" parameter is an array, where each index corresponds to a generator. Each index contains a map w/ that generator's parameters.
|
||||
func InterpolateGenerator(requestedGenerator *argoprojiov1alpha1.ApplicationSetGenerator, params map[string]interface{}, useGoTemplate bool) (argoprojiov1alpha1.ApplicationSetGenerator, error) {
|
||||
interpolatedGenerator := requestedGenerator.DeepCopy()
|
||||
tmplBytes, err := json.Marshal(interpolatedGenerator)
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("requestedGenerator", interpolatedGenerator).Error("error marshalling requested generator for interpolation")
|
||||
return *interpolatedGenerator, err
|
||||
}
|
||||
|
||||
render := utils.Render{}
|
||||
replacedTmplStr, err := render.Replace(string(tmplBytes), params, useGoTemplate)
|
||||
interpolatedGenerator, err := render.RenderGeneratorParams(requestedGenerator, params, useGoTemplate)
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("interpolatedGeneratorString", replacedTmplStr).Error("error interpolating generator with other generator's parameter")
|
||||
log.WithError(err).WithField("interpolatedGenerator", interpolatedGenerator).Error("error interpolating generator with other generator's parameter")
|
||||
return *interpolatedGenerator, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal([]byte(replacedTmplStr), interpolatedGenerator)
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("requestedGenerator", interpolatedGenerator).Error("error unmarshalling requested generator for interpolation")
|
||||
return *interpolatedGenerator, err
|
||||
}
|
||||
return *interpolatedGenerator, nil
|
||||
}
|
||||
|
||||
@@ -6,9 +6,11 @@ import (
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
testutils "github.com/argoproj/argo-cd/v2/applicationset/utils/test"
|
||||
argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
|
||||
"github.com/stretchr/testify/mock"
|
||||
@@ -159,8 +161,8 @@ func getMockClusterGenerator() Generator {
|
||||
}
|
||||
|
||||
func getMockGitGenerator() Generator {
|
||||
argoCDServiceMock := argoCDServiceMock{mock: &mock.Mock{}}
|
||||
argoCDServiceMock.mock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything).Return([]string{"app1", "app2", "app_3", "p1/app4"}, nil)
|
||||
argoCDServiceMock := testutils.ArgoCDServiceMock{Mock: &mock.Mock{}}
|
||||
argoCDServiceMock.Mock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything).Return([]string{"app1", "app2", "app_3", "p1/app4"}, nil)
|
||||
var gitGenerator = NewGitGenerator(argoCDServiceMock)
|
||||
return gitGenerator
|
||||
}
|
||||
@@ -248,6 +250,60 @@ func TestInterpolateGenerator(t *testing.T) {
|
||||
Path: "{{server}}",
|
||||
}
|
||||
|
||||
requestedGenerator = &argoprojiov1alpha1.ApplicationSetGenerator{
|
||||
Git: &argoprojiov1alpha1.GitGenerator{
|
||||
Files: append([]argoprojiov1alpha1.GitFileGeneratorItem{}, fileNamePath, fileServerPath),
|
||||
Template: argoprojiov1alpha1.ApplicationSetTemplate{},
|
||||
},
|
||||
}
|
||||
clusterGeneratorParams := map[string]interface{}{
|
||||
"name": "production_01/west", "server": "https://production-01.example.com",
|
||||
}
|
||||
interpolatedGenerator, err = InterpolateGenerator(requestedGenerator, clusterGeneratorParams, false)
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("requestedGenerator", requestedGenerator).Error("error interpolating Generator")
|
||||
return
|
||||
}
|
||||
assert.Equal(t, "production_01/west", interpolatedGenerator.Git.Files[0].Path)
|
||||
assert.Equal(t, "https://production-01.example.com", interpolatedGenerator.Git.Files[1].Path)
|
||||
}
|
||||
|
||||
func TestInterpolateGenerator_go(t *testing.T) {
|
||||
requestedGenerator := &argoprojiov1alpha1.ApplicationSetGenerator{
|
||||
Clusters: &argoprojiov1alpha1.ClusterGenerator{
|
||||
Selector: metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
"path-basename": "{{base .path.path}}",
|
||||
"path-zero": "{{index .path.segments 0}}",
|
||||
"path-full": "{{.path.path}}",
|
||||
"kubernetes.io/environment": `{{default "foo" .my_label}}`,
|
||||
}},
|
||||
},
|
||||
}
|
||||
gitGeneratorParams := map[string]interface{}{
|
||||
"path": map[string]interface{}{
|
||||
"path": "p1/p2/app3",
|
||||
"segments": []string{"p1", "p2", "app3"},
|
||||
},
|
||||
}
|
||||
interpolatedGenerator, err := InterpolateGenerator(requestedGenerator, gitGeneratorParams, true)
|
||||
require.NoError(t, err)
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("requestedGenerator", requestedGenerator).Error("error interpolating Generator")
|
||||
return
|
||||
}
|
||||
assert.Equal(t, "app3", interpolatedGenerator.Clusters.Selector.MatchLabels["path-basename"])
|
||||
assert.Equal(t, "p1", interpolatedGenerator.Clusters.Selector.MatchLabels["path-zero"])
|
||||
assert.Equal(t, "p1/p2/app3", interpolatedGenerator.Clusters.Selector.MatchLabels["path-full"])
|
||||
|
||||
fileNamePath := argoprojiov1alpha1.GitFileGeneratorItem{
|
||||
Path: "{{.name}}",
|
||||
}
|
||||
fileServerPath := argoprojiov1alpha1.GitFileGeneratorItem{
|
||||
Path: "{{.server}}",
|
||||
}
|
||||
|
||||
requestedGenerator = &argoprojiov1alpha1.ApplicationSetGenerator{
|
||||
Git: &argoprojiov1alpha1.GitGenerator{
|
||||
Files: append([]argoprojiov1alpha1.GitFileGeneratorItem{}, fileNamePath, fileServerPath),
|
||||
|
||||
@@ -58,9 +58,9 @@ func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Applic
|
||||
|
||||
var err error
|
||||
var res []map[string]interface{}
|
||||
if appSetGenerator.Git.Directories != nil {
|
||||
if len(appSetGenerator.Git.Directories) != 0 {
|
||||
res, err = g.generateParamsForGitDirectories(appSetGenerator, appSet.Spec.GoTemplate)
|
||||
} else if appSetGenerator.Git.Files != nil {
|
||||
} else if len(appSetGenerator.Git.Files) != 0 {
|
||||
res, err = g.generateParamsForGitFiles(appSetGenerator, appSet.Spec.GoTemplate)
|
||||
} else {
|
||||
return nil, EmptyAppSetGeneratorError
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package generators
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
@@ -9,6 +8,7 @@ import (
|
||||
"github.com/stretchr/testify/mock"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
testutils "github.com/argoproj/argo-cd/v2/applicationset/utils/test"
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
@@ -20,33 +20,6 @@ import (
|
||||
// return io.NewCloser(func() error { return nil }), c.RepoServerServiceClient, nil
|
||||
// }
|
||||
|
||||
type argoCDServiceMock struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (a argoCDServiceMock) GetApps(ctx context.Context, repoURL string, revision string) ([]string, error) {
|
||||
args := a.mock.Called(ctx, repoURL, revision)
|
||||
|
||||
return args.Get(0).([]string), args.Error(1)
|
||||
}
|
||||
|
||||
func (a argoCDServiceMock) GetFiles(ctx context.Context, repoURL string, revision string, pattern string) (map[string][]byte, error) {
|
||||
args := a.mock.Called(ctx, repoURL, revision, pattern)
|
||||
|
||||
return args.Get(0).(map[string][]byte), args.Error(1)
|
||||
}
|
||||
|
||||
func (a argoCDServiceMock) GetFileContent(ctx context.Context, repoURL string, revision string, path string) ([]byte, error) {
|
||||
args := a.mock.Called(ctx, repoURL, revision, path)
|
||||
|
||||
return args.Get(0).([]byte), args.Error(1)
|
||||
}
|
||||
|
||||
func (a argoCDServiceMock) GetDirectories(ctx context.Context, repoURL string, revision string) ([]string, error) {
|
||||
args := a.mock.Called(ctx, repoURL, revision)
|
||||
return args.Get(0).([]string), args.Error(1)
|
||||
}
|
||||
|
||||
func Test_generateParamsFromGitFile(t *testing.T) {
|
||||
params, err := (*GitGenerator)(nil).generateParamsFromGitFile("path/dir/file_name.yaml", []byte(`
|
||||
foo:
|
||||
@@ -271,9 +244,9 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) {
|
||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
argoCDServiceMock := argoCDServiceMock{mock: &mock.Mock{}}
|
||||
argoCDServiceMock := testutils.ArgoCDServiceMock{Mock: &mock.Mock{}}
|
||||
|
||||
argoCDServiceMock.mock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError)
|
||||
argoCDServiceMock.Mock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError)
|
||||
|
||||
var gitGenerator = NewGitGenerator(argoCDServiceMock)
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
@@ -301,7 +274,7 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) {
|
||||
assert.Equal(t, testCaseCopy.expected, got)
|
||||
}
|
||||
|
||||
argoCDServiceMock.mock.AssertExpectations(t)
|
||||
argoCDServiceMock.Mock.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -566,9 +539,9 @@ func TestGitGenerateParamsFromDirectoriesGoTemplate(t *testing.T) {
|
||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
argoCDServiceMock := argoCDServiceMock{mock: &mock.Mock{}}
|
||||
argoCDServiceMock := testutils.ArgoCDServiceMock{Mock: &mock.Mock{}}
|
||||
|
||||
argoCDServiceMock.mock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError)
|
||||
argoCDServiceMock.Mock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError)
|
||||
|
||||
var gitGenerator = NewGitGenerator(argoCDServiceMock)
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
@@ -597,7 +570,7 @@ func TestGitGenerateParamsFromDirectoriesGoTemplate(t *testing.T) {
|
||||
assert.Equal(t, testCaseCopy.expected, got)
|
||||
}
|
||||
|
||||
argoCDServiceMock.mock.AssertExpectations(t)
|
||||
argoCDServiceMock.Mock.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -857,8 +830,8 @@ cluster:
|
||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
argoCDServiceMock := argoCDServiceMock{mock: &mock.Mock{}}
|
||||
argoCDServiceMock.mock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
argoCDServiceMock := testutils.ArgoCDServiceMock{Mock: &mock.Mock{}}
|
||||
argoCDServiceMock.Mock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(testCaseCopy.repoFileContents, testCaseCopy.repoPathsError)
|
||||
|
||||
var gitGenerator = NewGitGenerator(argoCDServiceMock)
|
||||
@@ -887,7 +860,7 @@ cluster:
|
||||
assert.ElementsMatch(t, testCaseCopy.expected, got)
|
||||
}
|
||||
|
||||
argoCDServiceMock.mock.AssertExpectations(t)
|
||||
argoCDServiceMock.Mock.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1206,8 +1179,8 @@ cluster:
|
||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
argoCDServiceMock := argoCDServiceMock{mock: &mock.Mock{}}
|
||||
argoCDServiceMock.mock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
argoCDServiceMock := testutils.ArgoCDServiceMock{Mock: &mock.Mock{}}
|
||||
argoCDServiceMock.Mock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(testCaseCopy.repoFileContents, testCaseCopy.repoPathsError)
|
||||
|
||||
var gitGenerator = NewGitGenerator(argoCDServiceMock)
|
||||
@@ -1237,7 +1210,7 @@ cluster:
|
||||
assert.ElementsMatch(t, testCaseCopy.expected, got)
|
||||
}
|
||||
|
||||
argoCDServiceMock.mock.AssertExpectations(t)
|
||||
argoCDServiceMock.Mock.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@@ -16,6 +17,7 @@ import (
|
||||
"github.com/stretchr/testify/mock"
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
|
||||
testutils "github.com/argoproj/argo-cd/v2/applicationset/utils/test"
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
@@ -857,3 +859,72 @@ func (g *generatorMock) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.Appl
|
||||
return args.Get(0).(time.Duration)
|
||||
|
||||
}
|
||||
|
||||
func TestGitGenerator_GenerateParams_list_x_git_matrix_generator(t *testing.T) {
|
||||
// Given a matrix generator over a list generator and a git files generator, the nested git files generator should
|
||||
// be treated as a files generator, and it should produce parameters.
|
||||
|
||||
// This tests for a specific bug where a nested git files generator was being treated as a directory generator. This
|
||||
// happened because, when the matrix generator was being processed, the nested git files generator was being
|
||||
// interpolated by the deeplyReplace function. That function cannot differentiate between a nil slice and an empty
|
||||
// slice. So it was replacing the `Directories` field with an empty slice, which the ApplicationSet controller
|
||||
// interpreted as meaning this was a directory generator, not a files generator.
|
||||
|
||||
// Now instead of checking for nil, we check whether the field is a non-empty slice. This test prevents a regression
|
||||
// of that bug.
|
||||
|
||||
listGeneratorMock := &generatorMock{}
|
||||
listGeneratorMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), mock.AnythingOfType("*v1alpha1.ApplicationSet")).Return([]map[string]interface{}{
|
||||
{"some": "value"},
|
||||
}, nil)
|
||||
listGeneratorMock.On("GetTemplate", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator")).Return(&argoprojiov1alpha1.ApplicationSetTemplate{})
|
||||
|
||||
gitGeneratorSpec := &argoprojiov1alpha1.GitGenerator{
|
||||
RepoURL: "https://git.example.com",
|
||||
Files: []argoprojiov1alpha1.GitFileGeneratorItem{
|
||||
{Path: "some/path.json"},
|
||||
},
|
||||
}
|
||||
|
||||
repoServiceMock := testutils.ArgoCDServiceMock{Mock: &mock.Mock{}}
|
||||
repoServiceMock.Mock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(map[string][]byte{
|
||||
"some/path.json": []byte("test: content"),
|
||||
}, nil)
|
||||
gitGenerator := NewGitGenerator(repoServiceMock)
|
||||
|
||||
matrixGenerator := NewMatrixGenerator(map[string]Generator{
|
||||
"List": listGeneratorMock,
|
||||
"Git": gitGenerator,
|
||||
})
|
||||
|
||||
matrixGeneratorSpec := &argoprojiov1alpha1.MatrixGenerator{
|
||||
Generators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
||||
{
|
||||
List: &argoprojiov1alpha1.ListGenerator{
|
||||
Elements: []apiextensionsv1.JSON{
|
||||
{
|
||||
Raw: []byte(`{"some": "value"}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Git: gitGeneratorSpec,
|
||||
},
|
||||
},
|
||||
}
|
||||
params, err := matrixGenerator.GenerateParams(&argoprojiov1alpha1.ApplicationSetGenerator{
|
||||
Matrix: matrixGeneratorSpec,
|
||||
}, &argoprojiov1alpha1.ApplicationSet{})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []map[string]interface{}{{
|
||||
"path": "some",
|
||||
"path.basename": "some",
|
||||
"path.basenameNormalized": "some",
|
||||
"path.filename": "path.json",
|
||||
"path.filenameNormalized": "path.json",
|
||||
"path[0]": "some",
|
||||
"some": "value",
|
||||
"test": "content",
|
||||
}}, params)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"net/http"
|
||||
pathpkg "path"
|
||||
|
||||
gitlab "github.com/xanzy/go-gitlab"
|
||||
@@ -144,7 +145,11 @@ func (g *GitlabProvider) listBranches(_ context.Context, repo *Repository) ([]gi
|
||||
branches := []gitlab.Branch{}
|
||||
// If we don't specifically want to query for all branches, just use the default branch and call it a day.
|
||||
if !g.allBranches {
|
||||
gitlabBranch, _, err := g.client.Branches.GetBranch(repo.RepositoryId, repo.Branch, nil)
|
||||
gitlabBranch, resp, err := g.client.Branches.GetBranch(repo.RepositoryId, repo.Branch, nil)
|
||||
// 404s are not an error here, just a normal false.
|
||||
if resp != nil && resp.StatusCode == http.StatusNotFound {
|
||||
return []gitlab.Branch{}, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -157,6 +162,10 @@ func (g *GitlabProvider) listBranches(_ context.Context, repo *Repository) ([]gi
|
||||
}
|
||||
for {
|
||||
gitlabBranches, resp, err := g.client.Branches.ListBranches(repo.RepositoryId, opt)
|
||||
// 404s are not an error here, just a normal false.
|
||||
if resp != nil && resp.StatusCode == http.StatusNotFound {
|
||||
return []gitlab.Branch{}, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -274,6 +274,8 @@ func gitlabMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) {
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
case "/api/v4/projects/27084533/repository/branches/foo":
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
default:
|
||||
_, err := io.WriteString(w, `[]`)
|
||||
if err != nil {
|
||||
@@ -391,3 +393,29 @@ func TestGitlabHasPath(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGitlabGetBranches(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
gitlabMockHandler(t)(w, r)
|
||||
}))
|
||||
host, _ := NewGitlabProvider(context.Background(), "test-argocd-proton", "", ts.URL, false, true)
|
||||
|
||||
repo := &Repository{
|
||||
RepositoryId: 27084533,
|
||||
Branch: "master",
|
||||
}
|
||||
t.Run("branch exists", func(t *testing.T) {
|
||||
repos, err := host.GetBranches(context.Background(), repo)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, repos[0].Branch, "master")
|
||||
})
|
||||
|
||||
repo2 := &Repository{
|
||||
RepositoryId: 27084533,
|
||||
Branch: "foo",
|
||||
}
|
||||
t.Run("unknown branch", func(t *testing.T) {
|
||||
_, err := host.GetBranches(context.Background(), repo2)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
34
applicationset/utils/test/testutils.go
Normal file
34
applicationset/utils/test/testutils.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
type ArgoCDServiceMock struct {
|
||||
Mock *mock.Mock
|
||||
}
|
||||
|
||||
func (a ArgoCDServiceMock) GetApps(ctx context.Context, repoURL string, revision string) ([]string, error) {
|
||||
args := a.Mock.Called(ctx, repoURL, revision)
|
||||
|
||||
return args.Get(0).([]string), args.Error(1)
|
||||
}
|
||||
|
||||
func (a ArgoCDServiceMock) GetFiles(ctx context.Context, repoURL string, revision string, pattern string) (map[string][]byte, error) {
|
||||
args := a.Mock.Called(ctx, repoURL, revision, pattern)
|
||||
|
||||
return args.Get(0).(map[string][]byte), args.Error(1)
|
||||
}
|
||||
|
||||
func (a ArgoCDServiceMock) GetFileContent(ctx context.Context, repoURL string, revision string, path string) ([]byte, error) {
|
||||
args := a.Mock.Called(ctx, repoURL, revision, path)
|
||||
|
||||
return args.Get(0).([]byte), args.Error(1)
|
||||
}
|
||||
|
||||
func (a ArgoCDServiceMock) GetDirectories(ctx context.Context, repoURL string, revision string) ([]string, error) {
|
||||
args := a.Mock.Called(ctx, repoURL, revision)
|
||||
return args.Get(0).([]string), args.Error(1)
|
||||
}
|
||||
@@ -174,7 +174,7 @@ func (r *Render) deeplyReplace(copy, original reflect.Value, replaceMap map[stri
|
||||
|
||||
func (r *Render) RenderTemplateParams(tmpl *argoappsv1.Application, syncPolicy *argoappsv1.ApplicationSetSyncPolicy, params map[string]interface{}, useGoTemplate bool) (*argoappsv1.Application, error) {
|
||||
if tmpl == nil {
|
||||
return nil, fmt.Errorf("application template is empty ")
|
||||
return nil, fmt.Errorf("application template is empty")
|
||||
}
|
||||
|
||||
if len(params) == 0 {
|
||||
@@ -204,6 +204,27 @@ func (r *Render) RenderTemplateParams(tmpl *argoappsv1.Application, syncPolicy *
|
||||
return replacedTmpl, nil
|
||||
}
|
||||
|
||||
func (r *Render) RenderGeneratorParams(gen *argoappsv1.ApplicationSetGenerator, params map[string]interface{}, useGoTemplate bool) (*argoappsv1.ApplicationSetGenerator, error) {
|
||||
if gen == nil {
|
||||
return nil, fmt.Errorf("generator is empty")
|
||||
}
|
||||
|
||||
if len(params) == 0 {
|
||||
return gen, nil
|
||||
}
|
||||
|
||||
original := reflect.ValueOf(gen)
|
||||
copy := reflect.New(original.Type()).Elem()
|
||||
|
||||
if err := r.deeplyReplace(copy, original, params, useGoTemplate); err != nil {
|
||||
return nil, fmt.Errorf("failed to replace parameters in generator: %w", err)
|
||||
}
|
||||
|
||||
replacedGen := copy.Interface().(*argoappsv1.ApplicationSetGenerator)
|
||||
|
||||
return replacedGen, nil
|
||||
}
|
||||
|
||||
var isTemplatedRegex = regexp.MustCompile(".*{{.*}}.*")
|
||||
|
||||
// Replace executes basic string substitution of a template with replacement values.
|
||||
|
||||
@@ -271,6 +271,16 @@
|
||||
"description": "the application's namespace.",
|
||||
"name": "appNamespace",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"collectionFormat": "multi",
|
||||
"description": "the project names to restrict returned list applications (legacy name for backwards-compatibility).",
|
||||
"name": "project",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -585,6 +595,16 @@
|
||||
"description": "the application's namespace.",
|
||||
"name": "appNamespace",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"collectionFormat": "multi",
|
||||
"description": "the project names to restrict returned list applications (legacy name for backwards-compatibility).",
|
||||
"name": "project",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -3670,6 +3690,16 @@
|
||||
"description": "the application's namespace.",
|
||||
"name": "appNamespace",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"collectionFormat": "multi",
|
||||
"description": "the project names to restrict returned list applications (legacy name for backwards-compatibility).",
|
||||
"name": "project",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
|
||||
@@ -165,7 +165,9 @@ func NewApplicationCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.
|
||||
|
||||
// Get app before creating to see if it is being updated or no change
|
||||
existing, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{Name: &app.Name})
|
||||
if grpc.UnwrapGRPCStatus(err).Code() != codes.NotFound {
|
||||
unwrappedError := grpc.UnwrapGRPCStatus(err).Code()
|
||||
// As part of the fix for CVE-2022-41354, the API will return Permission Denied when an app does not exist.
|
||||
if unwrappedError != codes.NotFound && unwrappedError != codes.PermissionDenied {
|
||||
errors.CheckError(err)
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,10 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/argoproj/pkg/rand"
|
||||
|
||||
@@ -73,9 +75,8 @@ func runCommand(ctx context.Context, command Command, path string, env []string)
|
||||
}
|
||||
logCtx := log.WithFields(log.Fields{"execID": execId})
|
||||
|
||||
// log in a way we can copy-and-paste into a terminal
|
||||
args := strings.Join(cmd.Args, " ")
|
||||
logCtx.WithFields(log.Fields{"dir": cmd.Dir}).Info(args)
|
||||
argsToLog := getCommandArgsToLog(cmd)
|
||||
logCtx.WithFields(log.Fields{"dir": cmd.Dir}).Info(argsToLog)
|
||||
|
||||
var stdout bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
@@ -106,7 +107,7 @@ func runCommand(ctx context.Context, command Command, path string, env []string)
|
||||
logCtx.WithFields(log.Fields{"duration": duration}).Debug(output)
|
||||
|
||||
if err != nil {
|
||||
err := newCmdError(args, errors.New(err.Error()), strings.TrimSpace(stderr.String()))
|
||||
err := newCmdError(argsToLog, errors.New(err.Error()), strings.TrimSpace(stderr.String()))
|
||||
logCtx.Error(err.Error())
|
||||
return strings.TrimSuffix(output, "\n"), err
|
||||
}
|
||||
@@ -114,6 +115,28 @@ func runCommand(ctx context.Context, command Command, path string, env []string)
|
||||
return strings.TrimSuffix(output, "\n"), nil
|
||||
}
|
||||
|
||||
// getCommandArgsToLog represents the given command in a way that we can copy-and-paste into a terminal
|
||||
func getCommandArgsToLog(cmd *exec.Cmd) string {
|
||||
var argsToLog []string
|
||||
for _, arg := range cmd.Args {
|
||||
containsSpace := false
|
||||
for _, r := range arg {
|
||||
if unicode.IsSpace(r) {
|
||||
containsSpace = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if containsSpace {
|
||||
// add quotes and escape any internal quotes
|
||||
argsToLog = append(argsToLog, strconv.Quote(arg))
|
||||
} else {
|
||||
argsToLog = append(argsToLog, arg)
|
||||
}
|
||||
}
|
||||
args := strings.Join(argsToLog, " ")
|
||||
return args
|
||||
}
|
||||
|
||||
type CmdError struct {
|
||||
Args string
|
||||
Stderr string
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
@@ -747,3 +748,30 @@ func TestService_GetParametersAnnouncement(t *testing.T) {
|
||||
require.Nil(t, s.response)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_getCommandArgsToLog(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "no spaces",
|
||||
args: []string{"sh", "-c", "cat"},
|
||||
expected: "sh -c cat",
|
||||
},
|
||||
{
|
||||
name: "spaces",
|
||||
args: []string{"sh", "-c", `echo "hello world"`},
|
||||
expected: `sh -c "echo \"hello world\""`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tcc := tc
|
||||
t.Run(tcc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert.Equal(t, tcc.expected, getCommandArgsToLog(exec.Command(tcc.args[0], tcc.args[1:]...)))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
# Post Selector all generators
|
||||
|
||||
The Selector allows to post-filter based on generated values using the kubernetes common labelSelector format. In the example, the list generator generates a set of two application which then filter by the key value to only select the `env` with value `staging`:
|
||||
|
||||
## Example: List generator + Post Selector
|
||||
```yaml
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: ApplicationSet
|
||||
metadata:
|
||||
name: guestbook
|
||||
spec:
|
||||
generators:
|
||||
- list:
|
||||
elements:
|
||||
- cluster: engineering-dev
|
||||
url: https://kubernetes.default.svc
|
||||
env: staging
|
||||
- cluster: engineering-prod
|
||||
url: https://kubernetes.default.svc
|
||||
env: prod
|
||||
selector:
|
||||
matchLabels:
|
||||
env: staging
|
||||
template:
|
||||
metadata:
|
||||
name: '{{cluster}}-guestbook'
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: https://github.com/argoproj-labs/applicationset.git
|
||||
targetRevision: HEAD
|
||||
path: examples/list-generator/guestbook/{{cluster}}
|
||||
destination:
|
||||
server: '{{url}}'
|
||||
namespace: guestbook
|
||||
```
|
||||
|
||||
The List generator + Post Selector generates a single set of parameters:
|
||||
```yaml
|
||||
- cluster: engineering-dev
|
||||
url: https://kubernetes.default.svc
|
||||
env: staging
|
||||
```
|
||||
@@ -15,4 +15,6 @@ As of this writing there are eight generators:
|
||||
- [Pull Request generator](Generators-Pull-Request.md): The Pull Request generator uses the API of an SCMaaS provider (eg GitHub) to automatically discover open pull requests within an repository.
|
||||
- [Cluster Decision Resource generator](Generators-Cluster-Decision-Resource.md): The Cluster Decision Resource generator is used to interface with Kubernetes custom resources that use custom resource-specific logic to decide which set of Argo CD clusters to deploy to.
|
||||
|
||||
All generators can be filtered by using the [Post Selector](Generators-Post-Selector.md)
|
||||
|
||||
If you are new to generators, begin with the **List** and **Cluster** generators. For more advanced use cases, see the documentation for the remaining generators above.
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
# High Availability
|
||||
|
||||
Argo CD is largely stateless, all data is persisted as Kubernetes objects, which in turn is stored in Kubernetes' etcd. Redis is only used as a throw-away cache and can be lost. When lost, it will be rebuilt without loss of service.
|
||||
Argo CD is largely stateless. All data is persisted as Kubernetes objects, which in turn is stored in Kubernetes' etcd. Redis is only used as a throw-away cache and can be lost. When lost, it will be rebuilt without loss of service.
|
||||
|
||||
A set of HA manifests are provided for users who wish to run Argo CD in a highly available manner. This runs more containers, and runs Redis in HA mode.
|
||||
|
||||
[HA Manifests ⧉](https://github.com/argoproj/argo-cd/tree/master/manifests#high-availability)
|
||||
A set of [HA manifests](https://github.com/argoproj/argo-cd/tree/master/manifests/ha) are provided for users who wish to run Argo CD in a highly available manner. This runs more containers, and runs Redis in HA mode.
|
||||
|
||||
> **NOTE:** The HA installation will require at least three different nodes due to pod anti-affinity roles in the
|
||||
> specs. Additionally, IPv6 only clusters are not supported.
|
||||
@@ -17,11 +15,11 @@ A set of HA manifests are provided for users who wish to run Argo CD in a highly
|
||||
|
||||
The `argocd-repo-server` is responsible for cloning Git repository, keeping it up to date and generating manifests using the appropriate tool.
|
||||
|
||||
* `argocd-repo-server` fork/exec config management tool to generate manifests. The fork can fail due to lack of memory and limit on the number of OS threads.
|
||||
The `--parallelismlimit` flag controls how many manifests generations are running concurrently and allows avoiding OOM kills.
|
||||
* `argocd-repo-server` fork/exec config management tool to generate manifests. The fork can fail due to lack of memory or limit on the number of OS threads.
|
||||
The `--parallelismlimit` flag controls how many manifests generations are running concurrently and helps avoid OOM kills.
|
||||
|
||||
* the `argocd-repo-server` ensures that repository is in the clean state during the manifest generation using config management tools such as Kustomize, Helm
|
||||
or custom plugin. As a result Git repositories with multiple applications might be affect repository server performance.
|
||||
or custom plugin. As a result Git repositories with multiple applications might affect repository server performance.
|
||||
Read [Monorepo Scaling Considerations](#monorepo-scaling-considerations) for more information.
|
||||
|
||||
* `argocd-repo-server` clones repository into `/tmp` ( of path specified in `TMPDIR` env variable ). Pod might run out of disk space if have too many repository
|
||||
@@ -30,7 +28,7 @@ or repositories has a lot of files. To avoid this problem mount persistent volum
|
||||
* `argocd-repo-server` `git ls-remote` to resolve ambiguous revision such as `HEAD`, branch or tag name. This operation is happening pretty frequently
|
||||
and might fail. To avoid failed syncs use `ARGOCD_GIT_ATTEMPTS_COUNT` environment variable to retry failed requests.
|
||||
|
||||
* `argocd-repo-server` Every 3m (by default) Argo CD checks for changes to the app manifests. Argo CD assumes by default that manifests only change when the repo changes, so it caches generated manifests (for 24h by default). With Kustomize remote bases, or Helm patch releases, the manifests can change even though the repo has not changed. By reducing the cache time, you can get the changes without waiting for 24h. Use `--repo-cache-expiration duration`, and we'd suggest in low volume environments you try '1h'. Bear in mind this will negate the benefit of caching if set too low.
|
||||
* `argocd-repo-server` Every 3m (by default) Argo CD checks for changes to the app manifests. Argo CD assumes by default that manifests only change when the repo changes, so it caches the generated manifests (for 24h by default). With Kustomize remote bases, or Helm patch releases, the manifests can change even though the repo has not changed. By reducing the cache time, you can get the changes without waiting for 24h. Use `--repo-cache-expiration duration`, and we'd suggest in low volume environments you try '1h'. Bear in mind that this will negate the benefits of caching if set too low.
|
||||
|
||||
* `argocd-repo-server` fork exec config management tools such as `helm` or `kustomize` and enforces 90 seconds timeout. The timeout can be increased using `ARGOCD_EXEC_TIMEOUT` env variable. The value should be in Go time duration string format, for example, `2m30s`.
|
||||
|
||||
|
||||
@@ -1,5 +1,26 @@
|
||||
# v2.3 to 2.4
|
||||
|
||||
## Known Issues
|
||||
|
||||
### Broken `project` filter before 2.4.27
|
||||
|
||||
Argo CD 2.4.0 introduced a breaking API change, renaming the `project` filter to `projects`.
|
||||
|
||||
#### Impact to API clients
|
||||
|
||||
A similar issue applies to other API clients which communicate with the Argo CD API server via its REST API. If the
|
||||
client uses the `project` field to filter projects, the filter will not be applied. **The failing project filter could
|
||||
have detrimental consequences if, for example, you rely on it to list Applications to be deleted.**
|
||||
|
||||
#### Impact to CLI clients
|
||||
|
||||
CLI clients older that v2.4.0 rely on client-side filtering and are not impacted by this bug.
|
||||
|
||||
#### How to fix the problem
|
||||
|
||||
Upgrade to Argo CD >=2.4.27, >=2.5.15, or >=2.6.6. This version of Argo CD will accept both `project` and `projects` as
|
||||
valid filters.
|
||||
|
||||
## KSonnet support is removed
|
||||
|
||||
Ksonnet was deprecated in [2019](https://github.com/ksonnet/ksonnet/pull/914/files) and is no longer maintained.
|
||||
|
||||
@@ -1,5 +1,57 @@
|
||||
# v2.4 to 2.5
|
||||
|
||||
## Known Issues
|
||||
|
||||
### Broken `project` filter before 2.5.15
|
||||
|
||||
Argo CD 2.4.0 introduced a breaking API change, renaming the `project` filter to `projects`.
|
||||
|
||||
#### Impact to API clients
|
||||
|
||||
A similar issue applies to other API clients which communicate with the Argo CD API server via its REST API. If the
|
||||
client uses the `project` field to filter projects, the filter will not be applied. **The failing project filter could
|
||||
have detrimental consequences if, for example, you rely on it to list Applications to be deleted.**
|
||||
|
||||
#### Impact to CLI clients
|
||||
|
||||
CLI clients older that v2.4.0 rely on client-side filtering and are not impacted by this bug.
|
||||
|
||||
#### How to fix the problem
|
||||
|
||||
Upgrade to Argo CD >=2.4.27, >=2.5.15, or >=2.6.6. This version of Argo CD will accept both `project` and `projects` as
|
||||
valid filters.
|
||||
|
||||
### Broken matrix-nested git files generator in 2.5.14
|
||||
|
||||
Argo CD 2.5.14 introduced a bug in the matrix-nested git files generator. The bug only applies when the git files
|
||||
generator is the second generator nested under a matrix. For example:
|
||||
|
||||
```yaml
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: ApplicationSet
|
||||
metadata:
|
||||
name: guestbook
|
||||
spec:
|
||||
generators:
|
||||
- matrix:
|
||||
generators:
|
||||
- clusters: {}
|
||||
- git:
|
||||
repoURL: https://git.example.com/org/repo.git
|
||||
revision: HEAD
|
||||
files:
|
||||
- path: "defaults/*.yaml"
|
||||
template:
|
||||
# ...
|
||||
```
|
||||
|
||||
The nested git files generator will produce no parameters, causing the matrix generator to also produce no parameters.
|
||||
This will cause the ApplicationSet to produce no Applications. If the ApplicationSet controller is
|
||||
[configured with the ability to delete applications](https://argo-cd.readthedocs.io/en/latest/operator-manual/applicationset/Controlling-Resource-Modification/),
|
||||
it will delete all Applications which were previously created by the ApplicationSet.
|
||||
|
||||
To avoid this issue, upgrade directly to >=2.5.15 or >= 2.6.6.
|
||||
|
||||
## Configure RBAC to account for new `applicationsets` resource
|
||||
|
||||
2.5 introduces a new `applicationsets` [RBAC resource](https://argo-cd.readthedocs.io/en/stable/operator-manual/rbac/#rbac-resources-and-actions).
|
||||
@@ -132,3 +184,16 @@ This note is just for clarity. No action is required.
|
||||
|
||||
We [expected](../upgrading/2.3-2.4.md#enable-logs-rbac-enforcement) to enable logs RBAC enforcement by default in 2.5.
|
||||
We have decided not to do that in the 2.x series due to disruption for users of [Project Roles](../../user-guide/projects.md#project-roles).
|
||||
|
||||
## `argocd app create` for old CLI versions fails with API version >=2.5.16
|
||||
|
||||
Starting with Argo CD 2.5.16, the API returns `PermissionDenied` instead of `NotFound` for Application `GET` requests if
|
||||
the Application does not exist.
|
||||
|
||||
The Argo CD CLI before versions starting with version 2.5.0-rc1 and before versions 2.5.16 and 2.6.7 does a `GET`
|
||||
request before the `POST` request in `argocd app create`. The command does not gracefully handle the `PermissionDenied`
|
||||
response and will therefore fail to create/update the Application.
|
||||
|
||||
To solve the issue, upgrade the CLI to at least 2.5.16, or 2.6.7.
|
||||
|
||||
CLIs older than 2.5.0-rc1 are unaffected.
|
||||
|
||||
@@ -1,5 +1,57 @@
|
||||
# v2.5 to 2.6
|
||||
|
||||
## Known Issues
|
||||
|
||||
### Broken `project` filter before 2.6.6
|
||||
|
||||
Argo CD 2.4.0 introduced a breaking API change, renaming the `project` filter to `projects`.
|
||||
|
||||
#### Impact to API clients
|
||||
|
||||
A similar issue applies to other API clients which communicate with the Argo CD API server via its REST API. If the
|
||||
client uses the `project` field to filter projects, the filter will not be applied. **The failing project filter could
|
||||
have detrimental consequences if, for example, you rely on it to list Applications to be deleted.**
|
||||
|
||||
#### Impact to CLI clients
|
||||
|
||||
CLI clients older that v2.4.0 rely on client-side filtering and are not impacted by this bug.
|
||||
|
||||
#### How to fix the problem
|
||||
|
||||
Upgrade to Argo CD >=2.4.27, >=2.5.15, or >=2.6.6. This version of Argo CD will accept both `project` and `projects` as
|
||||
valid filters.
|
||||
|
||||
### Broken matrix-nested git files generator in 2.6.5
|
||||
|
||||
Argo CD 2.6.5 introduced a bug in the matrix-nested git files generator. The bug only applies when the git files
|
||||
generator is the second generator nested under a matrix. For example:
|
||||
|
||||
```yaml
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: ApplicationSet
|
||||
metadata:
|
||||
name: guestbook
|
||||
spec:
|
||||
generators:
|
||||
- matrix:
|
||||
generators:
|
||||
- clusters: {}
|
||||
- git:
|
||||
repoURL: https://git.example.com/org/repo.git
|
||||
revision: HEAD
|
||||
files:
|
||||
- path: "defaults/*.yaml"
|
||||
template:
|
||||
# ...
|
||||
```
|
||||
|
||||
The nested git files generator will produce no parameters, causing the matrix generator to also produce no parameters.
|
||||
This will cause the ApplicationSet to produce no Applications. If the ApplicationSet controller is
|
||||
[configured with the ability to delete applications](https://argo-cd.readthedocs.io/en/latest/operator-manual/applicationset/Controlling-Resource-Modification/),
|
||||
it will delete all Applications which were previously created by the ApplicationSet.
|
||||
|
||||
To avoid this issue, upgrade directly to >=2.5.15 or >= 2.6.6.
|
||||
|
||||
## ApplicationSets: `^` behavior change in Sprig's semver functions
|
||||
Argo CD 2.5 introduced [Go templating in ApplicationSets](https://argo-cd.readthedocs.io/en/stable/operator-manual/applicationset/GoTemplate/). Go templates have access to the Sprig function library.
|
||||
|
||||
@@ -29,3 +81,17 @@ name. Argo CD v2.6 introduces support for specifying sidecar plugins by name.
|
||||
|
||||
Removal of argocd-cm plugin support has been delayed until 2.7 to provide a transition time for users who need to
|
||||
specify plugins by name.
|
||||
|
||||
## `argocd app create` for old CLI versions fails with API version >=2.6.7
|
||||
|
||||
Starting with Argo CD 2.6.7, the API returns `PermissionDenied` instead of `NotFound` for Application `GET` requests if
|
||||
the Application does not exist.
|
||||
|
||||
The Argo CD CLI before versions starting with version 2.5.0-rc1 and before versions 2.5.16 and 2.6.7 does a `GET`
|
||||
request before the `POST` request in `argocd app create`. The command does not gracefully handle the `PermissionDenied`
|
||||
response and will therefore fail to create/update the Application.
|
||||
|
||||
To solve the issue, upgrade the CLI to at least 2.5.16, or 2.6.7.
|
||||
|
||||
CLIs older than 2.5.0-rc1 are unaffected.
|
||||
|
||||
|
||||
@@ -94,6 +94,7 @@ data:
|
||||
```
|
||||
|
||||
Make sure that:
|
||||
|
||||
- __issuer__ ends with the correct realm (in this example _master_)
|
||||
- __issuer__ on Keycloak releases older than version 17 the URL must include /auth (in this example /auth/realms/master)
|
||||
- __clientID__ is set to the Client ID you configured in Keycloak
|
||||
|
||||
@@ -13,4 +13,4 @@ Before effectively using Argo CD, it is necessary to understand the underlying t
|
||||
* [Helm](https://helm.sh)
|
||||
* If you're integrating with a CI tool:
|
||||
* [GitHub Actions Documentation](https://docs.github.com/en/actions)
|
||||
* [Jenkins User Guide](https://jenkins.io](https://www.jenkins.io/doc/book/)
|
||||
* [Jenkins User Guide](https://www.jenkins.io/doc/book/)
|
||||
|
||||
@@ -48,40 +48,4 @@ Within ApplicationSet there exist other more powerful generators in addition to
|
||||
|
||||
To learn more about the ApplicationSet controller, check out [ApplicationSet documentation](../operator-manual/applicationset/index.md) to install the ApplicationSet controller alongside Argo CD.
|
||||
|
||||
**Note:** Starting `v2.3` of Argo CD, we don't need to install ApplicationSet Controller separately. It would be instead as part of Argo CD installation.
|
||||
|
||||
#### Post Selector all generators
|
||||
|
||||
The Selector allows to post-filter based on generated values using the kubernetes common labelSelector format. In the example, the list generator generates a set of two application which then filter by the key value to only select the `env` with value `staging`:
|
||||
|
||||
```yaml
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: ApplicationSet
|
||||
metadata:
|
||||
name: guestbook
|
||||
spec:
|
||||
generators:
|
||||
- list:
|
||||
elements:
|
||||
- cluster: engineering-dev
|
||||
url: https://kubernetes.default.svc
|
||||
env: staging
|
||||
- cluster: engineering-prod
|
||||
url: https://kubernetes.default.svc
|
||||
env: prod
|
||||
selector:
|
||||
matchLabels:
|
||||
env: staging
|
||||
template:
|
||||
metadata:
|
||||
name: '{{cluster}}-guestbook'
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: https://github.com/argoproj-labs/applicationset.git
|
||||
targetRevision: HEAD
|
||||
path: examples/list-generator/guestbook/{{cluster}}
|
||||
destination:
|
||||
server: '{{url}}'
|
||||
namespace: guestbook
|
||||
```
|
||||
**Note:** Starting `v2.3` of Argo CD, we don't need to install ApplicationSet Controller separately. It would be instead as part of Argo CD installation.
|
||||
11
examples/k8s-rbac/argocd-server-applications/README.md
Normal file
11
examples/k8s-rbac/argocd-server-applications/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
This folder contains example RBAC for Kubernetes to allow the Argo CD API
|
||||
Server (`argocd-server`) to perform CRUD operations on `Application` CRs
|
||||
in all namespaces on the cluster.
|
||||
|
||||
Applying the `ClusterRole` and `ClusterRoleBinding` grant the Argo CD API
|
||||
server read and write permissions cluster-wide, which may not be what you
|
||||
want. Handle with care.
|
||||
|
||||
Only apply these if you have installed Argo CD into the default namespace
|
||||
`argocd`. Otherwise, you need to edit the cluster role binding to bind to
|
||||
the service account in the correct namespace.
|
||||
@@ -0,0 +1,18 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-server-cluster-apps
|
||||
app.kubernetes.io/part-of: argocd
|
||||
app.kubernetes.io/component: server
|
||||
name: argocd-server-cluster-apps
|
||||
rules:
|
||||
- apiGroups:
|
||||
- "argoproj.io"
|
||||
resources:
|
||||
- "applications"
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- update
|
||||
- patch
|
||||
@@ -0,0 +1,16 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-server-cluster-apps
|
||||
app.kubernetes.io/part-of: argocd
|
||||
app.kubernetes.io/component: server
|
||||
name: argocd-server-cluster-apps
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: argocd-server-cluster-apps
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: argocd-server
|
||||
namespace: argocd
|
||||
8
go.mod
8
go.mod
@@ -74,10 +74,10 @@ require (
|
||||
github.com/xanzy/go-gitlab v0.60.0
|
||||
github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
|
||||
golang.org/x/net v0.4.0 // indirect
|
||||
golang.org/x/net v0.7.0 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
|
||||
golang.org/x/term v0.3.0
|
||||
golang.org/x/term v0.5.0
|
||||
google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368
|
||||
google.golang.org/grpc v1.51.0
|
||||
google.golang.org/protobuf v1.28.1
|
||||
@@ -232,8 +232,8 @@ require (
|
||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
|
||||
golang.org/x/exp v0.0.0-20210901193431-a062eea981d2 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
golang.org/x/sys v0.3.0 // indirect
|
||||
golang.org/x/text v0.5.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
|
||||
golang.org/x/tools v0.1.12 // indirect
|
||||
gomodules.xyz/envconfig v1.3.1-0.20190308184047-426f31af0d45 // indirect
|
||||
|
||||
16
go.sum
16
go.sum
@@ -1340,8 +1340,8 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
|
||||
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -1487,13 +1487,13 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI=
|
||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -1504,8 +1504,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
||||
@@ -5,7 +5,7 @@ kind: Kustomization
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v2.6.4
|
||||
newTag: v2.6.7
|
||||
resources:
|
||||
- ./application-controller
|
||||
- ./dex
|
||||
|
||||
@@ -15557,7 +15557,7 @@ spec:
|
||||
key: applicationsetcontroller.enable.progressive.syncs
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -15821,7 +15821,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -15873,7 +15873,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -16080,7 +16080,7 @@ spec:
|
||||
key: application.namespaces
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -12,4 +12,4 @@ resources:
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v2.6.4
|
||||
newTag: v2.6.7
|
||||
|
||||
@@ -11,7 +11,7 @@ patchesStrategicMerge:
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v2.6.4
|
||||
newTag: v2.6.7
|
||||
resources:
|
||||
- ../../base/application-controller
|
||||
- ../../base/applicationset-controller
|
||||
|
||||
@@ -16758,7 +16758,7 @@ spec:
|
||||
key: applicationsetcontroller.enable.progressive.syncs
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -16868,7 +16868,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -16921,7 +16921,7 @@ spec:
|
||||
containers:
|
||||
- command:
|
||||
- argocd-notifications
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -17224,7 +17224,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -17276,7 +17276,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -17555,7 +17555,7 @@ spec:
|
||||
key: server.enable.proxy.extension
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -17791,7 +17791,7 @@ spec:
|
||||
key: application.namespaces
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -1562,7 +1562,7 @@ spec:
|
||||
key: applicationsetcontroller.enable.progressive.syncs
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -1672,7 +1672,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -1725,7 +1725,7 @@ spec:
|
||||
containers:
|
||||
- command:
|
||||
- argocd-notifications
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -2028,7 +2028,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -2080,7 +2080,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -2359,7 +2359,7 @@ spec:
|
||||
key: server.enable.proxy.extension
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -2595,7 +2595,7 @@ spec:
|
||||
key: application.namespaces
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -15877,7 +15877,7 @@ spec:
|
||||
key: applicationsetcontroller.enable.progressive.syncs
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -15987,7 +15987,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -16040,7 +16040,7 @@ spec:
|
||||
containers:
|
||||
- command:
|
||||
- argocd-notifications
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -16299,7 +16299,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -16351,7 +16351,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -16626,7 +16626,7 @@ spec:
|
||||
key: server.enable.proxy.extension
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -16860,7 +16860,7 @@ spec:
|
||||
key: application.namespaces
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -681,7 +681,7 @@ spec:
|
||||
key: applicationsetcontroller.enable.progressive.syncs
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -791,7 +791,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -844,7 +844,7 @@ spec:
|
||||
containers:
|
||||
- command:
|
||||
- argocd-notifications
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -1103,7 +1103,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -1155,7 +1155,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -1430,7 +1430,7 @@ spec:
|
||||
key: server.enable.proxy.extension
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -1664,7 +1664,7 @@ spec:
|
||||
key: application.namespaces
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.6.4
|
||||
image: quay.io/argoproj/argocd:v2.6.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -98,6 +98,7 @@ nav:
|
||||
- operator-manual/applicationset/Generators-SCM-Provider.md
|
||||
- operator-manual/applicationset/Generators-Cluster-Decision-Resource.md
|
||||
- operator-manual/applicationset/Generators-Pull-Request.md
|
||||
- operator-manual/applicationset/Generators-Post-Selector.md
|
||||
- Template fields:
|
||||
- operator-manual/applicationset/Template.md
|
||||
- operator-manual/applicationset/GoTemplate.md
|
||||
|
||||
@@ -51,7 +51,9 @@ type ApplicationQuery struct {
|
||||
// the repoURL to restrict returned list applications
|
||||
Repo *string `protobuf:"bytes,6,opt,name=repo" json:"repo,omitempty"`
|
||||
// the application's namespace
|
||||
AppNamespace *string `protobuf:"bytes,7,opt,name=appNamespace" json:"appNamespace,omitempty"`
|
||||
AppNamespace *string `protobuf:"bytes,7,opt,name=appNamespace" json:"appNamespace,omitempty"`
|
||||
// the project names to restrict returned list applications (legacy name for backwards-compatibility)
|
||||
Project []string `protobuf:"bytes,8,rep,name=project" json:"project,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
@@ -139,6 +141,13 @@ func (m *ApplicationQuery) GetAppNamespace() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *ApplicationQuery) GetProject() []string {
|
||||
if m != nil {
|
||||
return m.Project
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type NodeQuery struct {
|
||||
// the application's name
|
||||
Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
||||
@@ -2627,169 +2636,169 @@ func init() {
|
||||
}
|
||||
|
||||
var fileDescriptor_df6e82b174b5eaec = []byte{
|
||||
// 2581 bytes of a gzipped FileDescriptorProto
|
||||
// 2590 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x5a, 0xcd, 0x8f, 0x1c, 0x47,
|
||||
0x15, 0xa7, 0x66, 0xbf, 0x66, 0xde, 0xac, 0xbf, 0x2a, 0xf1, 0xd2, 0x69, 0xaf, 0xcd, 0xba, 0xfd,
|
||||
0xb5, 0x5e, 0x7b, 0x67, 0xec, 0xc1, 0x20, 0x67, 0x13, 0x04, 0xb6, 0xe3, 0x2f, 0x58, 0x3b, 0xa6,
|
||||
0xd7, 0xc6, 0x28, 0x1c, 0xa0, 0xd2, 0x53, 0x3b, 0xdb, 0x6c, 0x4f, 0x77, 0xbb, 0xbb, 0x67, 0xac,
|
||||
0x91, 0xf1, 0x25, 0x88, 0x13, 0x51, 0x90, 0x92, 0x1c, 0x50, 0x14, 0x21, 0x94, 0x28, 0x17, 0x2e,
|
||||
0xdc, 0x10, 0x12, 0x17, 0xb8, 0x20, 0x90, 0x38, 0x20, 0x3e, 0x2e, 0x39, 0x21, 0x8b, 0x1b, 0x17,
|
||||
0xfe, 0x04, 0x54, 0xd5, 0x55, 0xdd, 0xd5, 0x33, 0x3d, 0x3d, 0xbd, 0xec, 0x46, 0xf1, 0xad, 0x5e,
|
||||
0x4d, 0xd5, 0x7b, 0xbf, 0x7a, 0xf5, 0xbe, 0xea, 0xf5, 0xc0, 0xc9, 0x90, 0x06, 0x7d, 0x1a, 0x34,
|
||||
0x89, 0xef, 0x3b, 0xb6, 0x45, 0x22, 0xdb, 0x73, 0xd5, 0x71, 0xc3, 0x0f, 0xbc, 0xc8, 0xc3, 0x75,
|
||||
0x65, 0x4a, 0x5f, 0xec, 0x78, 0x5e, 0xc7, 0xa1, 0x4d, 0xe2, 0xdb, 0x4d, 0xe2, 0xba, 0x5e, 0xc4,
|
||||
0xa7, 0xc3, 0x78, 0xa9, 0x6e, 0x6c, 0x5f, 0x0e, 0x1b, 0xb6, 0xc7, 0x7f, 0xb5, 0xbc, 0x80, 0x36,
|
||||
0xfb, 0x17, 0x9b, 0x1d, 0xea, 0xd2, 0x80, 0x44, 0xb4, 0x2d, 0xd6, 0x5c, 0x4a, 0xd7, 0x74, 0x89,
|
||||
0xb5, 0x65, 0xbb, 0x34, 0x18, 0x34, 0xfd, 0xed, 0x0e, 0x9b, 0x08, 0x9b, 0x5d, 0x1a, 0x91, 0xbc,
|
||||
0x5d, 0xeb, 0x1d, 0x3b, 0xda, 0xea, 0xbd, 0xd9, 0xb0, 0xbc, 0x6e, 0x93, 0x04, 0x1d, 0xcf, 0x0f,
|
||||
0xbc, 0x1f, 0xf2, 0xc1, 0xaa, 0xd5, 0x6e, 0xf6, 0x5b, 0x29, 0x03, 0xf5, 0x2c, 0xfd, 0x8b, 0xc4,
|
||||
0xf1, 0xb7, 0xc8, 0x28, 0xb7, 0xeb, 0x13, 0xb8, 0x05, 0xd4, 0xf7, 0x84, 0x6e, 0xf8, 0xd0, 0x8e,
|
||||
0xbc, 0x60, 0xa0, 0x0c, 0x63, 0x36, 0xc6, 0xa7, 0x08, 0x0e, 0x5e, 0x49, 0xe5, 0x7d, 0xbb, 0x47,
|
||||
0x83, 0x01, 0xc6, 0x30, 0xed, 0x92, 0x2e, 0xd5, 0xd0, 0x12, 0x5a, 0xae, 0x99, 0x7c, 0x8c, 0x35,
|
||||
0x98, 0x0b, 0xe8, 0x66, 0x40, 0xc3, 0x2d, 0xad, 0xc2, 0xa7, 0x25, 0x89, 0x75, 0xa8, 0x32, 0xe1,
|
||||
0xd4, 0x8a, 0x42, 0x6d, 0x6a, 0x69, 0x6a, 0xb9, 0x66, 0x26, 0x34, 0x5e, 0x86, 0x03, 0x01, 0x0d,
|
||||
0xbd, 0x5e, 0x60, 0xd1, 0xef, 0xd0, 0x20, 0xb4, 0x3d, 0x57, 0x9b, 0xe6, 0xbb, 0x87, 0xa7, 0x19,
|
||||
0x97, 0x90, 0x3a, 0xd4, 0x8a, 0xbc, 0x40, 0x9b, 0xe1, 0x4b, 0x12, 0x9a, 0xe1, 0x61, 0xc0, 0xb5,
|
||||
0xd9, 0x18, 0x0f, 0x1b, 0x63, 0x03, 0xe6, 0x89, 0xef, 0xdf, 0x25, 0x5d, 0x1a, 0xfa, 0xc4, 0xa2,
|
||||
0xda, 0x1c, 0xff, 0x2d, 0x33, 0x67, 0x5c, 0x83, 0xda, 0x5d, 0xaf, 0x4d, 0xc7, 0x1f, 0x6a, 0x98,
|
||||
0x49, 0x25, 0x87, 0xc9, 0x36, 0x1c, 0x36, 0x69, 0xdf, 0x66, 0x20, 0xef, 0xd0, 0x88, 0xb4, 0x49,
|
||||
0x44, 0x86, 0x19, 0x56, 0x12, 0x86, 0x3a, 0x54, 0x03, 0xb1, 0x58, 0xab, 0xf0, 0xf9, 0x84, 0x1e,
|
||||
0x11, 0x36, 0x95, 0x23, 0xec, 0x2f, 0x08, 0x8e, 0x29, 0xd7, 0x61, 0x0a, 0x25, 0x5d, 0xef, 0x53,
|
||||
0x37, 0x0a, 0xc7, 0x8b, 0x3d, 0x0f, 0x87, 0xa4, 0x3e, 0x87, 0x0f, 0x33, 0xfa, 0x03, 0x03, 0xa2,
|
||||
0x4e, 0x4a, 0x20, 0xea, 0x1c, 0x5e, 0x82, 0xba, 0xa4, 0x1f, 0xdc, 0x7e, 0x4d, 0x5c, 0x9a, 0x3a,
|
||||
0x35, 0x72, 0x9c, 0x99, 0x9c, 0xe3, 0xb8, 0xa0, 0x29, 0xa7, 0xb9, 0x43, 0x5c, 0x7b, 0x93, 0x86,
|
||||
0x51, 0x59, 0xf5, 0xa1, 0x1d, 0xab, 0xef, 0x38, 0xd4, 0x6e, 0xd8, 0x0e, 0xbd, 0xb6, 0xd5, 0x73,
|
||||
0xb7, 0xf1, 0x8b, 0x30, 0x63, 0xb1, 0x01, 0x97, 0x30, 0x6f, 0xc6, 0x84, 0xf1, 0x18, 0x8e, 0x8f,
|
||||
0x83, 0xf4, 0xd0, 0x8e, 0xb6, 0xd8, 0xf6, 0x70, 0x1c, 0x36, 0x6b, 0x8b, 0x5a, 0xdb, 0x61, 0xaf,
|
||||
0x2b, 0xaf, 0x56, 0xd2, 0xa5, 0xb0, 0xfd, 0x0a, 0xc1, 0xf2, 0x44, 0xc9, 0x0f, 0x03, 0xe2, 0xfb,
|
||||
0x34, 0xc0, 0x37, 0x60, 0xe6, 0x11, 0xfb, 0x81, 0x5b, 0x6b, 0xbd, 0xd5, 0x68, 0xa8, 0x31, 0x6d,
|
||||
0x22, 0x97, 0x5b, 0x5f, 0x30, 0xe3, 0xed, 0xb8, 0x21, 0x75, 0x50, 0xe1, 0x7c, 0x16, 0x32, 0x7c,
|
||||
0x12, 0x55, 0xb1, 0xf5, 0x7c, 0xd9, 0xd5, 0x59, 0x98, 0xf6, 0x49, 0x10, 0x19, 0x87, 0xe1, 0x85,
|
||||
0xac, 0x19, 0xfa, 0x9e, 0x1b, 0x52, 0xe3, 0x77, 0x28, 0x73, 0xa1, 0xd7, 0x02, 0x4a, 0x22, 0x6a,
|
||||
0xd2, 0x47, 0x3d, 0x1a, 0x46, 0x78, 0x1b, 0xd4, 0x30, 0xcb, 0x75, 0x57, 0x6f, 0xdd, 0x6e, 0xa4,
|
||||
0x71, 0xaa, 0x21, 0xe3, 0x14, 0x1f, 0x7c, 0xdf, 0x6a, 0x37, 0xfa, 0xad, 0x86, 0xbf, 0xdd, 0x69,
|
||||
0xb0, 0xa8, 0x97, 0x41, 0x26, 0xa3, 0x9e, 0x7a, 0x54, 0x53, 0xe5, 0x8e, 0x17, 0x60, 0xb6, 0xe7,
|
||||
0x87, 0x34, 0x88, 0xf8, 0xc9, 0xaa, 0xa6, 0xa0, 0xd8, 0x2d, 0xf5, 0x89, 0x63, 0xb7, 0x49, 0x14,
|
||||
0xdf, 0x42, 0xd5, 0x4c, 0x68, 0xe3, 0xe3, 0x2c, 0xfa, 0x07, 0x7e, 0xfb, 0xf3, 0x42, 0xaf, 0xa2,
|
||||
0xac, 0x0c, 0xa1, 0xfc, 0x20, 0x8b, 0xf2, 0x35, 0xea, 0xd0, 0x14, 0x65, 0x9e, 0x61, 0x6a, 0x30,
|
||||
0x67, 0x91, 0xd0, 0x22, 0x6d, 0xc9, 0x4b, 0x92, 0x2c, 0x2c, 0xf8, 0x81, 0xe7, 0x93, 0x0e, 0xe7,
|
||||
0x74, 0xcf, 0x73, 0x6c, 0x6b, 0x20, 0x6c, 0x73, 0xf4, 0x87, 0x11, 0x23, 0x9e, 0xce, 0x31, 0xe2,
|
||||
0x13, 0x50, 0xdf, 0x18, 0xb8, 0xd6, 0xeb, 0x3e, 0x4f, 0x99, 0xcc, 0xc5, 0xec, 0x88, 0x76, 0x43,
|
||||
0x0d, 0xf1, 0xb8, 0x1f, 0x13, 0xc6, 0x87, 0x33, 0xb0, 0xa0, 0x9c, 0x80, 0x6d, 0x28, 0xc2, 0x5f,
|
||||
0xe4, 0xf4, 0x0b, 0x30, 0xdb, 0x0e, 0x06, 0x66, 0xcf, 0x15, 0x97, 0x29, 0x28, 0x26, 0xd8, 0x0f,
|
||||
0x7a, 0x6e, 0x0c, 0xb2, 0x6a, 0xc6, 0x04, 0xde, 0x84, 0x6a, 0x18, 0xb1, 0x24, 0xd9, 0x19, 0xf0,
|
||||
0x70, 0x54, 0x6f, 0x7d, 0x73, 0x77, 0x17, 0xc8, 0xa0, 0x6f, 0x08, 0x8e, 0x66, 0xc2, 0x1b, 0x3f,
|
||||
0x82, 0x9a, 0x8c, 0x84, 0xa1, 0x36, 0xb7, 0x34, 0xb5, 0x5c, 0x6f, 0x6d, 0xec, 0x5e, 0xd0, 0xeb,
|
||||
0x3e, 0x4b, 0xf0, 0x4a, 0xd4, 0x37, 0x53, 0x29, 0x78, 0x11, 0x6a, 0x5d, 0xe1, 0xeb, 0xa1, 0x56,
|
||||
0xe5, 0xda, 0x4e, 0x27, 0xf0, 0x77, 0x61, 0xc6, 0x76, 0x37, 0xbd, 0x50, 0xab, 0x71, 0x30, 0x57,
|
||||
0x77, 0x07, 0xe6, 0xb6, 0xbb, 0xe9, 0x99, 0x31, 0x43, 0xfc, 0x08, 0xf6, 0x05, 0x34, 0x0a, 0x06,
|
||||
0x52, 0x0b, 0x1a, 0x70, 0xbd, 0x7e, 0x6b, 0x77, 0x12, 0x4c, 0x95, 0xa5, 0x99, 0x95, 0x80, 0xd7,
|
||||
0xa0, 0x1e, 0xa6, 0x36, 0xa6, 0xd5, 0xb9, 0x40, 0x2d, 0xc3, 0x48, 0xb1, 0x41, 0x53, 0x5d, 0x3c,
|
||||
0x62, 0xc3, 0xf3, 0x39, 0x36, 0xfc, 0x4f, 0x04, 0x8b, 0x23, 0x61, 0x60, 0xc3, 0xa7, 0x85, 0x46,
|
||||
0x4a, 0x60, 0x3a, 0xf4, 0xa9, 0xc5, 0x23, 0x7f, 0xbd, 0x75, 0x67, 0xcf, 0xe2, 0x02, 0x97, 0xcb,
|
||||
0x59, 0x17, 0x85, 0xae, 0x52, 0xbe, 0xf9, 0x13, 0x04, 0x5f, 0x54, 0x38, 0xdf, 0x23, 0x91, 0xb5,
|
||||
0x55, 0x74, 0x24, 0xe6, 0x43, 0x6c, 0x8d, 0xc8, 0x66, 0x31, 0xc1, 0x0c, 0x8d, 0x0f, 0xee, 0x0f,
|
||||
0x7c, 0x06, 0x83, 0xfd, 0x92, 0x4e, 0x94, 0x4a, 0xfa, 0xef, 0x22, 0xd0, 0xd5, 0xc8, 0xe7, 0x39,
|
||||
0xce, 0x9b, 0xc4, 0xda, 0x2e, 0x82, 0xb2, 0x1f, 0x2a, 0x76, 0x9b, 0xe3, 0x98, 0x32, 0x2b, 0x76,
|
||||
0x7b, 0x87, 0x6e, 0x3f, 0x0c, 0x6a, 0x36, 0x07, 0xd4, 0xa7, 0x43, 0xa0, 0xa4, 0x8b, 0x15, 0x80,
|
||||
0x5a, 0x84, 0x9a, 0x3b, 0x54, 0x4c, 0xa5, 0x13, 0x39, 0x45, 0x54, 0x65, 0xa4, 0x88, 0xd2, 0x60,
|
||||
0xae, 0x9f, 0x54, 0xbd, 0xec, 0x67, 0x49, 0xb2, 0x83, 0x74, 0x02, 0xaf, 0xe7, 0x0b, 0x05, 0xc6,
|
||||
0x04, 0x43, 0xb1, 0x6d, 0xbb, 0x6d, 0x6d, 0x36, 0x46, 0xc1, 0xc6, 0xa5, 0xea, 0xdc, 0xf7, 0x2a,
|
||||
0xf0, 0xa5, 0x9c, 0xc3, 0x4d, 0xb4, 0x80, 0xe7, 0xe3, 0x84, 0x89, 0x1d, 0xce, 0x8d, 0xb5, 0xc3,
|
||||
0xea, 0x24, 0x3b, 0xac, 0xe5, 0x68, 0xe5, 0x9d, 0x0a, 0x2c, 0xe5, 0x68, 0x65, 0x72, 0x42, 0x7d,
|
||||
0x6e, 0xd4, 0xb2, 0xe9, 0x05, 0xe2, 0xc6, 0xab, 0x66, 0x4c, 0x30, 0xcf, 0xf0, 0x02, 0x7f, 0x8b,
|
||||
0xb8, 0x5a, 0x35, 0xf6, 0x8c, 0x98, 0x2a, 0xa5, 0x90, 0xff, 0x22, 0xd0, 0xa4, 0x16, 0xae, 0x58,
|
||||
0x5c, 0x27, 0x3d, 0xf7, 0xf9, 0x57, 0xc4, 0x02, 0xcc, 0x12, 0x8e, 0x56, 0x18, 0x88, 0xa0, 0x46,
|
||||
0x8e, 0x5c, 0xcd, 0x8f, 0x89, 0x47, 0xb2, 0x47, 0x0e, 0xd7, 0xed, 0x30, 0x92, 0x05, 0x2d, 0xde,
|
||||
0x84, 0xb9, 0x98, 0x5b, 0x5c, 0xc2, 0xd4, 0x5b, 0xeb, 0xbb, 0x4d, 0x6c, 0x19, 0xf5, 0x4a, 0xe6,
|
||||
0xc6, 0xcb, 0x70, 0x24, 0x37, 0xfa, 0x08, 0x18, 0x3a, 0x54, 0x65, 0x32, 0x17, 0x17, 0x90, 0xd0,
|
||||
0xc6, 0x7f, 0xa6, 0xb2, 0x61, 0xdd, 0x6b, 0xaf, 0x7b, 0x9d, 0x82, 0xb7, 0x60, 0xf1, 0xa5, 0x69,
|
||||
0x30, 0xe7, 0x7b, 0x6d, 0xe5, 0xd9, 0x27, 0x49, 0xb6, 0xcf, 0xf2, 0xdc, 0x88, 0xd8, 0x2e, 0x0d,
|
||||
0x44, 0x7e, 0x49, 0x27, 0x98, 0xb2, 0x43, 0xdb, 0xb5, 0xe8, 0x06, 0xb5, 0x3c, 0xb7, 0x1d, 0xf2,
|
||||
0x5b, 0x9b, 0x32, 0x33, 0x73, 0xf8, 0x16, 0xd4, 0x38, 0x7d, 0xdf, 0xee, 0xc6, 0x41, 0xb8, 0xde,
|
||||
0x5a, 0x69, 0xc4, 0xad, 0x92, 0x86, 0xda, 0x2a, 0x49, 0x75, 0xd8, 0xa5, 0x11, 0x69, 0xf4, 0x2f,
|
||||
0x36, 0xd8, 0x0e, 0x33, 0xdd, 0xcc, 0xb0, 0x44, 0xc4, 0x76, 0xd6, 0x6d, 0x97, 0x17, 0x58, 0x4c,
|
||||
0x54, 0x3a, 0xc1, 0x0c, 0x62, 0xd3, 0x73, 0x1c, 0xef, 0xb1, 0xf4, 0x81, 0x98, 0x62, 0xbb, 0x7a,
|
||||
0x6e, 0x64, 0x3b, 0x5c, 0x7e, 0xec, 0x00, 0xe9, 0x04, 0xdf, 0x65, 0x3b, 0x11, 0x0d, 0x78, 0x09,
|
||||
0x53, 0x33, 0x05, 0x95, 0x98, 0x5c, 0x3d, 0xee, 0x0b, 0x48, 0xdf, 0x8b, 0x8d, 0x73, 0x5e, 0x35,
|
||||
0xce, 0x61, 0x83, 0xdf, 0x97, 0xf3, 0x6e, 0xe6, 0xcd, 0x10, 0xda, 0xb7, 0xbd, 0x5e, 0xa8, 0xed,
|
||||
0x8f, 0x93, 0xb8, 0xa4, 0x47, 0x0c, 0xf6, 0x40, 0x8e, 0xc1, 0xfe, 0x1e, 0x41, 0x75, 0xdd, 0xeb,
|
||||
0x5c, 0x77, 0xa3, 0x60, 0xc0, 0x2b, 0x7b, 0xcf, 0x8d, 0xa8, 0x2b, 0xad, 0x42, 0x92, 0x4c, 0xd5,
|
||||
0x91, 0xdd, 0xa5, 0x1b, 0x11, 0xe9, 0xfa, 0xa2, 0x26, 0xd9, 0x91, 0xaa, 0x93, 0xcd, 0xec, 0xf8,
|
||||
0x0e, 0x09, 0x23, 0xee, 0xbd, 0x55, 0x93, 0x8f, 0x19, 0xd0, 0x64, 0xc1, 0x46, 0x14, 0x08, 0xd7,
|
||||
0xcd, 0xcc, 0xa9, 0x86, 0x34, 0x13, 0x63, 0x13, 0xa4, 0xb1, 0x01, 0x2f, 0x25, 0xa5, 0xec, 0x7d,
|
||||
0x1a, 0x74, 0x6d, 0x97, 0x14, 0xc7, 0xdb, 0x32, 0x5d, 0x98, 0x07, 0x19, 0x07, 0x62, 0xf5, 0xdf,
|
||||
0x43, 0xdb, 0x6d, 0x7b, 0x8f, 0x0b, 0x1c, 0xa1, 0x0c, 0xdb, 0xbf, 0x65, 0xfb, 0x2d, 0x0a, 0xdf,
|
||||
0xc4, 0x37, 0x6f, 0xc1, 0x3e, 0xe6, 0xc5, 0x7d, 0x2a, 0x7e, 0x10, 0x81, 0xc2, 0x18, 0xf7, 0x24,
|
||||
0x4f, 0x79, 0x98, 0xd9, 0x8d, 0x78, 0x1d, 0x0e, 0x90, 0x30, 0xb4, 0x3b, 0x2e, 0x6d, 0x4b, 0x5e,
|
||||
0x95, 0xd2, 0xbc, 0x86, 0xb7, 0xc6, 0xcf, 0x3e, 0xbe, 0x42, 0xdc, 0x9d, 0x24, 0x8d, 0x1f, 0x23,
|
||||
0x38, 0x9c, 0xcb, 0x24, 0xb1, 0x75, 0xa4, 0x84, 0x57, 0x1d, 0xaa, 0xa1, 0xb5, 0x45, 0xdb, 0x3d,
|
||||
0x87, 0xca, 0xbe, 0x86, 0xa4, 0xd9, 0x6f, 0xed, 0x5e, 0x7c, 0x93, 0x22, 0xbc, 0x27, 0x34, 0x3e,
|
||||
0x06, 0xd0, 0x25, 0x6e, 0x8f, 0x38, 0x1c, 0xc2, 0x34, 0x87, 0xa0, 0xcc, 0x18, 0x8b, 0xa0, 0xe7,
|
||||
0x99, 0x81, 0xe8, 0x24, 0xfc, 0x03, 0xc1, 0x7e, 0x19, 0x06, 0xc5, 0x1d, 0x2e, 0xc3, 0x01, 0x45,
|
||||
0x0d, 0x77, 0xd3, 0xeb, 0x1c, 0x9e, 0x9e, 0x10, 0xe2, 0xa4, 0x2d, 0x4c, 0x65, 0xbb, 0x97, 0xfd,
|
||||
0x4c, 0xff, 0xb1, 0x74, 0x1e, 0x42, 0x3b, 0xaa, 0xc4, 0x7e, 0x04, 0xda, 0x1d, 0xe2, 0x92, 0x0e,
|
||||
0x6d, 0x27, 0x87, 0x4b, 0x0c, 0xe9, 0x07, 0xea, 0x63, 0x79, 0xd7, 0x4f, 0xd3, 0xa4, 0x9c, 0xb1,
|
||||
0x37, 0x37, 0xe5, 0xc3, 0x3b, 0x80, 0xea, 0xba, 0xed, 0x6e, 0xb3, 0xf7, 0x1b, 0x3b, 0x57, 0x64,
|
||||
0x47, 0x8e, 0xd4, 0x61, 0x4c, 0xe0, 0x83, 0x30, 0xd5, 0x0b, 0x1c, 0x71, 0xcf, 0x6c, 0x88, 0x97,
|
||||
0xa0, 0xde, 0xa6, 0xa1, 0x15, 0xd8, 0xbe, 0xb8, 0x65, 0xde, 0xe8, 0x53, 0xa6, 0x98, 0xb6, 0x6d,
|
||||
0xcb, 0x73, 0xaf, 0x39, 0x24, 0x0c, 0x65, 0x62, 0x48, 0x26, 0x8c, 0x57, 0x61, 0x1f, 0x93, 0x99,
|
||||
0x1e, 0xf3, 0x5c, 0xf6, 0x98, 0x87, 0x33, 0xf0, 0x25, 0x3c, 0x89, 0xf8, 0x26, 0xbc, 0xc0, 0xf2,
|
||||
0xf1, 0x15, 0xdf, 0x17, 0x4c, 0x4a, 0x16, 0x23, 0x53, 0x43, 0x97, 0xde, 0xfa, 0xa9, 0x01, 0x58,
|
||||
0xb5, 0x79, 0x1a, 0xf4, 0x6d, 0x8b, 0xe2, 0x77, 0x11, 0x4c, 0x33, 0x01, 0xf8, 0xe8, 0x38, 0x17,
|
||||
0xe3, 0xb6, 0xa7, 0xef, 0xdd, 0x83, 0x8e, 0x49, 0x33, 0x16, 0xdf, 0xfa, 0xfb, 0xbf, 0xdf, 0xab,
|
||||
0x2c, 0xe0, 0x17, 0xf9, 0x67, 0x84, 0xfe, 0x45, 0xb5, 0xa5, 0x1f, 0xe2, 0xb7, 0x11, 0x60, 0x51,
|
||||
0x85, 0x28, 0xdd, 0x5d, 0x7c, 0x6e, 0x1c, 0xc4, 0x9c, 0x2e, 0xb0, 0x7e, 0x54, 0x89, 0xf6, 0x0d,
|
||||
0xcb, 0x0b, 0x28, 0x8b, 0xed, 0x7c, 0x01, 0x07, 0xb0, 0xc2, 0x01, 0x9c, 0xc4, 0x46, 0x1e, 0x80,
|
||||
0xe6, 0x13, 0xa6, 0xb7, 0xa7, 0x4d, 0x1a, 0xcb, 0xfd, 0x08, 0xc1, 0xcc, 0x43, 0x5e, 0x73, 0x4f,
|
||||
0x50, 0xd2, 0xc6, 0x9e, 0x29, 0x89, 0x8b, 0xe3, 0x68, 0x8d, 0x13, 0x1c, 0xe9, 0x51, 0x7c, 0x44,
|
||||
0x22, 0x0d, 0xa3, 0x80, 0x92, 0x6e, 0x06, 0xf0, 0x05, 0x84, 0x3f, 0x41, 0x30, 0x1b, 0xb7, 0x1b,
|
||||
0xf1, 0xa9, 0x71, 0x28, 0x33, 0xed, 0x48, 0x7d, 0xef, 0x7a, 0x77, 0xc6, 0x59, 0x8e, 0xf1, 0x84,
|
||||
0x91, 0x7b, 0x9d, 0x6b, 0x99, 0xce, 0xde, 0xfb, 0x08, 0xa6, 0x6e, 0xd2, 0x89, 0xf6, 0xb6, 0x87,
|
||||
0xe0, 0x46, 0x14, 0x98, 0x73, 0xd5, 0xf8, 0x63, 0x04, 0x2f, 0xdd, 0xa4, 0x51, 0x7e, 0xaa, 0xc3,
|
||||
0xcb, 0x93, 0xf3, 0x8f, 0x30, 0xbb, 0x73, 0x25, 0x56, 0x26, 0x31, 0xbe, 0xc9, 0x91, 0x9d, 0xc5,
|
||||
0x67, 0x8a, 0x8c, 0x30, 0x1c, 0xb8, 0xd6, 0x63, 0x81, 0xe3, 0xcf, 0x08, 0x0e, 0x0e, 0x7f, 0x6b,
|
||||
0xc1, 0xd9, 0xe4, 0x98, 0xfb, 0x29, 0x46, 0xbf, 0xbb, 0xdb, 0x58, 0x9a, 0x65, 0x6a, 0x5c, 0xe1,
|
||||
0xc8, 0x5f, 0xc1, 0x2f, 0x17, 0x21, 0x97, 0x4d, 0xca, 0xb0, 0xf9, 0x44, 0x0e, 0x9f, 0xf2, 0x8f,
|
||||
0x7f, 0x1c, 0xf6, 0x5b, 0x08, 0xe6, 0x6f, 0xd2, 0xe8, 0x4e, 0xd2, 0xa3, 0x3b, 0x55, 0xaa, 0x87,
|
||||
0xaf, 0x2f, 0x36, 0x94, 0x6f, 0x74, 0xf2, 0xa7, 0x44, 0xa5, 0xab, 0x1c, 0xd8, 0x19, 0x7c, 0xaa,
|
||||
0x08, 0x58, 0xda, 0x17, 0xfc, 0x08, 0xc1, 0x61, 0x15, 0x44, 0xfa, 0x85, 0xe3, 0x2b, 0x3b, 0xfb,
|
||||
0xa2, 0x20, 0xbe, 0x4b, 0x4c, 0x40, 0xd7, 0xe2, 0xe8, 0xce, 0x1b, 0xf9, 0x17, 0xde, 0x1d, 0x41,
|
||||
0xb1, 0x86, 0x56, 0x96, 0x11, 0xfe, 0x03, 0x82, 0xd9, 0xb8, 0x09, 0x37, 0x5e, 0x47, 0x99, 0x5e,
|
||||
0xfd, 0x5e, 0x7a, 0xcf, 0x75, 0x0e, 0xf9, 0xeb, 0xfa, 0x85, 0x7c, 0x85, 0xaa, 0xfb, 0xe5, 0xd5,
|
||||
0x36, 0xb8, 0x96, 0xb3, 0x6e, 0xff, 0x1b, 0x04, 0x90, 0x36, 0x12, 0xf1, 0xd9, 0xe2, 0x73, 0x28,
|
||||
0xcd, 0x46, 0x7d, 0x6f, 0x5b, 0x89, 0x46, 0x83, 0x9f, 0x67, 0x59, 0x5f, 0x2a, 0xf4, 0x39, 0x9f,
|
||||
0x5a, 0x6b, 0x71, 0xd3, 0xf1, 0x97, 0x08, 0x66, 0x78, 0x9f, 0x08, 0x9f, 0x1c, 0x87, 0x59, 0x6d,
|
||||
0x23, 0xed, 0xa5, 0xea, 0x4f, 0x73, 0xa8, 0x4b, 0xad, 0xa2, 0xc0, 0xb5, 0x86, 0x56, 0x70, 0x1f,
|
||||
0x66, 0xe3, 0x9e, 0xcd, 0x78, 0xf3, 0xc8, 0xf4, 0x74, 0xf4, 0xa5, 0x82, 0x44, 0x1a, 0x1b, 0xaa,
|
||||
0x88, 0x99, 0x2b, 0x93, 0x62, 0xe6, 0x34, 0x0b, 0x6b, 0xf8, 0x44, 0x51, 0xd0, 0xfb, 0x0c, 0x14,
|
||||
0x73, 0x8e, 0xa3, 0x3b, 0x65, 0x2c, 0x4d, 0x8a, 0x9b, 0x4c, 0x3b, 0x3f, 0x47, 0x70, 0x70, 0xb8,
|
||||
0xe4, 0xc4, 0x47, 0x86, 0x62, 0xa6, 0x5a, 0x67, 0xeb, 0x59, 0x2d, 0x8e, 0x2b, 0x57, 0x8d, 0x6f,
|
||||
0x70, 0x14, 0x6b, 0xf8, 0xf2, 0x44, 0xcf, 0xb8, 0x2b, 0xa3, 0x0e, 0x63, 0xb4, 0x9a, 0x7e, 0xb3,
|
||||
0xf8, 0x2d, 0x82, 0x79, 0xc9, 0xf7, 0x7e, 0x40, 0x69, 0x31, 0xac, 0xbd, 0x73, 0x04, 0x26, 0xcb,
|
||||
0x78, 0x95, 0xc3, 0xff, 0x2a, 0xbe, 0x54, 0x12, 0xbe, 0x84, 0xbd, 0x1a, 0x31, 0xa4, 0x7f, 0x44,
|
||||
0x70, 0xe8, 0x61, 0x6c, 0xf7, 0x9f, 0x13, 0xfe, 0x6b, 0x1c, 0xff, 0xd7, 0xf0, 0x2b, 0x05, 0x75,
|
||||
0xd1, 0xa4, 0x63, 0x5c, 0x40, 0xf8, 0xd7, 0x08, 0xaa, 0xb2, 0x03, 0x8f, 0xcf, 0x8c, 0x75, 0x8c,
|
||||
0x6c, 0x8f, 0x7e, 0x2f, 0x8d, 0x59, 0x14, 0x01, 0xc6, 0xc9, 0xc2, 0x54, 0x2a, 0xe4, 0x33, 0x83,
|
||||
0x7e, 0x1f, 0x01, 0x4e, 0xde, 0x8b, 0xc9, 0x0b, 0x12, 0x9f, 0xce, 0x88, 0x1a, 0xdb, 0x60, 0xd0,
|
||||
0xcf, 0x4c, 0x5c, 0x97, 0x4d, 0xa5, 0x2b, 0x85, 0xa9, 0xd4, 0x4b, 0xe4, 0xbf, 0x83, 0xa0, 0x7e,
|
||||
0x93, 0x26, 0x35, 0x7b, 0x81, 0x2e, 0xb3, 0x9f, 0x16, 0xf4, 0xe5, 0xc9, 0x0b, 0x05, 0xa2, 0xf3,
|
||||
0x1c, 0xd1, 0x69, 0x5c, 0xac, 0x2a, 0x09, 0xe0, 0x43, 0x04, 0xfb, 0xee, 0xa9, 0x26, 0x8a, 0xcf,
|
||||
0x4f, 0x92, 0x94, 0x89, 0xe4, 0xe5, 0x71, 0x7d, 0x99, 0xe3, 0x5a, 0x35, 0x4a, 0xe1, 0x5a, 0x13,
|
||||
0xfd, 0xfb, 0x5f, 0xa0, 0xf8, 0x69, 0x37, 0xd4, 0x7d, 0xfd, 0x7f, 0xf5, 0x56, 0xd0, 0xc4, 0x35,
|
||||
0x2e, 0x71, 0x7c, 0x0d, 0x7c, 0xbe, 0x0c, 0xbe, 0xa6, 0x68, 0xc9, 0xe2, 0x0f, 0x10, 0x1c, 0xe2,
|
||||
0xfd, 0x6f, 0x95, 0xf1, 0x50, 0x8a, 0x19, 0xd7, 0x2d, 0x2f, 0x91, 0x62, 0x44, 0xfc, 0x31, 0x76,
|
||||
0x04, 0x6a, 0x4d, 0xf6, 0xb6, 0x7f, 0x86, 0x60, 0xbf, 0x4c, 0x6a, 0xe2, 0x76, 0x57, 0x27, 0x29,
|
||||
0x6e, 0xa7, 0x49, 0x50, 0x98, 0xdb, 0x4a, 0x39, 0x73, 0xfb, 0x04, 0xc1, 0x9c, 0xe8, 0x3d, 0x17,
|
||||
0x94, 0x0a, 0x4a, 0x73, 0x5a, 0x1f, 0x7a, 0xf9, 0x8b, 0xa6, 0xa6, 0xf1, 0x3d, 0x2e, 0xf6, 0x01,
|
||||
0x6e, 0x16, 0x89, 0xf5, 0xbd, 0x76, 0xd8, 0x7c, 0x22, 0x3a, 0x8a, 0x4f, 0x9b, 0x8e, 0xd7, 0x09,
|
||||
0xdf, 0x30, 0x70, 0x61, 0x42, 0x64, 0x6b, 0x2e, 0x20, 0x1c, 0x41, 0x8d, 0x19, 0x07, 0x6f, 0x27,
|
||||
0xe0, 0xa5, 0xa1, 0xe6, 0xc3, 0x48, 0xa7, 0x41, 0xd7, 0x47, 0xda, 0x13, 0x69, 0x06, 0x14, 0xcf,
|
||||
0x3e, 0x7c, 0xbc, 0x50, 0x2c, 0x17, 0xf4, 0x36, 0x82, 0x43, 0xaa, 0xb5, 0xc7, 0xe2, 0x4b, 0xdb,
|
||||
0x7a, 0x11, 0x0a, 0x51, 0x54, 0xe3, 0x95, 0x52, 0x86, 0xc4, 0xe1, 0x5c, 0xbd, 0xf1, 0xa7, 0x67,
|
||||
0xc7, 0xd0, 0x5f, 0x9f, 0x1d, 0x43, 0xff, 0x7a, 0x76, 0x0c, 0xbd, 0x71, 0xb9, 0xdc, 0x1f, 0x0f,
|
||||
0x2d, 0xc7, 0xa6, 0x6e, 0xa4, 0xb2, 0xff, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x83, 0x0c, 0x3f,
|
||||
0x61, 0x5e, 0x29, 0x00, 0x00,
|
||||
0xdc, 0x10, 0x12, 0x17, 0xb8, 0x20, 0x90, 0x38, 0x20, 0x3e, 0x2e, 0x9c, 0x90, 0xc5, 0x8d, 0x0b,
|
||||
0x07, 0xfe, 0x00, 0x54, 0xd5, 0x55, 0xdd, 0xd5, 0x33, 0x3d, 0x3d, 0xbd, 0xec, 0x46, 0xf1, 0xad,
|
||||
0x5e, 0x4d, 0xd5, 0x7b, 0xbf, 0x7a, 0xf5, 0xbe, 0xea, 0xf5, 0xc0, 0xc9, 0x90, 0x06, 0x7d, 0x1a,
|
||||
0x34, 0x89, 0xef, 0x3b, 0xb6, 0x45, 0x22, 0xdb, 0x73, 0xd5, 0x71, 0xc3, 0x0f, 0xbc, 0xc8, 0xc3,
|
||||
0x75, 0x65, 0x4a, 0x5f, 0xec, 0x78, 0x5e, 0xc7, 0xa1, 0x4d, 0xe2, 0xdb, 0x4d, 0xe2, 0xba, 0x5e,
|
||||
0xc4, 0xa7, 0xc3, 0x78, 0xa9, 0x6e, 0x6c, 0x5f, 0x0e, 0x1b, 0xb6, 0xc7, 0x7f, 0xb5, 0xbc, 0x80,
|
||||
0x36, 0xfb, 0x17, 0x9b, 0x1d, 0xea, 0xd2, 0x80, 0x44, 0xb4, 0x2d, 0xd6, 0x5c, 0x4a, 0xd7, 0x74,
|
||||
0x89, 0xb5, 0x65, 0xbb, 0x34, 0x18, 0x34, 0xfd, 0xed, 0x0e, 0x9b, 0x08, 0x9b, 0x5d, 0x1a, 0x91,
|
||||
0xbc, 0x5d, 0xeb, 0x1d, 0x3b, 0xda, 0xea, 0xbd, 0xd9, 0xb0, 0xbc, 0x6e, 0x93, 0x04, 0x1d, 0xcf,
|
||||
0x0f, 0xbc, 0xef, 0xf3, 0xc1, 0xaa, 0xd5, 0x6e, 0xf6, 0x5b, 0x29, 0x03, 0xf5, 0x2c, 0xfd, 0x8b,
|
||||
0xc4, 0xf1, 0xb7, 0xc8, 0x28, 0xb7, 0xeb, 0x13, 0xb8, 0x05, 0xd4, 0xf7, 0x84, 0x6e, 0xf8, 0xd0,
|
||||
0x8e, 0xbc, 0x60, 0xa0, 0x0c, 0x63, 0x36, 0xc6, 0x7f, 0x11, 0x1c, 0xbc, 0x92, 0xca, 0xfb, 0x66,
|
||||
0x8f, 0x06, 0x03, 0x8c, 0x61, 0xda, 0x25, 0x5d, 0xaa, 0xa1, 0x25, 0xb4, 0x5c, 0x33, 0xf9, 0x18,
|
||||
0x6b, 0x30, 0x17, 0xd0, 0xcd, 0x80, 0x86, 0x5b, 0x5a, 0x85, 0x4f, 0x4b, 0x12, 0xeb, 0x50, 0x65,
|
||||
0xc2, 0xa9, 0x15, 0x85, 0xda, 0xd4, 0xd2, 0xd4, 0x72, 0xcd, 0x4c, 0x68, 0xbc, 0x0c, 0x07, 0x02,
|
||||
0x1a, 0x7a, 0xbd, 0xc0, 0xa2, 0xdf, 0xa2, 0x41, 0x68, 0x7b, 0xae, 0x36, 0xcd, 0x77, 0x0f, 0x4f,
|
||||
0x33, 0x2e, 0x21, 0x75, 0xa8, 0x15, 0x79, 0x81, 0x36, 0xc3, 0x97, 0x24, 0x34, 0xc3, 0xc3, 0x80,
|
||||
0x6b, 0xb3, 0x31, 0x1e, 0x36, 0xc6, 0x06, 0xcc, 0x13, 0xdf, 0xbf, 0x4b, 0xba, 0x34, 0xf4, 0x89,
|
||||
0x45, 0xb5, 0x39, 0xfe, 0x5b, 0x66, 0x8e, 0x61, 0x16, 0x48, 0xb4, 0x2a, 0x07, 0x26, 0x49, 0xe3,
|
||||
0x1a, 0xd4, 0xee, 0x7a, 0x6d, 0x3a, 0xfe, 0xb8, 0xc3, 0xec, 0x2b, 0xa3, 0xec, 0x8d, 0x6d, 0x38,
|
||||
0x6c, 0xd2, 0xbe, 0xcd, 0xe0, 0xdf, 0xa1, 0x11, 0x69, 0x93, 0x88, 0x0c, 0x33, 0xac, 0x24, 0x0c,
|
||||
0x75, 0xa8, 0x06, 0x62, 0xb1, 0x56, 0xe1, 0xf3, 0x09, 0x3d, 0x22, 0x6c, 0x2a, 0x47, 0xd8, 0x9f,
|
||||
0x10, 0x1c, 0x53, 0x2e, 0xca, 0x14, 0xea, 0xbb, 0xde, 0xa7, 0x6e, 0x14, 0x8e, 0x17, 0x7b, 0x1e,
|
||||
0x0e, 0x49, 0x4d, 0x0f, 0x1f, 0x66, 0xf4, 0x07, 0x06, 0x44, 0x9d, 0x94, 0x40, 0xd4, 0x39, 0xbc,
|
||||
0x04, 0x75, 0x49, 0x3f, 0xb8, 0xfd, 0x9a, 0xb8, 0x4e, 0x75, 0x6a, 0xe4, 0x38, 0x33, 0x39, 0xc7,
|
||||
0x71, 0x41, 0x53, 0x4e, 0x73, 0x87, 0xb8, 0xf6, 0x26, 0x0d, 0xa3, 0xb2, 0xea, 0x43, 0x3b, 0x56,
|
||||
0xdf, 0x71, 0xa8, 0xdd, 0xb0, 0x1d, 0x7a, 0x6d, 0xab, 0xe7, 0x6e, 0xe3, 0x17, 0x61, 0xc6, 0x62,
|
||||
0x03, 0x2e, 0x61, 0xde, 0x8c, 0x09, 0xe3, 0x31, 0x1c, 0x1f, 0x07, 0xe9, 0xa1, 0x1d, 0x6d, 0xb1,
|
||||
0xed, 0xe1, 0x38, 0x6c, 0xd6, 0x16, 0xb5, 0xb6, 0xc3, 0x5e, 0x57, 0x5e, 0xad, 0xa4, 0x4b, 0x61,
|
||||
0xfb, 0x05, 0x82, 0xe5, 0x89, 0x92, 0x1f, 0x06, 0xc4, 0xf7, 0x69, 0x80, 0x6f, 0xc0, 0xcc, 0x23,
|
||||
0xf6, 0x03, 0xb7, 0xd6, 0x7a, 0xab, 0xd1, 0x50, 0xa3, 0xdd, 0x44, 0x2e, 0xb7, 0x3e, 0x67, 0xc6,
|
||||
0xdb, 0x71, 0x43, 0xea, 0xa0, 0xc2, 0xf9, 0x2c, 0x64, 0xf8, 0x24, 0xaa, 0x62, 0xeb, 0xf9, 0xb2,
|
||||
0xab, 0xb3, 0x30, 0xed, 0x93, 0x20, 0x32, 0x0e, 0xc3, 0x0b, 0x59, 0x33, 0xf4, 0x3d, 0x37, 0xa4,
|
||||
0xc6, 0x6f, 0x50, 0xe6, 0x42, 0xaf, 0x05, 0x94, 0x44, 0xd4, 0xa4, 0x8f, 0x7a, 0x34, 0x8c, 0xf0,
|
||||
0x36, 0xa8, 0x01, 0x98, 0xeb, 0xae, 0xde, 0xba, 0xdd, 0x48, 0x23, 0x58, 0x43, 0x46, 0x30, 0x3e,
|
||||
0xf8, 0xae, 0xd5, 0x6e, 0xf4, 0x5b, 0x0d, 0x7f, 0xbb, 0xd3, 0x60, 0xf1, 0x30, 0x83, 0x4c, 0xc6,
|
||||
0x43, 0xf5, 0xa8, 0xa6, 0xca, 0x1d, 0x2f, 0xc0, 0x6c, 0xcf, 0x0f, 0x69, 0x10, 0xf1, 0x93, 0x55,
|
||||
0x4d, 0x41, 0xb1, 0x5b, 0xea, 0x13, 0xc7, 0x6e, 0x93, 0x28, 0xbe, 0x85, 0xaa, 0x99, 0xd0, 0xc6,
|
||||
0xc7, 0x59, 0xf4, 0x0f, 0xfc, 0xf6, 0x67, 0x85, 0x5e, 0x45, 0x59, 0x19, 0x42, 0xf9, 0x41, 0x16,
|
||||
0xe5, 0x6b, 0xd4, 0xa1, 0x29, 0xca, 0x3c, 0xc3, 0xd4, 0x60, 0xce, 0x22, 0xa1, 0x45, 0xda, 0x92,
|
||||
0x97, 0x24, 0x59, 0x58, 0xf0, 0x03, 0xcf, 0x27, 0x1d, 0xce, 0xe9, 0x9e, 0xe7, 0xd8, 0xd6, 0x40,
|
||||
0xd8, 0xe6, 0xe8, 0x0f, 0x23, 0x46, 0x3c, 0x9d, 0x63, 0xc4, 0x27, 0xa0, 0xbe, 0x31, 0x70, 0xad,
|
||||
0xd7, 0x7d, 0x9e, 0x4c, 0x99, 0x8b, 0xd9, 0x11, 0xed, 0x86, 0x1a, 0xe2, 0x81, 0x37, 0x26, 0x8c,
|
||||
0x0f, 0x67, 0x60, 0x41, 0x39, 0x01, 0xdb, 0x50, 0x84, 0xbf, 0xc8, 0xe9, 0x17, 0x60, 0xb6, 0x1d,
|
||||
0x0c, 0xcc, 0x9e, 0x2b, 0x2e, 0x53, 0x50, 0x4c, 0xb0, 0x1f, 0xf4, 0xdc, 0x18, 0x64, 0xd5, 0x8c,
|
||||
0x09, 0xbc, 0x09, 0xd5, 0x30, 0x62, 0xe9, 0xb3, 0x33, 0xe0, 0xe1, 0xa8, 0xde, 0xfa, 0xfa, 0xee,
|
||||
0x2e, 0x90, 0x41, 0xdf, 0x10, 0x1c, 0xcd, 0x84, 0x37, 0x7e, 0x04, 0x35, 0x19, 0x09, 0x43, 0x6d,
|
||||
0x6e, 0x69, 0x6a, 0xb9, 0xde, 0xda, 0xd8, 0xbd, 0xa0, 0xd7, 0x7d, 0x96, 0xfa, 0x95, 0xa8, 0x6f,
|
||||
0xa6, 0x52, 0xf0, 0x22, 0xd4, 0xba, 0xc2, 0xd7, 0x43, 0x91, 0xe6, 0xd2, 0x09, 0xfc, 0x6d, 0x98,
|
||||
0xb1, 0xdd, 0x4d, 0x2f, 0xd4, 0x6a, 0x1c, 0xcc, 0xd5, 0xdd, 0x81, 0xb9, 0xed, 0x6e, 0x7a, 0x66,
|
||||
0xcc, 0x10, 0x3f, 0x82, 0x7d, 0x01, 0x8d, 0x82, 0x81, 0xd4, 0x82, 0x06, 0x5c, 0xaf, 0xdf, 0xd8,
|
||||
0x9d, 0x04, 0x53, 0x65, 0x69, 0x66, 0x25, 0xe0, 0x35, 0xa8, 0x87, 0xa9, 0x8d, 0x69, 0x75, 0x2e,
|
||||
0x50, 0xcb, 0x30, 0x52, 0x6c, 0xd0, 0x54, 0x17, 0x8f, 0xd8, 0xf0, 0x7c, 0x8e, 0x0d, 0xff, 0x1d,
|
||||
0xc1, 0xe2, 0x48, 0x18, 0xd8, 0xf0, 0x69, 0xa1, 0x91, 0x12, 0x98, 0x0e, 0x7d, 0x6a, 0xf1, 0xc8,
|
||||
0x5f, 0x6f, 0xdd, 0xd9, 0xb3, 0xb8, 0xc0, 0xe5, 0x72, 0xd6, 0x45, 0xa1, 0xab, 0x94, 0x6f, 0xfe,
|
||||
0x08, 0xc1, 0xe7, 0x15, 0xce, 0xf7, 0x48, 0x64, 0x6d, 0x15, 0x1d, 0x89, 0xf9, 0x10, 0x5b, 0x23,
|
||||
0xb2, 0x59, 0x4c, 0x30, 0x43, 0xe3, 0x83, 0xfb, 0x03, 0x9f, 0xc1, 0x60, 0xbf, 0xa4, 0x13, 0xa5,
|
||||
0x92, 0xfe, 0xbb, 0x08, 0x74, 0x35, 0xf2, 0x79, 0x8e, 0xf3, 0x26, 0xb1, 0xb6, 0x8b, 0xa0, 0xec,
|
||||
0x87, 0x8a, 0xdd, 0xe6, 0x38, 0xa6, 0xcc, 0x8a, 0xdd, 0xde, 0xa1, 0xdb, 0x0f, 0x83, 0x9a, 0xcd,
|
||||
0x01, 0xf5, 0x8f, 0x21, 0x50, 0xd2, 0xc5, 0x0a, 0x40, 0x2d, 0x42, 0xcd, 0x1d, 0x2a, 0xa6, 0xd2,
|
||||
0x89, 0x9c, 0x22, 0xaa, 0x32, 0x52, 0x44, 0x69, 0x30, 0xd7, 0x4f, 0xea, 0x61, 0xf6, 0xb3, 0x24,
|
||||
0xd9, 0x41, 0x3a, 0x81, 0xd7, 0xf3, 0x85, 0x02, 0x63, 0x82, 0xa1, 0xd8, 0xb6, 0xdd, 0xb6, 0x36,
|
||||
0x1b, 0xa3, 0x60, 0xe3, 0x32, 0x15, 0xb0, 0xf1, 0x5e, 0x05, 0xbe, 0x90, 0x73, 0xb8, 0x89, 0x16,
|
||||
0xf0, 0x7c, 0x9c, 0x30, 0xb1, 0xc3, 0xb9, 0xb1, 0x76, 0x58, 0x9d, 0x64, 0x87, 0xb5, 0x1c, 0xad,
|
||||
0xbc, 0x53, 0x81, 0xa5, 0x1c, 0xad, 0x4c, 0x4e, 0xa8, 0xcf, 0x8d, 0x5a, 0x36, 0xbd, 0x40, 0xdc,
|
||||
0x78, 0xd5, 0x8c, 0x09, 0xe6, 0x19, 0x5e, 0xe0, 0x6f, 0x11, 0x57, 0xab, 0xc6, 0x9e, 0x11, 0x53,
|
||||
0xa5, 0x14, 0xf2, 0x1f, 0x04, 0x9a, 0xd4, 0xc2, 0x15, 0x8b, 0xeb, 0xa4, 0xe7, 0x3e, 0xff, 0x8a,
|
||||
0x58, 0x80, 0x59, 0xc2, 0xd1, 0x0a, 0x03, 0x11, 0xd4, 0xc8, 0x91, 0xab, 0xf9, 0x31, 0xf1, 0x48,
|
||||
0xf6, 0xc8, 0xe1, 0xba, 0x1d, 0x46, 0xb2, 0xa0, 0xc5, 0x9b, 0x30, 0x17, 0x73, 0x8b, 0x4b, 0x98,
|
||||
0x7a, 0x6b, 0x7d, 0xb7, 0x89, 0x2d, 0xa3, 0x5e, 0xc9, 0xdc, 0x78, 0x19, 0x8e, 0xe4, 0x46, 0x1f,
|
||||
0x01, 0x43, 0x87, 0xaa, 0x4c, 0xe6, 0xe2, 0x02, 0x12, 0xda, 0xf8, 0xf7, 0x54, 0x36, 0xac, 0x7b,
|
||||
0xed, 0x75, 0xaf, 0x53, 0xf0, 0x16, 0x2c, 0xbe, 0x34, 0xf6, 0x58, 0xf6, 0xda, 0xca, 0xb3, 0x4f,
|
||||
0x92, 0x6c, 0x9f, 0xe5, 0xb9, 0x11, 0xb1, 0x5d, 0x1a, 0x88, 0xfc, 0x92, 0x4e, 0x30, 0x65, 0x87,
|
||||
0xb6, 0x6b, 0xd1, 0x0d, 0x6a, 0x79, 0x6e, 0x3b, 0xe4, 0xb7, 0x36, 0x65, 0x66, 0xe6, 0xf0, 0x2d,
|
||||
0xa8, 0x71, 0xfa, 0xbe, 0xdd, 0x8d, 0x83, 0x70, 0xbd, 0xb5, 0xd2, 0x88, 0x9b, 0x28, 0x0d, 0xb5,
|
||||
0x89, 0x92, 0xea, 0xb0, 0x4b, 0x23, 0xd2, 0xe8, 0x5f, 0x6c, 0xb0, 0x1d, 0x66, 0xba, 0x99, 0x61,
|
||||
0x89, 0x88, 0xed, 0xac, 0xdb, 0x2e, 0x2f, 0xb0, 0x98, 0xa8, 0x74, 0x82, 0x19, 0xc4, 0xa6, 0xe7,
|
||||
0x38, 0xde, 0x63, 0xe9, 0x03, 0x31, 0xc5, 0x76, 0xf5, 0xdc, 0xc8, 0x76, 0xb8, 0xfc, 0xd8, 0x01,
|
||||
0xd2, 0x09, 0xbe, 0xcb, 0x76, 0x22, 0x1a, 0xf0, 0x12, 0xa6, 0x66, 0x0a, 0x2a, 0x31, 0xb9, 0x7a,
|
||||
0xdc, 0x17, 0x90, 0xbe, 0x17, 0x1b, 0xe7, 0xbc, 0x6a, 0x9c, 0xc3, 0x06, 0xbf, 0x2f, 0xe7, 0xdd,
|
||||
0xcc, 0xdb, 0x24, 0xb4, 0x6f, 0x7b, 0xbd, 0x50, 0xdb, 0x1f, 0x27, 0x71, 0x49, 0x8f, 0x18, 0xec,
|
||||
0x81, 0x1c, 0x83, 0xfd, 0x2d, 0x82, 0xea, 0xba, 0xd7, 0xb9, 0xee, 0x46, 0xc1, 0x80, 0x57, 0xf6,
|
||||
0x9e, 0x1b, 0x51, 0x57, 0x5a, 0x85, 0x24, 0x99, 0xaa, 0x23, 0xbb, 0x4b, 0x37, 0x22, 0xd2, 0xf5,
|
||||
0x45, 0x4d, 0xb2, 0x23, 0x55, 0x27, 0x9b, 0xd9, 0xf1, 0x1d, 0x12, 0x46, 0xdc, 0x7b, 0xab, 0x26,
|
||||
0x1f, 0x33, 0xa0, 0xc9, 0x82, 0x8d, 0x28, 0x10, 0xae, 0x9b, 0x99, 0x53, 0x0d, 0x69, 0x26, 0xc6,
|
||||
0x26, 0x48, 0x63, 0x03, 0x5e, 0x4a, 0x4a, 0xd9, 0xfb, 0x34, 0xe8, 0xda, 0x2e, 0x29, 0x8e, 0xb7,
|
||||
0x65, 0xba, 0x30, 0x0f, 0x32, 0x0e, 0xc4, 0xea, 0xbf, 0x87, 0xb6, 0xdb, 0xf6, 0x1e, 0x17, 0x38,
|
||||
0x42, 0x19, 0xb6, 0x7f, 0xc9, 0xf6, 0x5b, 0x14, 0xbe, 0x89, 0x6f, 0xde, 0x82, 0x7d, 0xcc, 0x8b,
|
||||
0xfb, 0x54, 0xfc, 0x20, 0x02, 0x85, 0x31, 0xee, 0x49, 0x9e, 0xf2, 0x30, 0xb3, 0x1b, 0xf1, 0x3a,
|
||||
0x1c, 0x20, 0x61, 0x68, 0x77, 0x5c, 0xda, 0x96, 0xbc, 0x2a, 0xa5, 0x79, 0x0d, 0x6f, 0x8d, 0x9f,
|
||||
0x7d, 0x7c, 0x85, 0xb8, 0x3b, 0x49, 0x1a, 0x3f, 0x44, 0x70, 0x38, 0x97, 0x49, 0x62, 0xeb, 0x48,
|
||||
0x09, 0xaf, 0x3a, 0x54, 0x43, 0x6b, 0x8b, 0xb6, 0x7b, 0x0e, 0x95, 0x7d, 0x0d, 0x49, 0xb3, 0xdf,
|
||||
0xda, 0xbd, 0xf8, 0x26, 0x45, 0x78, 0x4f, 0x68, 0x7c, 0x0c, 0xa0, 0x4b, 0xdc, 0x1e, 0x71, 0x38,
|
||||
0x84, 0x69, 0x0e, 0x41, 0x99, 0x31, 0x16, 0x41, 0xcf, 0x33, 0x03, 0xd1, 0x49, 0xf8, 0x1b, 0x82,
|
||||
0xfd, 0x32, 0x0c, 0x8a, 0x3b, 0x5c, 0x86, 0x03, 0x8a, 0x1a, 0xee, 0xa6, 0xd7, 0x39, 0x3c, 0x3d,
|
||||
0x21, 0xc4, 0x49, 0x5b, 0x98, 0xca, 0xf6, 0x35, 0xfb, 0x99, 0xce, 0x64, 0xe9, 0x3c, 0x84, 0x76,
|
||||
0x54, 0x89, 0xfd, 0x00, 0xb4, 0x3b, 0xc4, 0x25, 0x1d, 0xda, 0x4e, 0x0e, 0x97, 0x18, 0xd2, 0xf7,
|
||||
0xd4, 0xc7, 0xf2, 0xae, 0x9f, 0xa6, 0x49, 0x39, 0x63, 0x6f, 0x6e, 0xca, 0x87, 0x77, 0x00, 0xd5,
|
||||
0x75, 0xdb, 0xdd, 0x66, 0xef, 0x37, 0x76, 0xae, 0xc8, 0x8e, 0x1c, 0xa9, 0xc3, 0x98, 0xc0, 0x07,
|
||||
0x61, 0xaa, 0x17, 0x38, 0xe2, 0x9e, 0xd9, 0x10, 0x2f, 0x41, 0xbd, 0x4d, 0x43, 0x2b, 0xb0, 0x7d,
|
||||
0x71, 0xcb, 0xbc, 0xd1, 0xa7, 0x4c, 0x31, 0x6d, 0xdb, 0x96, 0xe7, 0x5e, 0x73, 0x48, 0x18, 0xca,
|
||||
0xc4, 0x90, 0x4c, 0x18, 0xaf, 0xc2, 0x3e, 0x26, 0x33, 0x3d, 0xe6, 0xb9, 0xec, 0x31, 0x0f, 0x67,
|
||||
0xe0, 0x4b, 0x78, 0x12, 0xf1, 0x4d, 0x78, 0x81, 0xe5, 0xe3, 0x2b, 0xbe, 0x2f, 0x98, 0x94, 0x2c,
|
||||
0x46, 0xa6, 0x86, 0x2e, 0xbd, 0xf5, 0x63, 0x03, 0xb0, 0x6a, 0xf3, 0x34, 0xe8, 0xdb, 0x16, 0xc5,
|
||||
0xef, 0x22, 0x98, 0x66, 0x02, 0xf0, 0xd1, 0x71, 0x2e, 0xc6, 0x6d, 0x4f, 0xdf, 0xbb, 0x07, 0x1d,
|
||||
0x93, 0x66, 0x2c, 0xbe, 0xf5, 0xd7, 0x7f, 0xbd, 0x57, 0x59, 0xc0, 0x2f, 0xf2, 0x0f, 0x0c, 0xfd,
|
||||
0x8b, 0x6a, 0xb3, 0x3f, 0xc4, 0x6f, 0x23, 0xc0, 0xa2, 0x0a, 0x51, 0xba, 0xbb, 0xf8, 0xdc, 0x38,
|
||||
0x88, 0x39, 0x5d, 0x60, 0xfd, 0xa8, 0x12, 0xed, 0x1b, 0x96, 0x17, 0x50, 0x16, 0xdb, 0xf9, 0x02,
|
||||
0x0e, 0x60, 0x85, 0x03, 0x38, 0x89, 0x8d, 0x3c, 0x00, 0xcd, 0x27, 0x4c, 0x6f, 0x4f, 0x9b, 0x34,
|
||||
0x96, 0xfb, 0x11, 0x82, 0x99, 0x87, 0xbc, 0xe6, 0x9e, 0xa0, 0xa4, 0x8d, 0x3d, 0x53, 0x12, 0x17,
|
||||
0xc7, 0xd1, 0x1a, 0x27, 0x38, 0xd2, 0xa3, 0xf8, 0x88, 0x44, 0x1a, 0x46, 0x01, 0x25, 0xdd, 0x0c,
|
||||
0xe0, 0x0b, 0x08, 0x7f, 0x82, 0x60, 0x36, 0x6e, 0x37, 0xe2, 0x53, 0xe3, 0x50, 0x66, 0xda, 0x91,
|
||||
0xfa, 0xde, 0xf5, 0xee, 0x8c, 0xb3, 0x1c, 0xe3, 0x09, 0x23, 0xf7, 0x3a, 0xd7, 0x32, 0x9d, 0xbd,
|
||||
0xf7, 0x11, 0x4c, 0xdd, 0xa4, 0x13, 0xed, 0x6d, 0x0f, 0xc1, 0x8d, 0x28, 0x30, 0xe7, 0xaa, 0xf1,
|
||||
0xc7, 0x08, 0x5e, 0xba, 0x49, 0xa3, 0xfc, 0x54, 0x87, 0x97, 0x27, 0xe7, 0x1f, 0x61, 0x76, 0xe7,
|
||||
0x4a, 0xac, 0x4c, 0x62, 0x7c, 0x93, 0x23, 0x3b, 0x8b, 0xcf, 0x14, 0x19, 0x61, 0x38, 0x70, 0xad,
|
||||
0xc7, 0x02, 0xc7, 0x1f, 0x11, 0x1c, 0x1c, 0xfe, 0xd6, 0x82, 0xb3, 0xc9, 0x31, 0xf7, 0x53, 0x8c,
|
||||
0x7e, 0x77, 0xb7, 0xb1, 0x34, 0xcb, 0xd4, 0xb8, 0xc2, 0x91, 0xbf, 0x82, 0x5f, 0x2e, 0x42, 0x2e,
|
||||
0x9b, 0x94, 0x61, 0xf3, 0x89, 0x1c, 0x3e, 0xe5, 0x9f, 0x05, 0x39, 0xec, 0xb7, 0x10, 0xcc, 0xdf,
|
||||
0xa4, 0xd1, 0x9d, 0xa4, 0x47, 0x77, 0xaa, 0x54, 0x0f, 0x5f, 0x5f, 0x6c, 0x28, 0x5f, 0xef, 0xe4,
|
||||
0x4f, 0x89, 0x4a, 0x57, 0x39, 0xb0, 0x33, 0xf8, 0x54, 0x11, 0xb0, 0xb4, 0x2f, 0xf8, 0x11, 0x82,
|
||||
0xc3, 0x2a, 0x88, 0xf4, 0x0b, 0xc7, 0x97, 0x76, 0xf6, 0x45, 0x41, 0x7c, 0x97, 0x98, 0x80, 0xae,
|
||||
0xc5, 0xd1, 0x9d, 0x37, 0xf2, 0x2f, 0xbc, 0x3b, 0x82, 0x62, 0x0d, 0xad, 0x2c, 0x23, 0xfc, 0x3b,
|
||||
0x04, 0xb3, 0x71, 0x13, 0x6e, 0xbc, 0x8e, 0x32, 0xbd, 0xfa, 0xbd, 0xf4, 0x9e, 0xeb, 0x1c, 0xf2,
|
||||
0x57, 0xf5, 0x0b, 0xf9, 0x0a, 0x55, 0xf7, 0xcb, 0xab, 0x6d, 0x70, 0x2d, 0x67, 0xdd, 0xfe, 0x57,
|
||||
0x08, 0x20, 0x6d, 0x24, 0xe2, 0xb3, 0xc5, 0xe7, 0x50, 0x9a, 0x8d, 0xfa, 0xde, 0xb6, 0x12, 0x8d,
|
||||
0x06, 0x3f, 0xcf, 0xb2, 0xbe, 0x54, 0xe8, 0x73, 0x3e, 0xb5, 0xd6, 0xe2, 0xa6, 0xe3, 0xcf, 0x11,
|
||||
0xcc, 0xf0, 0x3e, 0x11, 0x3e, 0x39, 0x0e, 0xb3, 0xda, 0x46, 0xda, 0x4b, 0xd5, 0x9f, 0xe6, 0x50,
|
||||
0x97, 0x5a, 0x45, 0x81, 0x6b, 0x0d, 0xad, 0xe0, 0x3e, 0xcc, 0xc6, 0x3d, 0x9b, 0xf1, 0xe6, 0x91,
|
||||
0xe9, 0xe9, 0xe8, 0x4b, 0x05, 0x89, 0x34, 0x36, 0x54, 0x11, 0x33, 0x57, 0x26, 0xc5, 0xcc, 0x69,
|
||||
0x16, 0xd6, 0xf0, 0x89, 0xa2, 0xa0, 0xf7, 0x29, 0x28, 0xe6, 0x1c, 0x47, 0x77, 0xca, 0x58, 0x9a,
|
||||
0x14, 0x37, 0x99, 0x76, 0x7e, 0x8a, 0xe0, 0xe0, 0x70, 0xc9, 0x89, 0x8f, 0x0c, 0xc5, 0x4c, 0xb5,
|
||||
0xce, 0xd6, 0xb3, 0x5a, 0x1c, 0x57, 0xae, 0x1a, 0x5f, 0xe3, 0x28, 0xd6, 0xf0, 0xe5, 0x89, 0x9e,
|
||||
0x71, 0x57, 0x46, 0x1d, 0xc6, 0x68, 0x35, 0xfd, 0x66, 0xf1, 0x6b, 0x04, 0xf3, 0x92, 0xef, 0xfd,
|
||||
0x80, 0xd2, 0x62, 0x58, 0x7b, 0xe7, 0x08, 0x4c, 0x96, 0xf1, 0x2a, 0x87, 0xff, 0x65, 0x7c, 0xa9,
|
||||
0x24, 0x7c, 0x09, 0x7b, 0x35, 0x62, 0x48, 0x7f, 0x8f, 0xe0, 0xd0, 0xc3, 0xd8, 0xee, 0x3f, 0x23,
|
||||
0xfc, 0xd7, 0x38, 0xfe, 0xaf, 0xe0, 0x57, 0x0a, 0xea, 0xa2, 0x49, 0xc7, 0xb8, 0x80, 0xf0, 0x2f,
|
||||
0x11, 0x54, 0x65, 0x07, 0x1e, 0x9f, 0x19, 0xeb, 0x18, 0xd9, 0x1e, 0xfd, 0x5e, 0x1a, 0xb3, 0x28,
|
||||
0x02, 0x8c, 0x93, 0x85, 0xa9, 0x54, 0xc8, 0x67, 0x06, 0xfd, 0x3e, 0x02, 0x9c, 0xbc, 0x17, 0x93,
|
||||
0x17, 0x24, 0x3e, 0x9d, 0x11, 0x35, 0xb6, 0xc1, 0xa0, 0x9f, 0x99, 0xb8, 0x2e, 0x9b, 0x4a, 0x57,
|
||||
0x0a, 0x53, 0xa9, 0x97, 0xc8, 0x7f, 0x07, 0x41, 0xfd, 0x26, 0x4d, 0x6a, 0xf6, 0x02, 0x5d, 0x66,
|
||||
0x3f, 0x2d, 0xe8, 0xcb, 0x93, 0x17, 0x0a, 0x44, 0xe7, 0x39, 0xa2, 0xd3, 0xb8, 0x58, 0x55, 0x12,
|
||||
0xc0, 0x87, 0x08, 0xf6, 0xdd, 0x53, 0x4d, 0x14, 0x9f, 0x9f, 0x24, 0x29, 0x13, 0xc9, 0xcb, 0xe3,
|
||||
0xfa, 0x22, 0xc7, 0xb5, 0x6a, 0x94, 0xc2, 0xb5, 0x26, 0xfa, 0xf7, 0x3f, 0x43, 0xf1, 0xd3, 0x6e,
|
||||
0xa8, 0xfb, 0xfa, 0xff, 0xea, 0xad, 0xa0, 0x89, 0x6b, 0x5c, 0xe2, 0xf8, 0x1a, 0xf8, 0x7c, 0x19,
|
||||
0x7c, 0x4d, 0xd1, 0x92, 0xc5, 0x1f, 0x20, 0x38, 0xc4, 0xfb, 0xdf, 0x2a, 0xe3, 0xa1, 0x14, 0x33,
|
||||
0xae, 0x5b, 0x5e, 0x22, 0xc5, 0x88, 0xf8, 0x63, 0xec, 0x08, 0xd4, 0x9a, 0xec, 0x6d, 0xff, 0x04,
|
||||
0xc1, 0x7e, 0x99, 0xd4, 0xc4, 0xed, 0xae, 0x4e, 0x52, 0xdc, 0x4e, 0x93, 0xa0, 0x30, 0xb7, 0x95,
|
||||
0x72, 0xe6, 0xf6, 0x09, 0x82, 0x39, 0xd1, 0x7b, 0x2e, 0x28, 0x15, 0x94, 0xe6, 0xb4, 0x3e, 0xf4,
|
||||
0xf2, 0x17, 0x4d, 0x4d, 0xe3, 0x3b, 0x5c, 0xec, 0x03, 0xdc, 0x2c, 0x12, 0xeb, 0x7b, 0xed, 0xb0,
|
||||
0xf9, 0x44, 0x74, 0x14, 0x9f, 0x36, 0x1d, 0xaf, 0x13, 0xbe, 0x61, 0xe0, 0xc2, 0x84, 0xc8, 0xd6,
|
||||
0x5c, 0x40, 0x38, 0x82, 0x1a, 0x33, 0x0e, 0xde, 0x4e, 0xc0, 0x4b, 0x43, 0xcd, 0x87, 0x91, 0x4e,
|
||||
0x83, 0xae, 0x8f, 0xb4, 0x27, 0xd2, 0x0c, 0x28, 0x9e, 0x7d, 0xf8, 0x78, 0xa1, 0x58, 0x2e, 0xe8,
|
||||
0x6d, 0x04, 0x87, 0x54, 0x6b, 0x8f, 0xc5, 0x97, 0xb6, 0xf5, 0x22, 0x14, 0xa2, 0xa8, 0xc6, 0x2b,
|
||||
0xa5, 0x0c, 0x89, 0xc3, 0xb9, 0x7a, 0xe3, 0x0f, 0xcf, 0x8e, 0xa1, 0x3f, 0x3f, 0x3b, 0x86, 0xfe,
|
||||
0xf9, 0xec, 0x18, 0x7a, 0xe3, 0x72, 0xb9, 0xbf, 0x24, 0x5a, 0x8e, 0x4d, 0xdd, 0x48, 0x65, 0xff,
|
||||
0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x96, 0x44, 0xa9, 0x6d, 0x78, 0x29, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
@@ -4002,6 +4011,15 @@ func (m *ApplicationQuery) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i -= len(m.XXX_unrecognized)
|
||||
copy(dAtA[i:], m.XXX_unrecognized)
|
||||
}
|
||||
if len(m.Project) > 0 {
|
||||
for iNdEx := len(m.Project) - 1; iNdEx >= 0; iNdEx-- {
|
||||
i -= len(m.Project[iNdEx])
|
||||
copy(dAtA[i:], m.Project[iNdEx])
|
||||
i = encodeVarintApplication(dAtA, i, uint64(len(m.Project[iNdEx])))
|
||||
i--
|
||||
dAtA[i] = 0x42
|
||||
}
|
||||
}
|
||||
if m.AppNamespace != nil {
|
||||
i -= len(*m.AppNamespace)
|
||||
copy(dAtA[i:], *m.AppNamespace)
|
||||
@@ -6241,6 +6259,12 @@ func (m *ApplicationQuery) Size() (n int) {
|
||||
l = len(*m.AppNamespace)
|
||||
n += 1 + l + sovApplication(uint64(l))
|
||||
}
|
||||
if len(m.Project) > 0 {
|
||||
for _, s := range m.Project {
|
||||
l = len(s)
|
||||
n += 1 + l + sovApplication(uint64(l))
|
||||
}
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
}
|
||||
@@ -7474,6 +7498,38 @@ func (m *ApplicationQuery) Unmarshal(dAtA []byte) error {
|
||||
s := string(dAtA[iNdEx:postIndex])
|
||||
m.AppNamespace = &s
|
||||
iNdEx = postIndex
|
||||
case 8:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowApplication
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthApplication
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthApplication
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Project = append(m.Project, string(dAtA[iNdEx:postIndex]))
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipApplication(dAtA[iNdEx:])
|
||||
|
||||
@@ -19,7 +19,7 @@ const (
|
||||
AppProjectFullName string = AppProjectPlural + "." + Group
|
||||
|
||||
// ApplicationSet constants
|
||||
ApplicationSetKind string = "Applicationset"
|
||||
ApplicationSetKind string = "ApplicationSet"
|
||||
ApplicationSetSingular string = "applicationset"
|
||||
ApplicationSetShortName string = "appset"
|
||||
ApplicationSetPlural string = "applicationsets"
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
// Code generated by mockery v2.13.1. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
apiclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
|
||||
metadata "google.golang.org/grpc/metadata"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// RepoServerService_GenerateManifestWithFilesClient is an autogenerated mock type for the RepoServerService_GenerateManifestWithFilesClient type
|
||||
type RepoServerService_GenerateManifestWithFilesClient struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// CloseAndRecv provides a mock function with given fields:
|
||||
func (_m *RepoServerService_GenerateManifestWithFilesClient) CloseAndRecv() (*apiclient.ManifestResponse, error) {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 *apiclient.ManifestResponse
|
||||
if rf, ok := ret.Get(0).(func() *apiclient.ManifestResponse); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*apiclient.ManifestResponse)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func() error); ok {
|
||||
r1 = rf()
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// CloseSend provides a mock function with given fields:
|
||||
func (_m *RepoServerService_GenerateManifestWithFilesClient) CloseSend() error {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func() error); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Context provides a mock function with given fields:
|
||||
func (_m *RepoServerService_GenerateManifestWithFilesClient) Context() context.Context {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 context.Context
|
||||
if rf, ok := ret.Get(0).(func() context.Context); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(context.Context)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Header provides a mock function with given fields:
|
||||
func (_m *RepoServerService_GenerateManifestWithFilesClient) Header() (metadata.MD, error) {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 metadata.MD
|
||||
if rf, ok := ret.Get(0).(func() metadata.MD); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(metadata.MD)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func() error); ok {
|
||||
r1 = rf()
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// RecvMsg provides a mock function with given fields: m
|
||||
func (_m *RepoServerService_GenerateManifestWithFilesClient) RecvMsg(m interface{}) error {
|
||||
ret := _m.Called(m)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(interface{}) error); ok {
|
||||
r0 = rf(m)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Send provides a mock function with given fields: _a0
|
||||
func (_m *RepoServerService_GenerateManifestWithFilesClient) Send(_a0 *apiclient.ManifestRequestWithFiles) error {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(*apiclient.ManifestRequestWithFiles) error); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// SendMsg provides a mock function with given fields: m
|
||||
func (_m *RepoServerService_GenerateManifestWithFilesClient) SendMsg(m interface{}) error {
|
||||
ret := _m.Called(m)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(interface{}) error); ok {
|
||||
r0 = rf(m)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Trailer provides a mock function with given fields:
|
||||
func (_m *RepoServerService_GenerateManifestWithFilesClient) Trailer() metadata.MD {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 metadata.MD
|
||||
if rf, ok := ret.Get(0).(func() metadata.MD); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(metadata.MD)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
type mockConstructorTestingTNewRepoServerService_GenerateManifestWithFilesClient interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}
|
||||
|
||||
// NewRepoServerService_GenerateManifestWithFilesClient creates a new instance of RepoServerService_GenerateManifestWithFilesClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
func NewRepoServerService_GenerateManifestWithFilesClient(t mockConstructorTestingTNewRepoServerService_GenerateManifestWithFilesClient) *RepoServerService_GenerateManifestWithFilesClient {
|
||||
mock := &RepoServerService_GenerateManifestWithFilesClient{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
@@ -68,7 +68,8 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
watchAPIBufferSize = env.ParseNumFromEnv(argocommon.EnvWatchAPIBufferSize, 1000, 0, math.MaxInt32)
|
||||
watchAPIBufferSize = env.ParseNumFromEnv(argocommon.EnvWatchAPIBufferSize, 1000, 0, math.MaxInt32)
|
||||
permissionDeniedErr = status.Error(codes.PermissionDenied, "permission denied")
|
||||
)
|
||||
|
||||
// Server provides an Application service
|
||||
@@ -78,7 +79,7 @@ type Server struct {
|
||||
appclientset appclientset.Interface
|
||||
appLister applisters.ApplicationLister
|
||||
appInformer cache.SharedIndexInformer
|
||||
appBroadcaster *broadcasterHandler
|
||||
appBroadcaster Broadcaster
|
||||
repoClientset apiclient.Clientset
|
||||
kubectl kube.Kubectl
|
||||
db db.ArgoDB
|
||||
@@ -98,6 +99,7 @@ func NewServer(
|
||||
appclientset appclientset.Interface,
|
||||
appLister applisters.ApplicationLister,
|
||||
appInformer cache.SharedIndexInformer,
|
||||
appBroadcaster Broadcaster,
|
||||
repoClientset apiclient.Clientset,
|
||||
cache *servercache.Cache,
|
||||
kubectl kube.Kubectl,
|
||||
@@ -108,7 +110,9 @@ func NewServer(
|
||||
projInformer cache.SharedIndexInformer,
|
||||
enabledNamespaces []string,
|
||||
) (application.ApplicationServiceServer, AppResourceTreeFn) {
|
||||
appBroadcaster := &broadcasterHandler{}
|
||||
if appBroadcaster == nil {
|
||||
appBroadcaster = &broadcasterHandler{}
|
||||
}
|
||||
appInformer.AddEventHandler(appBroadcaster)
|
||||
s := &Server{
|
||||
ns: namespace,
|
||||
@@ -131,6 +135,61 @@ func NewServer(
|
||||
return s, s.getAppResources
|
||||
}
|
||||
|
||||
// getAppEnforceRBAC gets the Application with the given name in the given namespace. If no namespace is
|
||||
// specified, the Application is fetched from the default namespace (the one in which the API server is running).
|
||||
//
|
||||
// If the Application does not exist, then we have no way of determining if the user would have had access to get that
|
||||
// Application. Verifying access requires knowing the Application's name, namespace, and project. The user may specify,
|
||||
// at minimum, the Application name.
|
||||
//
|
||||
// So to prevent a malicious user from inferring the existence or absense of the Application or namespace, we respond
|
||||
// "permission denied" if the Application does not exist.
|
||||
func (s *Server) getAppEnforceRBAC(ctx context.Context, action, namespace, name string, getApp func() (*appv1.Application, error)) (*appv1.Application, error) {
|
||||
logCtx := log.WithFields(map[string]interface{}{
|
||||
"application": name,
|
||||
"namespace": namespace,
|
||||
})
|
||||
a, err := getApp()
|
||||
if err != nil {
|
||||
if apierr.IsNotFound(err) {
|
||||
logCtx.Warn("application does not exist")
|
||||
return nil, permissionDeniedErr
|
||||
}
|
||||
logCtx.Errorf("failed to get application: %s", err)
|
||||
return nil, permissionDeniedErr
|
||||
}
|
||||
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, action, a.RBACName(s.ns)); err != nil {
|
||||
logCtx.WithFields(map[string]interface{}{
|
||||
"project": a.Spec.Project,
|
||||
argocommon.SecurityField: argocommon.SecurityMedium,
|
||||
}).Warnf("user tried to %s application which they do not have access to: %s", action, err)
|
||||
return nil, permissionDeniedErr
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// getApplicationEnforceRBACInformer uses an informer to get an Application. If the app does not exist, permission is
|
||||
// denied, or any other error occurs when getting the app, we return a permission denied error to obscure any sensitive
|
||||
// information.
|
||||
func (s *Server) getApplicationEnforceRBACInformer(ctx context.Context, action, namespace, name string) (*appv1.Application, error) {
|
||||
namespaceOrDefault := s.appNamespaceOrDefault(namespace)
|
||||
return s.getAppEnforceRBAC(ctx, action, namespaceOrDefault, name, func() (*appv1.Application, error) {
|
||||
return s.appLister.Applications(namespaceOrDefault).Get(name)
|
||||
})
|
||||
}
|
||||
|
||||
// getApplicationEnforceRBACClient uses a client to get an Application. If the app does not exist, permission is denied,
|
||||
// or any other error occurs when getting the app, we return a permission denied error to obscure any sensitive
|
||||
// information.
|
||||
func (s *Server) getApplicationEnforceRBACClient(ctx context.Context, action, namespace, name, resourceVersion string) (*appv1.Application, error) {
|
||||
namespaceOrDefault := s.appNamespaceOrDefault(namespace)
|
||||
return s.getAppEnforceRBAC(ctx, action, namespaceOrDefault, name, func() (*appv1.Application, error) {
|
||||
return s.appclientset.ArgoprojV1alpha1().Applications(namespaceOrDefault).Get(ctx, name, metav1.GetOptions{
|
||||
ResourceVersion: resourceVersion,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// List returns list of applications
|
||||
func (s *Server) List(ctx context.Context, q *application.ApplicationQuery) (*appv1.ApplicationList, error) {
|
||||
selector, err := labels.Parse(q.GetSelector())
|
||||
@@ -148,7 +207,7 @@ func (s *Server) List(ctx context.Context, q *application.ApplicationQuery) (*ap
|
||||
}
|
||||
newItems := make([]appv1.Application, 0)
|
||||
for _, a := range apps {
|
||||
// Skip any application that is neither in the conrol plane's namespace
|
||||
// Skip any application that is neither in the control plane's namespace
|
||||
// nor in the list of enabled namespaces.
|
||||
if a.Namespace != s.ns && !glob.MatchStringInList(s.enabledNamespaces, a.Namespace, false) {
|
||||
continue
|
||||
@@ -165,8 +224,8 @@ func (s *Server) List(ctx context.Context, q *application.ApplicationQuery) (*ap
|
||||
}
|
||||
}
|
||||
|
||||
// Filter applications by name
|
||||
newItems = argoutil.FilterByProjects(newItems, q.Projects)
|
||||
// Filter applications by projects
|
||||
newItems = argoutil.FilterByProjects(newItems, getProjectsFromApplicationQuery(*q))
|
||||
|
||||
// Filter applications by source repo URL
|
||||
newItems = argoutil.FilterByRepo(newItems, q.GetRepo())
|
||||
@@ -318,13 +377,8 @@ func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationMan
|
||||
if q.Name == nil || *q.Name == "" {
|
||||
return nil, fmt.Errorf("invalid request: application name is missing")
|
||||
}
|
||||
appName := q.GetName()
|
||||
appNs := s.appNamespaceOrDefault(q.GetAppNamespace())
|
||||
a, err := s.appLister.Applications(appNs).Get(appName)
|
||||
a, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetAppNamespace(), q.GetName())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting application: %w", err)
|
||||
}
|
||||
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -426,14 +480,8 @@ func (s *Server) GetManifestsWithFiles(stream application.ApplicationService_Get
|
||||
return fmt.Errorf("invalid request: application name is missing")
|
||||
}
|
||||
|
||||
appName := query.GetName()
|
||||
appNs := s.appNamespaceOrDefault(query.GetAppNamespace())
|
||||
a, err := s.appLister.Applications(appNs).Get(appName)
|
||||
|
||||
a, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, query.GetAppNamespace(), query.GetName())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting application: %w", err)
|
||||
}
|
||||
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -538,14 +586,8 @@ func (s *Server) Get(ctx context.Context, q *application.ApplicationQuery) (*app
|
||||
// We must use a client Get instead of an informer Get, because it's common to call Get immediately
|
||||
// following a Watch (which is not yet powered by an informer), and the Get must reflect what was
|
||||
// previously seen by the client.
|
||||
a, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Get(ctx, appName, metav1.GetOptions{
|
||||
ResourceVersion: q.GetResourceVersion(),
|
||||
})
|
||||
|
||||
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, appNs, appName, q.GetResourceVersion())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting application: %w", err)
|
||||
}
|
||||
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -627,13 +669,8 @@ func (s *Server) Get(ctx context.Context, q *application.ApplicationQuery) (*app
|
||||
|
||||
// ListResourceEvents returns a list of event resources
|
||||
func (s *Server) ListResourceEvents(ctx context.Context, q *application.ApplicationResourceEventsQuery) (*v1.EventList, error) {
|
||||
appName := q.GetName()
|
||||
appNs := s.appNamespaceOrDefault(q.GetAppNamespace())
|
||||
a, err := s.appLister.Applications(appNs).Get(appName)
|
||||
a, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetAppNamespace(), q.GetName())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting application: %w", err)
|
||||
}
|
||||
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -694,13 +731,13 @@ func (s *Server) ListResourceEvents(ctx context.Context, q *application.Applicat
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func (s *Server) validateAndUpdateApp(ctx context.Context, newApp *appv1.Application, merge bool, validate bool) (*appv1.Application, error) {
|
||||
func (s *Server) validateAndUpdateApp(ctx context.Context, newApp *appv1.Application, merge bool, validate bool, action string) (*appv1.Application, error) {
|
||||
s.projectLock.RLock(newApp.Spec.GetProject())
|
||||
defer s.projectLock.RUnlock(newApp.Spec.GetProject())
|
||||
|
||||
app, err := s.appclientset.ArgoprojV1alpha1().Applications(newApp.Namespace).Get(ctx, newApp.Name, metav1.GetOptions{})
|
||||
app, err := s.getApplicationEnforceRBACClient(ctx, action, newApp.Namespace, newApp.Name, "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting application: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = s.validateAndNormalizeApp(ctx, newApp, validate)
|
||||
@@ -807,7 +844,7 @@ func (s *Server) Update(ctx context.Context, q *application.ApplicationUpdateReq
|
||||
if q.Validate != nil {
|
||||
validate = *q.Validate
|
||||
}
|
||||
return s.validateAndUpdateApp(ctx, q.Application, false, validate)
|
||||
return s.validateAndUpdateApp(ctx, q.Application, false, validate, rbacpolicy.ActionUpdate)
|
||||
}
|
||||
|
||||
// UpdateSpec updates an application spec and filters out any invalid parameter overrides
|
||||
@@ -815,13 +852,8 @@ func (s *Server) UpdateSpec(ctx context.Context, q *application.ApplicationUpdat
|
||||
if q.GetSpec() == nil {
|
||||
return nil, fmt.Errorf("error updating application spec: spec is nil in request")
|
||||
}
|
||||
appName := q.GetName()
|
||||
appNs := s.appNamespaceOrDefault(q.GetAppNamespace())
|
||||
a, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Get(ctx, appName, metav1.GetOptions{})
|
||||
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionUpdate, q.GetAppNamespace(), q.GetName(), "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting application: %w", err)
|
||||
}
|
||||
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, a.RBACName(s.ns)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -830,7 +862,7 @@ func (s *Server) UpdateSpec(ctx context.Context, q *application.ApplicationUpdat
|
||||
if q.Validate != nil {
|
||||
validate = *q.Validate
|
||||
}
|
||||
a, err = s.validateAndUpdateApp(ctx, a, false, validate)
|
||||
a, err = s.validateAndUpdateApp(ctx, a, false, validate, rbacpolicy.ActionUpdate)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error validating and updating app: %w", err)
|
||||
}
|
||||
@@ -839,11 +871,9 @@ func (s *Server) UpdateSpec(ctx context.Context, q *application.ApplicationUpdat
|
||||
|
||||
// Patch patches an application
|
||||
func (s *Server) Patch(ctx context.Context, q *application.ApplicationPatchRequest) (*appv1.Application, error) {
|
||||
appName := q.GetName()
|
||||
appNs := s.appNamespaceOrDefault(q.GetAppNamespace())
|
||||
app, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Get(ctx, appName, metav1.GetOptions{})
|
||||
app, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, q.GetAppNamespace(), q.GetName(), "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting application: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, app.RBACName(s.ns)); err != nil {
|
||||
@@ -881,16 +911,16 @@ func (s *Server) Patch(ctx context.Context, q *application.ApplicationPatchReque
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error unmarshaling patched app: %w", err)
|
||||
}
|
||||
return s.validateAndUpdateApp(ctx, newApp, false, true)
|
||||
return s.validateAndUpdateApp(ctx, newApp, false, true, rbacpolicy.ActionUpdate)
|
||||
}
|
||||
|
||||
// Delete removes an application and all associated resources
|
||||
func (s *Server) Delete(ctx context.Context, q *application.ApplicationDeleteRequest) (*application.ApplicationResponse, error) {
|
||||
appName := q.GetName()
|
||||
appNs := s.appNamespaceOrDefault(q.GetAppNamespace())
|
||||
a, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Get(ctx, appName, metav1.GetOptions{})
|
||||
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, appNs, appName, "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting application: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.projectLock.RLock(a.Spec.Project)
|
||||
@@ -956,8 +986,8 @@ func (s *Server) Watch(q *application.ApplicationQuery, ws application.Applicati
|
||||
logCtx = logCtx.WithField("application", *q.Name)
|
||||
}
|
||||
projects := map[string]bool{}
|
||||
for i := range q.Projects {
|
||||
projects[q.Projects[i]] = true
|
||||
for _, project := range getProjectsFromApplicationQuery(*q) {
|
||||
projects[project] = true
|
||||
}
|
||||
claims := ws.Context().Value("claims")
|
||||
selector, err := labels.Parse(q.GetSelector())
|
||||
@@ -1034,7 +1064,9 @@ func (s *Server) validateAndNormalizeApp(ctx context.Context, app *appv1.Applica
|
||||
proj, err := argo.GetAppProject(app, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx)
|
||||
if err != nil {
|
||||
if apierr.IsNotFound(err) {
|
||||
return status.Errorf(codes.InvalidArgument, "application references project %s which does not exist", app.Spec.Project)
|
||||
// Offer no hint that the project does not exist.
|
||||
log.Warnf("User attempted to create/update application in non-existent project %q", app.Spec.Project)
|
||||
return permissionDeniedErr
|
||||
}
|
||||
return fmt.Errorf("error getting application's project: %w", err)
|
||||
}
|
||||
@@ -1138,22 +1170,16 @@ func (s *Server) getAppResources(ctx context.Context, a *appv1.Application) (*ap
|
||||
return s.cache.GetAppResourcesTree(a.InstanceName(s.ns), &tree)
|
||||
})
|
||||
if err != nil {
|
||||
return &tree, fmt.Errorf("error getting cached app state: %w", err)
|
||||
return &tree, fmt.Errorf("error getting cached app resource tree: %w", err)
|
||||
}
|
||||
return &tree, nil
|
||||
}
|
||||
|
||||
func (s *Server) getAppLiveResource(ctx context.Context, action string, q *application.ApplicationResourceRequest) (*appv1.ResourceNode, *rest.Config, *appv1.Application, error) {
|
||||
appName := q.GetName()
|
||||
appNs := s.appNamespaceOrDefault(q.GetAppNamespace())
|
||||
a, err := s.appLister.Applications(appNs).Get(appName)
|
||||
a, err := s.getApplicationEnforceRBACInformer(ctx, action, q.GetAppNamespace(), q.GetName())
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("error getting app by name: %w", err)
|
||||
}
|
||||
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, action, a.RBACName(s.ns)); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
tree, err := s.getAppResources(ctx, a)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("error getting app resources: %w", err)
|
||||
@@ -1173,7 +1199,7 @@ func (s *Server) getAppLiveResource(ctx context.Context, action string, q *appli
|
||||
func (s *Server) GetResource(ctx context.Context, q *application.ApplicationResourceRequest) (*application.ApplicationResourceResponse, error) {
|
||||
res, config, _, err := s.getAppLiveResource(ctx, rbacpolicy.ActionGet, q)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting app live resource: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// make sure to use specified resource version if provided
|
||||
@@ -1220,9 +1246,6 @@ func (s *Server) PatchResource(ctx context.Context, q *application.ApplicationRe
|
||||
}
|
||||
res, config, a, err := s.getAppLiveResource(ctx, rbacpolicy.ActionUpdate, resourceRequest)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting app live resource: %w", err)
|
||||
}
|
||||
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, a.RBACName(s.ns)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1234,6 +1257,9 @@ func (s *Server) PatchResource(ctx context.Context, q *application.ApplicationRe
|
||||
}
|
||||
return nil, fmt.Errorf("error patching resource: %w", err)
|
||||
}
|
||||
if manifest == nil {
|
||||
return nil, fmt.Errorf("failed to patch resource: manifest was nil")
|
||||
}
|
||||
manifest, err = replaceSecretValues(manifest)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error replacing secret values: %w", err)
|
||||
@@ -1262,9 +1288,6 @@ func (s *Server) DeleteResource(ctx context.Context, q *application.ApplicationR
|
||||
}
|
||||
res, config, a, err := s.getAppLiveResource(ctx, rbacpolicy.ActionDelete, resourceRequest)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting live resource for delete: %w", err)
|
||||
}
|
||||
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionDelete, a.RBACName(s.ns)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var deleteOption metav1.DeleteOptions
|
||||
@@ -1288,13 +1311,8 @@ func (s *Server) DeleteResource(ctx context.Context, q *application.ApplicationR
|
||||
}
|
||||
|
||||
func (s *Server) ResourceTree(ctx context.Context, q *application.ResourcesQuery) (*appv1.ApplicationTree, error) {
|
||||
appName := q.GetApplicationName()
|
||||
appNs := s.appNamespaceOrDefault(q.GetAppNamespace())
|
||||
a, err := s.appLister.Applications(appNs).Get(appName)
|
||||
a, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetAppNamespace(), q.GetApplicationName())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting application by name: %w", err)
|
||||
}
|
||||
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1302,14 +1320,8 @@ func (s *Server) ResourceTree(ctx context.Context, q *application.ResourcesQuery
|
||||
}
|
||||
|
||||
func (s *Server) WatchResourceTree(q *application.ResourcesQuery, ws application.ApplicationService_WatchResourceTreeServer) error {
|
||||
appName := q.GetApplicationName()
|
||||
appNs := s.appNamespaceOrDefault(q.GetAppNamespace())
|
||||
a, err := s.appLister.Applications(appNs).Get(appName)
|
||||
_, err := s.getApplicationEnforceRBACInformer(ws.Context(), rbacpolicy.ActionGet, q.GetAppNamespace(), q.GetApplicationName())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting application by name: %w", err)
|
||||
}
|
||||
|
||||
if err := s.enf.EnforceErr(ws.Context().Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1324,13 +1336,8 @@ func (s *Server) WatchResourceTree(q *application.ResourcesQuery, ws application
|
||||
}
|
||||
|
||||
func (s *Server) RevisionMetadata(ctx context.Context, q *application.RevisionMetadataQuery) (*appv1.RevisionMetadata, error) {
|
||||
appName := q.GetName()
|
||||
appNs := s.appNamespaceOrDefault(q.GetAppNamespace())
|
||||
a, err := s.appLister.Applications(appNs).Get(appName)
|
||||
a, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetAppNamespace(), q.GetName())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting app by name: %w", err)
|
||||
}
|
||||
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1365,14 +1372,9 @@ func isMatchingResource(q *application.ResourcesQuery, key kube.ResourceKey) boo
|
||||
}
|
||||
|
||||
func (s *Server) ManagedResources(ctx context.Context, q *application.ResourcesQuery) (*application.ManagedResourcesResponse, error) {
|
||||
appName := q.GetApplicationName()
|
||||
appNs := s.appNamespaceOrDefault(q.GetAppNamespace())
|
||||
a, err := s.appLister.Applications(appNs).Get(appName)
|
||||
a, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetAppNamespace(), q.GetApplicationName())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting application: %w", err)
|
||||
}
|
||||
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil {
|
||||
return nil, fmt.Errorf("error verifying rbac: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
items := make([]*appv1.ResourceDiff, 0)
|
||||
@@ -1380,7 +1382,7 @@ func (s *Server) ManagedResources(ctx context.Context, q *application.ResourcesQ
|
||||
return s.cache.GetAppManagedResources(a.InstanceName(s.ns), &items)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting cached app state: %w", err)
|
||||
return nil, fmt.Errorf("error getting cached app managed resources: %w", err)
|
||||
}
|
||||
res := &application.ManagedResourcesResponse{}
|
||||
for i := range items {
|
||||
@@ -1427,14 +1429,8 @@ func (s *Server) PodLogs(q *application.ApplicationPodLogsQuery, ws application.
|
||||
}
|
||||
}
|
||||
|
||||
appName := q.GetName()
|
||||
appNs := s.appNamespaceOrDefault(q.GetAppNamespace())
|
||||
a, err := s.appLister.Applications(appNs).Get(appName)
|
||||
a, err := s.getApplicationEnforceRBACInformer(ws.Context(), rbacpolicy.ActionGet, q.GetAppNamespace(), q.GetName())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting application by name: %w", err)
|
||||
}
|
||||
|
||||
if err := s.enf.EnforceErr(ws.Context().Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1625,12 +1621,9 @@ func isTheSelectedOne(currentNode *appv1.ResourceNode, q *application.Applicatio
|
||||
|
||||
// Sync syncs an application to its target state
|
||||
func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncRequest) (*appv1.Application, error) {
|
||||
appName := syncReq.GetName()
|
||||
appNs := s.appNamespaceOrDefault(syncReq.GetAppNamespace())
|
||||
appIf := s.appclientset.ArgoprojV1alpha1().Applications(appNs)
|
||||
a, err := appIf.Get(ctx, appName, metav1.GetOptions{})
|
||||
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, syncReq.GetAppNamespace(), syncReq.GetName(), "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting application by name: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
proj, err := argo.GetAppProject(a, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx)
|
||||
@@ -1717,6 +1710,9 @@ func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncR
|
||||
op.Retry = *retry
|
||||
}
|
||||
|
||||
appName := syncReq.GetName()
|
||||
appNs := s.appNamespaceOrDefault(syncReq.GetAppNamespace())
|
||||
appIf := s.appclientset.ArgoprojV1alpha1().Applications(appNs)
|
||||
a, err = argo.SetAppOperation(appIf, appName, &op)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error setting app operation: %w", err)
|
||||
@@ -1734,14 +1730,8 @@ func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncR
|
||||
}
|
||||
|
||||
func (s *Server) Rollback(ctx context.Context, rollbackReq *application.ApplicationRollbackRequest) (*appv1.Application, error) {
|
||||
appName := rollbackReq.GetName()
|
||||
appNs := s.appNamespaceOrDefault(rollbackReq.GetAppNamespace())
|
||||
appIf := s.appclientset.ArgoprojV1alpha1().Applications(appNs)
|
||||
a, err := appIf.Get(ctx, appName, metav1.GetOptions{})
|
||||
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionSync, rollbackReq.GetAppNamespace(), rollbackReq.GetName(), "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting application by name: %w", err)
|
||||
}
|
||||
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionSync, a.RBACName(s.ns)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1787,6 +1777,9 @@ func (s *Server) Rollback(ctx context.Context, rollbackReq *application.Applicat
|
||||
},
|
||||
InitiatedBy: appv1.OperationInitiator{Username: session.Username(ctx)},
|
||||
}
|
||||
appName := rollbackReq.GetName()
|
||||
appNs := s.appNamespaceOrDefault(rollbackReq.GetAppNamespace())
|
||||
appIf := s.appclientset.ArgoprojV1alpha1().Applications(appNs)
|
||||
a, err = argo.SetAppOperation(appIf, appName, &op)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error setting app operation: %w", err)
|
||||
@@ -1796,24 +1789,9 @@ func (s *Server) Rollback(ctx context.Context, rollbackReq *application.Applicat
|
||||
}
|
||||
|
||||
func (s *Server) ListLinks(ctx context.Context, req *application.ListAppLinksRequest) (*application.LinksResponse, error) {
|
||||
appName := req.GetName()
|
||||
appNs := s.appNamespaceOrDefault(req.GetNamespace())
|
||||
|
||||
a, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Get(ctx, appName, metav1.GetOptions{})
|
||||
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionSync, req.GetNamespace(), req.GetName(), "")
|
||||
if err != nil {
|
||||
log.WithFields(map[string]interface{}{
|
||||
"application": appName,
|
||||
"ns": appNs,
|
||||
}).Errorf("failed to get application, error=%v", err.Error())
|
||||
return nil, fmt.Errorf("error getting application")
|
||||
}
|
||||
|
||||
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil {
|
||||
log.WithFields(map[string]interface{}{
|
||||
"application": appName,
|
||||
"ns": appNs,
|
||||
}).Warnf("unauthorized access to app, error=%v", err.Error())
|
||||
return nil, fmt.Errorf("error getting application")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
obj, err := kube.ToUnstructured(a)
|
||||
@@ -1895,11 +1873,8 @@ func (s *Server) resolveRevision(ctx context.Context, app *appv1.Application, sy
|
||||
func (s *Server) TerminateOperation(ctx context.Context, termOpReq *application.OperationTerminateRequest) (*application.OperationTerminateResponse, error) {
|
||||
appName := termOpReq.GetName()
|
||||
appNs := s.appNamespaceOrDefault(termOpReq.GetAppNamespace())
|
||||
a, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Get(ctx, appName, metav1.GetOptions{})
|
||||
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionSync, appNs, appName, "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting application by name: %w", err)
|
||||
}
|
||||
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionSync, a.RBACName(s.ns)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1971,10 +1946,9 @@ func (s *Server) ListResourceActions(ctx context.Context, q *application.Applica
|
||||
|
||||
func (s *Server) getUnstructuredLiveResourceOrApp(ctx context.Context, rbacRequest string, q *application.ApplicationResourceRequest) (obj *unstructured.Unstructured, res *appv1.ResourceNode, app *appv1.Application, config *rest.Config, err error) {
|
||||
if q.GetKind() == "Application" && q.GetGroup() == "argoproj.io" && q.GetName() == q.GetResourceName() {
|
||||
namespace := s.appNamespaceOrDefault(q.GetAppNamespace())
|
||||
app, err = s.appLister.Applications(namespace).Get(q.GetName())
|
||||
app, err = s.getApplicationEnforceRBACInformer(ctx, rbacRequest, q.GetAppNamespace(), q.GetName())
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, fmt.Errorf("error getting app by name: %w", err)
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
if err = s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacRequest, app.RBACName(s.ns)); err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
@@ -1987,7 +1961,7 @@ func (s *Server) getUnstructuredLiveResourceOrApp(ctx context.Context, rbacReque
|
||||
} else {
|
||||
res, config, app, err = s.getAppLiveResource(ctx, rbacRequest, q)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, fmt.Errorf("error getting app live resource: %w", err)
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
obj, err = s.kubectl.GetResource(ctx, config, res.GroupKindVersion(), res.Name, res.Namespace)
|
||||
}
|
||||
@@ -2157,15 +2131,8 @@ func (s *Server) plugins() ([]*appv1.ConfigManagementPlugin, error) {
|
||||
}
|
||||
|
||||
func (s *Server) GetApplicationSyncWindows(ctx context.Context, q *application.ApplicationSyncWindowsQuery) (*application.ApplicationSyncWindowsResponse, error) {
|
||||
appName := q.GetName()
|
||||
appNs := s.appNamespaceOrDefault(q.GetAppNamespace())
|
||||
appIf := s.appclientset.ArgoprojV1alpha1().Applications(appNs)
|
||||
a, err := appIf.Get(ctx, appName, metav1.GetOptions{})
|
||||
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, q.GetAppNamespace(), q.GetName(), "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting application by name: %w", err)
|
||||
}
|
||||
|
||||
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -2245,3 +2212,12 @@ func (s *Server) appNamespaceOrDefault(appNs string) string {
|
||||
func (s *Server) isNamespaceEnabled(namespace string) bool {
|
||||
return security.IsNamespaceEnabled(namespace, s.ns, s.enabledNamespaces)
|
||||
}
|
||||
|
||||
// getProjectFromApplicationQuery gets the project names from a query. If the legacy "project" field was specified, use
|
||||
// that. Otherwise, use the newer "projects" field.
|
||||
func getProjectsFromApplicationQuery(q application.ApplicationQuery) []string {
|
||||
if q.Project != nil {
|
||||
return q.Project
|
||||
}
|
||||
return q.Projects
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ message ApplicationQuery {
|
||||
optional string repo = 6;
|
||||
// the application's namespace
|
||||
optional string appNamespace = 7;
|
||||
// the project names to restrict returned list applications (legacy name for backwards-compatibility)
|
||||
repeated string project = 8;
|
||||
}
|
||||
|
||||
message NodeQuery {
|
||||
|
||||
@@ -4,12 +4,15 @@ import (
|
||||
"context"
|
||||
coreerrors "errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
synccommon "github.com/argoproj/gitops-engine/pkg/sync/common"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube/kubetest"
|
||||
"github.com/argoproj/pkg/sync"
|
||||
"github.com/ghodss/yaml"
|
||||
@@ -18,13 +21,17 @@ import (
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
k8sappsv1 "k8s.io/api/apps/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
"k8s.io/client-go/rest"
|
||||
kubetesting "k8s.io/client-go/testing"
|
||||
k8scache "k8s.io/client-go/tools/cache"
|
||||
"k8s.io/utils/pointer"
|
||||
@@ -36,6 +43,7 @@ import (
|
||||
appinformer "github.com/argoproj/argo-cd/v2/pkg/client/informers/externalversions"
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient/mocks"
|
||||
appmocks "github.com/argoproj/argo-cd/v2/server/application/mocks"
|
||||
servercache "github.com/argoproj/argo-cd/v2/server/cache"
|
||||
"github.com/argoproj/argo-cd/v2/server/rbacpolicy"
|
||||
"github.com/argoproj/argo-cd/v2/test"
|
||||
@@ -98,6 +106,11 @@ func fakeRepoServerClient(isHelm bool) *mocks.RepoServerServiceClient {
|
||||
mockRepoServiceClient.On("GenerateManifest", mock.Anything, mock.Anything).Return(&apiclient.ManifestResponse{}, nil)
|
||||
mockRepoServiceClient.On("GetAppDetails", mock.Anything, mock.Anything).Return(&apiclient.RepoAppDetailsResponse{}, nil)
|
||||
mockRepoServiceClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil)
|
||||
mockRepoServiceClient.On("GetRevisionMetadata", mock.Anything, mock.Anything).Return(&appsv1.RevisionMetadata{}, nil)
|
||||
mockWithFilesClient := &mocks.RepoServerService_GenerateManifestWithFilesClient{}
|
||||
mockWithFilesClient.On("Send", mock.Anything).Return(nil)
|
||||
mockWithFilesClient.On("CloseAndRecv").Return(&apiclient.ManifestResponse{}, nil)
|
||||
mockRepoServiceClient.On("GenerateManifestWithFiles", mock.Anything, mock.Anything).Return(mockWithFilesClient, nil)
|
||||
|
||||
if isHelm {
|
||||
mockRepoServiceClient.On("ResolveRevision", mock.Anything, mock.Anything).Return(fakeResolveRevesionResponseHelm(), nil)
|
||||
@@ -109,15 +122,15 @@ func fakeRepoServerClient(isHelm bool) *mocks.RepoServerServiceClient {
|
||||
}
|
||||
|
||||
// return an ApplicationServiceServer which returns fake data
|
||||
func newTestAppServer(objects ...runtime.Object) *Server {
|
||||
func newTestAppServer(t *testing.T, objects ...runtime.Object) *Server {
|
||||
f := func(enf *rbac.Enforcer) {
|
||||
_ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
|
||||
enf.SetDefaultRole("role:admin")
|
||||
}
|
||||
return newTestAppServerWithEnforcerConfigure(f, objects...)
|
||||
return newTestAppServerWithEnforcerConfigure(f, t, objects...)
|
||||
}
|
||||
|
||||
func newTestAppServerWithEnforcerConfigure(f func(*rbac.Enforcer), objects ...runtime.Object) *Server {
|
||||
func newTestAppServerWithEnforcerConfigure(f func(*rbac.Enforcer), t *testing.T, objects ...runtime.Object) *Server {
|
||||
kubeclientset := fake.NewSimpleClientset(&v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: testNamespace,
|
||||
@@ -202,15 +215,83 @@ func newTestAppServerWithEnforcerConfigure(f func(*rbac.Enforcer), objects ...ru
|
||||
panic("Timed out waiting for caches to sync")
|
||||
}
|
||||
|
||||
broadcaster := new(appmocks.Broadcaster)
|
||||
broadcaster.On("Subscribe", mock.Anything, mock.Anything).Return(func() {}).Run(func(args mock.Arguments) {
|
||||
// Simulate the broadcaster notifying the subscriber of an application update.
|
||||
// The second parameter to Subscribe is filters. For the purposes of tests, we ignore the filters. Future tests
|
||||
// might require implementing those.
|
||||
go func() {
|
||||
events := args.Get(0).(chan *appsv1.ApplicationWatchEvent)
|
||||
for _, obj := range objects {
|
||||
app, ok := obj.(*appsv1.Application)
|
||||
if ok {
|
||||
oldVersion, err := strconv.Atoi(app.ResourceVersion)
|
||||
if err != nil {
|
||||
oldVersion = 0
|
||||
}
|
||||
clonedApp := app.DeepCopy()
|
||||
clonedApp.ResourceVersion = fmt.Sprintf("%d", oldVersion+1)
|
||||
events <- &appsv1.ApplicationWatchEvent{Type: watch.Added, Application: *clonedApp}
|
||||
}
|
||||
}
|
||||
}()
|
||||
})
|
||||
broadcaster.On("OnAdd", mock.Anything).Return()
|
||||
broadcaster.On("OnUpdate", mock.Anything, mock.Anything).Return()
|
||||
broadcaster.On("OnDelete", mock.Anything).Return()
|
||||
|
||||
appStateCache := appstate.NewCache(cache.NewCache(cache.NewInMemoryCache(time.Hour)), time.Hour)
|
||||
// pre-populate the app cache
|
||||
for _, obj := range objects {
|
||||
app, ok := obj.(*appsv1.Application)
|
||||
if ok {
|
||||
err := appStateCache.SetAppManagedResources(app.Name, []*appsv1.ResourceDiff{})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Pre-populate the resource tree based on the app's resources.
|
||||
nodes := make([]appsv1.ResourceNode, len(app.Status.Resources))
|
||||
for i, res := range app.Status.Resources {
|
||||
nodes[i] = appsv1.ResourceNode{
|
||||
ResourceRef: appsv1.ResourceRef{
|
||||
Group: res.Group,
|
||||
Kind: res.Kind,
|
||||
Version: res.Version,
|
||||
Name: res.Name,
|
||||
Namespace: res.Namespace,
|
||||
UID: "fake",
|
||||
},
|
||||
}
|
||||
}
|
||||
err = appStateCache.SetAppResourcesTree(app.Name, &appsv1.ApplicationTree{
|
||||
Nodes: nodes,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
appCache := servercache.NewCache(appStateCache, time.Hour, time.Hour, time.Hour)
|
||||
|
||||
kubectl := &kubetest.MockKubectlCmd{}
|
||||
kubectl = kubectl.WithGetResourceFunc(func(_ context.Context, _ *rest.Config, gvk schema.GroupVersionKind, name string, namespace string) (*unstructured.Unstructured, error) {
|
||||
for _, obj := range objects {
|
||||
if obj.GetObjectKind().GroupVersionKind().GroupKind() == gvk.GroupKind() {
|
||||
if obj, ok := obj.(*unstructured.Unstructured); ok && obj.GetName() == name && obj.GetNamespace() == namespace {
|
||||
return obj, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
server, _ := NewServer(
|
||||
testNamespace,
|
||||
kubeclientset,
|
||||
fakeAppsClientset,
|
||||
factory.Argoproj().V1alpha1().Applications().Lister(),
|
||||
appInformer,
|
||||
broadcaster,
|
||||
mockRepoClient,
|
||||
nil,
|
||||
&kubetest.MockKubectlCmd{},
|
||||
appCache,
|
||||
kubectl,
|
||||
db,
|
||||
enforcer,
|
||||
sync.NewKeyLock(),
|
||||
@@ -301,8 +382,428 @@ func createTestApp(testApp string, opts ...func(app *appsv1.Application)) *appsv
|
||||
return &app
|
||||
}
|
||||
|
||||
type TestServerStream struct {
|
||||
ctx context.Context
|
||||
appName string
|
||||
headerSent bool
|
||||
}
|
||||
|
||||
func (t *TestServerStream) SetHeader(metadata.MD) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TestServerStream) SendHeader(metadata.MD) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TestServerStream) SetTrailer(metadata.MD) {
|
||||
return
|
||||
}
|
||||
|
||||
func (t *TestServerStream) Context() context.Context {
|
||||
return t.ctx
|
||||
}
|
||||
|
||||
func (t *TestServerStream) SendMsg(m interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TestServerStream) RecvMsg(m interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TestServerStream) SendAndClose(r *apiclient.ManifestResponse) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TestServerStream) Recv() (*application.ApplicationManifestQueryWithFilesWrapper, error) {
|
||||
if !t.headerSent {
|
||||
t.headerSent = true
|
||||
return &application.ApplicationManifestQueryWithFilesWrapper{Part: &application.ApplicationManifestQueryWithFilesWrapper_Query{
|
||||
Query: &application.ApplicationManifestQueryWithFiles{
|
||||
Name: pointer.String(t.appName),
|
||||
Checksum: pointer.String(""),
|
||||
},
|
||||
}}, nil
|
||||
}
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
func (t *TestServerStream) ServerStream() TestServerStream {
|
||||
return TestServerStream{}
|
||||
}
|
||||
|
||||
type TestResourceTreeServer struct {
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func (t *TestResourceTreeServer) Send(tree *appsv1.ApplicationTree) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TestResourceTreeServer) SetHeader(metadata.MD) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TestResourceTreeServer) SendHeader(metadata.MD) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TestResourceTreeServer) SetTrailer(metadata.MD) {
|
||||
return
|
||||
}
|
||||
|
||||
func (t *TestResourceTreeServer) Context() context.Context {
|
||||
return t.ctx
|
||||
}
|
||||
|
||||
func (t *TestResourceTreeServer) SendMsg(m interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TestResourceTreeServer) RecvMsg(m interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type TestPodLogsServer struct {
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func (t *TestPodLogsServer) Send(log *application.LogEntry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TestPodLogsServer) SetHeader(metadata.MD) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TestPodLogsServer) SendHeader(metadata.MD) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TestPodLogsServer) SetTrailer(metadata.MD) {
|
||||
return
|
||||
}
|
||||
|
||||
func (t *TestPodLogsServer) Context() context.Context {
|
||||
return t.ctx
|
||||
}
|
||||
|
||||
func (t *TestPodLogsServer) SendMsg(m interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TestPodLogsServer) RecvMsg(m interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestNoAppEnumeration(t *testing.T) {
|
||||
// This test ensures that malicious users can't infer the existence or non-existence of Applications by inspecting
|
||||
// error messages. The errors for "app does not exist" must be the same as errors for "you aren't allowed to
|
||||
// interact with this app."
|
||||
|
||||
// These tests are only important on API calls where the full app RBAC name (project, namespace, and name) is _not_
|
||||
// known based on the query parameters. For example, the Create call cannot leak existence of Applications, because
|
||||
// the Application's project, namespace, and name are all specified in the API call. The call can be rejected
|
||||
// immediately if the user does not have access. But the Delete endpoint may be called with just the Application
|
||||
// name. So we cannot return a different error message for "does not exist" and "you don't have delete permissions,"
|
||||
// because the user could infer that the Application exists if they do not get the "does not exist" message. For
|
||||
// endpoints that do not require the full RBAC name, we must return a generic "permission denied" for both "does not
|
||||
// exist" and "no access."
|
||||
|
||||
f := func(enf *rbac.Enforcer) {
|
||||
_ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
|
||||
enf.SetDefaultRole("role:none")
|
||||
}
|
||||
deployment := k8sappsv1.Deployment{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
Namespace: "test",
|
||||
},
|
||||
}
|
||||
testApp := newTestApp(func(app *appsv1.Application) {
|
||||
app.Name = "test"
|
||||
app.Status.Resources = []appsv1.ResourceStatus{
|
||||
{
|
||||
Group: deployment.GroupVersionKind().Group,
|
||||
Kind: deployment.GroupVersionKind().Kind,
|
||||
Version: deployment.GroupVersionKind().Version,
|
||||
Name: deployment.Name,
|
||||
Namespace: deployment.Namespace,
|
||||
Status: "Synced",
|
||||
},
|
||||
}
|
||||
app.Status.History = []appsv1.RevisionHistory{
|
||||
{
|
||||
ID: 0,
|
||||
Source: appsv1.ApplicationSource{
|
||||
TargetRevision: "something-old",
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
testDeployment := kube.MustToUnstructured(&deployment)
|
||||
appServer := newTestAppServerWithEnforcerConfigure(f, t, testApp, testDeployment)
|
||||
|
||||
noRoleCtx := context.Background()
|
||||
adminCtx := context.WithValue(noRoleCtx, "claims", &jwt.MapClaims{"groups": []string{"admin"}})
|
||||
|
||||
t.Run("Get", func(t *testing.T) {
|
||||
_, err := appServer.Get(adminCtx, &application.ApplicationQuery{Name: pointer.String("test")})
|
||||
assert.NoError(t, err)
|
||||
_, err = appServer.Get(noRoleCtx, &application.ApplicationQuery{Name: pointer.String("test")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
_, err = appServer.Get(adminCtx, &application.ApplicationQuery{Name: pointer.String("doest-not-exist")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
})
|
||||
|
||||
t.Run("GetManifests", func(t *testing.T) {
|
||||
_, err := appServer.GetManifests(adminCtx, &application.ApplicationManifestQuery{Name: pointer.String("test")})
|
||||
assert.NoError(t, err)
|
||||
_, err = appServer.GetManifests(noRoleCtx, &application.ApplicationManifestQuery{Name: pointer.String("test")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
_, err = appServer.GetManifests(adminCtx, &application.ApplicationManifestQuery{Name: pointer.String("doest-not-exist")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
})
|
||||
|
||||
t.Run("ListResourceEvents", func(t *testing.T) {
|
||||
_, err := appServer.ListResourceEvents(adminCtx, &application.ApplicationResourceEventsQuery{Name: pointer.String("test")})
|
||||
assert.NoError(t, err)
|
||||
_, err = appServer.ListResourceEvents(noRoleCtx, &application.ApplicationResourceEventsQuery{Name: pointer.String("test")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
_, err = appServer.ListResourceEvents(adminCtx, &application.ApplicationResourceEventsQuery{Name: pointer.String("doest-not-exist")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
})
|
||||
|
||||
t.Run("UpdateSpec", func(t *testing.T) {
|
||||
_, err := appServer.UpdateSpec(adminCtx, &application.ApplicationUpdateSpecRequest{Name: pointer.String("test"), Spec: &appsv1.ApplicationSpec{
|
||||
Destination: appsv1.ApplicationDestination{Namespace: "default", Server: "https://cluster-api.com"},
|
||||
Source: &appsv1.ApplicationSource{RepoURL: "https://some-fake-source", Path: "."},
|
||||
}})
|
||||
assert.NoError(t, err)
|
||||
_, err = appServer.UpdateSpec(noRoleCtx, &application.ApplicationUpdateSpecRequest{Name: pointer.String("test"), Spec: &appsv1.ApplicationSpec{
|
||||
Destination: appsv1.ApplicationDestination{Namespace: "default", Server: "https://cluster-api.com"},
|
||||
Source: &appsv1.ApplicationSource{RepoURL: "https://some-fake-source", Path: "."},
|
||||
}})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
_, err = appServer.UpdateSpec(adminCtx, &application.ApplicationUpdateSpecRequest{Name: pointer.String("doest-not-exist"), Spec: &appsv1.ApplicationSpec{
|
||||
Destination: appsv1.ApplicationDestination{Namespace: "default", Server: "https://cluster-api.com"},
|
||||
Source: &appsv1.ApplicationSource{RepoURL: "https://some-fake-source", Path: "."},
|
||||
}})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
})
|
||||
|
||||
t.Run("Patch", func(t *testing.T) {
|
||||
_, err := appServer.Patch(adminCtx, &application.ApplicationPatchRequest{Name: pointer.String("test"), Patch: pointer.String(`[{"op": "replace", "path": "/spec/source/path", "value": "foo"}]`)})
|
||||
assert.NoError(t, err)
|
||||
_, err = appServer.Patch(noRoleCtx, &application.ApplicationPatchRequest{Name: pointer.String("test"), Patch: pointer.String(`[{"op": "replace", "path": "/spec/source/path", "value": "foo"}]`)})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
_, err = appServer.Patch(adminCtx, &application.ApplicationPatchRequest{Name: pointer.String("doest-not-exist")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
})
|
||||
|
||||
t.Run("GetResource", func(t *testing.T) {
|
||||
_, err := appServer.GetResource(adminCtx, &application.ApplicationResourceRequest{Name: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test")})
|
||||
assert.NoError(t, err)
|
||||
_, err = appServer.GetResource(noRoleCtx, &application.ApplicationResourceRequest{Name: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
_, err = appServer.GetResource(adminCtx, &application.ApplicationResourceRequest{Name: pointer.String("doest-not-exist"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
})
|
||||
|
||||
t.Run("PatchResource", func(t *testing.T) {
|
||||
_, err := appServer.PatchResource(adminCtx, &application.ApplicationResourcePatchRequest{Name: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test"), Patch: pointer.String(`[{"op": "replace", "path": "/spec/replicas", "value": 3}]`)})
|
||||
// This will always throw an error, because the kubectl mock for PatchResource is hard-coded to return nil.
|
||||
// The best we can do is to confirm we get past the permission check.
|
||||
assert.NotEqual(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
_, err = appServer.PatchResource(noRoleCtx, &application.ApplicationResourcePatchRequest{Name: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test"), Patch: pointer.String(`[{"op": "replace", "path": "/spec/replicas", "value": 3}]`)})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
_, err = appServer.PatchResource(adminCtx, &application.ApplicationResourcePatchRequest{Name: pointer.String("doest-not-exist"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test"), Patch: pointer.String(`[{"op": "replace", "path": "/spec/replicas", "value": 3}]`)})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
})
|
||||
|
||||
t.Run("DeleteResource", func(t *testing.T) {
|
||||
_, err := appServer.DeleteResource(adminCtx, &application.ApplicationResourceDeleteRequest{Name: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test")})
|
||||
assert.NoError(t, err)
|
||||
_, err = appServer.DeleteResource(noRoleCtx, &application.ApplicationResourceDeleteRequest{Name: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
_, err = appServer.DeleteResource(adminCtx, &application.ApplicationResourceDeleteRequest{Name: pointer.String("doest-not-exist"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
})
|
||||
|
||||
t.Run("ResourceTree", func(t *testing.T) {
|
||||
_, err := appServer.ResourceTree(adminCtx, &application.ResourcesQuery{ApplicationName: pointer.String("test")})
|
||||
assert.NoError(t, err)
|
||||
_, err = appServer.ResourceTree(noRoleCtx, &application.ResourcesQuery{ApplicationName: pointer.String("test")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
_, err = appServer.ResourceTree(adminCtx, &application.ResourcesQuery{ApplicationName: pointer.String("doest-not-exist")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
})
|
||||
|
||||
t.Run("RevisionMetadata", func(t *testing.T) {
|
||||
_, err := appServer.RevisionMetadata(adminCtx, &application.RevisionMetadataQuery{Name: pointer.String("test")})
|
||||
assert.NoError(t, err)
|
||||
_, err = appServer.RevisionMetadata(noRoleCtx, &application.RevisionMetadataQuery{Name: pointer.String("test")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
_, err = appServer.RevisionMetadata(adminCtx, &application.RevisionMetadataQuery{Name: pointer.String("doest-not-exist")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
})
|
||||
|
||||
t.Run("ManagedResources", func(t *testing.T) {
|
||||
_, err := appServer.ManagedResources(adminCtx, &application.ResourcesQuery{ApplicationName: pointer.String("test")})
|
||||
assert.NoError(t, err)
|
||||
_, err = appServer.ManagedResources(noRoleCtx, &application.ResourcesQuery{ApplicationName: pointer.String("test")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
_, err = appServer.ManagedResources(adminCtx, &application.ResourcesQuery{ApplicationName: pointer.String("doest-not-exist")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
})
|
||||
|
||||
t.Run("Sync", func(t *testing.T) {
|
||||
_, err := appServer.Sync(adminCtx, &application.ApplicationSyncRequest{Name: pointer.String("test")})
|
||||
assert.NoError(t, err)
|
||||
_, err = appServer.Sync(noRoleCtx, &application.ApplicationSyncRequest{Name: pointer.String("test")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
_, err = appServer.Sync(adminCtx, &application.ApplicationSyncRequest{Name: pointer.String("doest-not-exist")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
})
|
||||
|
||||
t.Run("TerminateOperation", func(t *testing.T) {
|
||||
// The sync operation is already started from the previous test. We just need to set the field that the
|
||||
// controller would set if this were an actual Argo CD environment.
|
||||
setSyncRunningOperationState(t, appServer)
|
||||
_, err := appServer.TerminateOperation(adminCtx, &application.OperationTerminateRequest{Name: pointer.String("test")})
|
||||
assert.NoError(t, err)
|
||||
_, err = appServer.TerminateOperation(noRoleCtx, &application.OperationTerminateRequest{Name: pointer.String("test")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
_, err = appServer.TerminateOperation(adminCtx, &application.OperationTerminateRequest{Name: pointer.String("doest-not-exist")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
})
|
||||
|
||||
t.Run("Rollback", func(t *testing.T) {
|
||||
unsetSyncRunningOperationState(t, appServer)
|
||||
_, err := appServer.Rollback(adminCtx, &application.ApplicationRollbackRequest{Name: pointer.String("test")})
|
||||
assert.NoError(t, err)
|
||||
_, err = appServer.Rollback(noRoleCtx, &application.ApplicationRollbackRequest{Name: pointer.String("test")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
_, err = appServer.Rollback(adminCtx, &application.ApplicationRollbackRequest{Name: pointer.String("doest-not-exist")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
})
|
||||
|
||||
t.Run("ListResourceActions", func(t *testing.T) {
|
||||
_, err := appServer.ListResourceActions(adminCtx, &application.ApplicationResourceRequest{Name: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test")})
|
||||
assert.NoError(t, err)
|
||||
_, err = appServer.ListResourceActions(noRoleCtx, &application.ApplicationResourceRequest{Name: pointer.String("test")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
_, err = appServer.ListResourceActions(noRoleCtx, &application.ApplicationResourceRequest{Group: pointer.String("argoproj.io"), Kind: pointer.String("Application"), Name: pointer.String("test")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
_, err = appServer.ListResourceActions(adminCtx, &application.ApplicationResourceRequest{Name: pointer.String("doest-not-exist")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
})
|
||||
|
||||
t.Run("RunResourceAction", func(t *testing.T) {
|
||||
_, err := appServer.RunResourceAction(adminCtx, &application.ResourceActionRunRequest{Name: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test"), Action: pointer.String("restart")})
|
||||
assert.NoError(t, err)
|
||||
_, err = appServer.RunResourceAction(noRoleCtx, &application.ResourceActionRunRequest{Name: pointer.String("test")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
_, err = appServer.RunResourceAction(noRoleCtx, &application.ResourceActionRunRequest{Group: pointer.String("argoproj.io"), Kind: pointer.String("Application"), Name: pointer.String("test")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
_, err = appServer.RunResourceAction(adminCtx, &application.ResourceActionRunRequest{Name: pointer.String("doest-not-exist")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
})
|
||||
|
||||
t.Run("GetApplicationSyncWindows", func(t *testing.T) {
|
||||
_, err := appServer.GetApplicationSyncWindows(adminCtx, &application.ApplicationSyncWindowsQuery{Name: pointer.String("test")})
|
||||
assert.NoError(t, err)
|
||||
_, err = appServer.GetApplicationSyncWindows(noRoleCtx, &application.ApplicationSyncWindowsQuery{Name: pointer.String("test")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
_, err = appServer.GetApplicationSyncWindows(adminCtx, &application.ApplicationSyncWindowsQuery{Name: pointer.String("doest-not-exist")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
})
|
||||
|
||||
t.Run("GetManifestsWithFiles", func(t *testing.T) {
|
||||
err := appServer.GetManifestsWithFiles(&TestServerStream{ctx: adminCtx, appName: "test"})
|
||||
assert.NoError(t, err)
|
||||
err = appServer.GetManifestsWithFiles(&TestServerStream{ctx: noRoleCtx, appName: "test"})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
err = appServer.GetManifestsWithFiles(&TestServerStream{ctx: adminCtx, appName: "does-not-exist"})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
})
|
||||
|
||||
t.Run("WatchResourceTree", func(t *testing.T) {
|
||||
err := appServer.WatchResourceTree(&application.ResourcesQuery{ApplicationName: pointer.String("test")}, &TestResourceTreeServer{ctx: adminCtx})
|
||||
assert.NoError(t, err)
|
||||
err = appServer.WatchResourceTree(&application.ResourcesQuery{ApplicationName: pointer.String("test")}, &TestResourceTreeServer{ctx: noRoleCtx})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
err = appServer.WatchResourceTree(&application.ResourcesQuery{ApplicationName: pointer.String("does-not-exist")}, &TestResourceTreeServer{ctx: adminCtx})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
})
|
||||
|
||||
t.Run("PodLogs", func(t *testing.T) {
|
||||
err := appServer.PodLogs(&application.ApplicationPodLogsQuery{Name: pointer.String("test")}, &TestPodLogsServer{ctx: adminCtx})
|
||||
assert.NoError(t, err)
|
||||
err = appServer.PodLogs(&application.ApplicationPodLogsQuery{Name: pointer.String("test")}, &TestPodLogsServer{ctx: noRoleCtx})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
err = appServer.PodLogs(&application.ApplicationPodLogsQuery{Name: pointer.String("does-not-exist")}, &TestPodLogsServer{ctx: adminCtx})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
})
|
||||
|
||||
t.Run("ListLinks", func(t *testing.T) {
|
||||
_, err := appServer.ListLinks(adminCtx, &application.ListAppLinksRequest{Name: pointer.String("test")})
|
||||
assert.NoError(t, err)
|
||||
_, err = appServer.ListLinks(noRoleCtx, &application.ListAppLinksRequest{Name: pointer.String("test")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
_, err = appServer.ListLinks(adminCtx, &application.ListAppLinksRequest{Name: pointer.String("does-not-exist")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
})
|
||||
|
||||
t.Run("ListResourceLinks", func(t *testing.T) {
|
||||
_, err := appServer.ListResourceLinks(adminCtx, &application.ApplicationResourceRequest{Name: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test")})
|
||||
assert.NoError(t, err)
|
||||
_, err = appServer.ListResourceLinks(noRoleCtx, &application.ApplicationResourceRequest{Name: pointer.String("test"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
_, err = appServer.ListResourceLinks(adminCtx, &application.ApplicationResourceRequest{Name: pointer.String("does-not-exist"), ResourceName: pointer.String("test"), Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Namespace: pointer.String("test")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
})
|
||||
|
||||
// Do this last so other stuff doesn't fail.
|
||||
t.Run("Delete", func(t *testing.T) {
|
||||
_, err := appServer.Delete(adminCtx, &application.ApplicationDeleteRequest{Name: pointer.String("test")})
|
||||
assert.NoError(t, err)
|
||||
_, err = appServer.Delete(noRoleCtx, &application.ApplicationDeleteRequest{Name: pointer.String("test")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
_, err = appServer.Delete(adminCtx, &application.ApplicationDeleteRequest{Name: pointer.String("doest-not-exist")})
|
||||
assert.Equal(t, permissionDeniedErr.Error(), err.Error(), "error message must be _only_ the permission error, to avoid leaking information about app existence")
|
||||
})
|
||||
}
|
||||
|
||||
// setSyncRunningOperationState simulates starting a sync operation on the given app.
|
||||
func setSyncRunningOperationState(t *testing.T, appServer *Server) {
|
||||
appIf := appServer.appclientset.ArgoprojV1alpha1().Applications("default")
|
||||
app, err := appIf.Get(context.Background(), "test", metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
// This sets the status that would be set by the controller usually.
|
||||
app.Status.OperationState = &appsv1.OperationState{Phase: synccommon.OperationRunning, Operation: appsv1.Operation{Sync: &appsv1.SyncOperation{}}}
|
||||
_, err = appIf.Update(context.Background(), app, metav1.UpdateOptions{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// unsetSyncRunningOperationState simulates finishing a sync operation on the given app.
|
||||
func unsetSyncRunningOperationState(t *testing.T, appServer *Server) {
|
||||
appIf := appServer.appclientset.ArgoprojV1alpha1().Applications("default")
|
||||
app, err := appIf.Get(context.Background(), "test", metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
app.Operation = nil
|
||||
app.Status.OperationState = nil
|
||||
_, err = appIf.Update(context.Background(), app, metav1.UpdateOptions{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestListAppsInNamespaceWithLabels(t *testing.T) {
|
||||
appServer := newTestAppServer(newTestApp(func(app *appsv1.Application) {
|
||||
appServer := newTestAppServer(t, newTestApp(func(app *appsv1.Application) {
|
||||
app.Name = "App1"
|
||||
app.ObjectMeta.Namespace = "test-namespace"
|
||||
app.SetLabels(map[string]string{"key1": "value1", "key2": "value1"})
|
||||
@@ -323,7 +824,7 @@ func TestListAppsInNamespaceWithLabels(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListAppsInDefaultNSWithLabels(t *testing.T) {
|
||||
appServer := newTestAppServer(newTestApp(func(app *appsv1.Application) {
|
||||
appServer := newTestAppServer(t, newTestApp(func(app *appsv1.Application) {
|
||||
app.Name = "App1"
|
||||
app.SetLabels(map[string]string{"key1": "value1", "key2": "value1"})
|
||||
}), newTestApp(func(app *appsv1.Application) {
|
||||
@@ -401,8 +902,59 @@ func testListAppsWithLabels(t *testing.T, appQuery application.ApplicationQuery,
|
||||
}
|
||||
}
|
||||
|
||||
func TestListAppWithProjects(t *testing.T) {
|
||||
appServer := newTestAppServer(t, newTestApp(func(app *appsv1.Application) {
|
||||
app.Name = "App1"
|
||||
app.Spec.Project = "test-project1"
|
||||
}), newTestApp(func(app *appsv1.Application) {
|
||||
app.Name = "App2"
|
||||
app.Spec.Project = "test-project2"
|
||||
}), newTestApp(func(app *appsv1.Application) {
|
||||
app.Name = "App3"
|
||||
app.Spec.Project = "test-project3"
|
||||
}))
|
||||
|
||||
t.Run("List all apps", func(t *testing.T) {
|
||||
appQuery := application.ApplicationQuery{}
|
||||
appList, err := appServer.List(context.Background(), &appQuery)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, appList.Items, 3)
|
||||
})
|
||||
|
||||
t.Run("List apps with projects filter set", func(t *testing.T) {
|
||||
appQuery := application.ApplicationQuery{Projects: []string{"test-project1"}}
|
||||
appList, err := appServer.List(context.Background(), &appQuery)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, appList.Items, 1)
|
||||
for _, app := range appList.Items {
|
||||
assert.Equal(t, "test-project1", app.Spec.Project)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("List apps with project filter set (legacy field)", func(t *testing.T) {
|
||||
appQuery := application.ApplicationQuery{Project: []string{"test-project1"}}
|
||||
appList, err := appServer.List(context.Background(), &appQuery)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, appList.Items, 1)
|
||||
for _, app := range appList.Items {
|
||||
assert.Equal(t, "test-project1", app.Spec.Project)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("List apps with both projects and project filter set", func(t *testing.T) {
|
||||
// If the older field is present, we should use it instead of the newer field.
|
||||
appQuery := application.ApplicationQuery{Project: []string{"test-project1"}, Projects: []string{"test-project2"}}
|
||||
appList, err := appServer.List(context.Background(), &appQuery)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, appList.Items, 1)
|
||||
for _, app := range appList.Items {
|
||||
assert.Equal(t, "test-project1", app.Spec.Project)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestListApps(t *testing.T) {
|
||||
appServer := newTestAppServer(newTestApp(func(app *appsv1.Application) {
|
||||
appServer := newTestAppServer(t, newTestApp(func(app *appsv1.Application) {
|
||||
app.Name = "bcd"
|
||||
}), newTestApp(func(app *appsv1.Application) {
|
||||
app.Name = "abc"
|
||||
@@ -450,7 +1002,7 @@ g, group-49, role:test3
|
||||
`
|
||||
_ = enf.SetUserPolicy(policy)
|
||||
}
|
||||
appServer := newTestAppServerWithEnforcerConfigure(f, objects...)
|
||||
appServer := newTestAppServerWithEnforcerConfigure(f, t, objects...)
|
||||
|
||||
res, err := appServer.List(ctx, &application.ApplicationQuery{})
|
||||
|
||||
@@ -464,7 +1016,7 @@ g, group-49, role:test3
|
||||
|
||||
func TestCreateApp(t *testing.T) {
|
||||
testApp := newTestApp()
|
||||
appServer := newTestAppServer()
|
||||
appServer := newTestAppServer(t)
|
||||
testApp.Spec.Project = ""
|
||||
createReq := application.ApplicationCreateRequest{
|
||||
Application: testApp,
|
||||
@@ -477,7 +1029,7 @@ func TestCreateApp(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateAppWithDestName(t *testing.T) {
|
||||
appServer := newTestAppServer()
|
||||
appServer := newTestAppServer(t)
|
||||
testApp := newTestAppWithDestName()
|
||||
createReq := application.ApplicationCreateRequest{
|
||||
Application: testApp,
|
||||
@@ -490,7 +1042,7 @@ func TestCreateAppWithDestName(t *testing.T) {
|
||||
|
||||
func TestUpdateApp(t *testing.T) {
|
||||
testApp := newTestApp()
|
||||
appServer := newTestAppServer(testApp)
|
||||
appServer := newTestAppServer(t, testApp)
|
||||
testApp.Spec.Project = ""
|
||||
app, err := appServer.Update(context.Background(), &application.ApplicationUpdateRequest{
|
||||
Application: testApp,
|
||||
@@ -501,7 +1053,7 @@ func TestUpdateApp(t *testing.T) {
|
||||
|
||||
func TestUpdateAppSpec(t *testing.T) {
|
||||
testApp := newTestApp()
|
||||
appServer := newTestAppServer(testApp)
|
||||
appServer := newTestAppServer(t, testApp)
|
||||
testApp.Spec.Project = ""
|
||||
spec, err := appServer.UpdateSpec(context.Background(), &application.ApplicationUpdateSpecRequest{
|
||||
Name: &testApp.Name,
|
||||
@@ -516,7 +1068,7 @@ func TestUpdateAppSpec(t *testing.T) {
|
||||
|
||||
func TestDeleteApp(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
appServer := newTestAppServer()
|
||||
appServer := newTestAppServer(t)
|
||||
createReq := application.ApplicationCreateRequest{
|
||||
Application: newTestApp(),
|
||||
}
|
||||
@@ -604,20 +1156,9 @@ func TestDeleteApp(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestDeleteApp_InvalidName(t *testing.T) {
|
||||
appServer := newTestAppServer()
|
||||
_, err := appServer.Delete(context.Background(), &application.ApplicationDeleteRequest{
|
||||
Name: pointer.StringPtr("foo"),
|
||||
})
|
||||
if !assert.Error(t, err) {
|
||||
return
|
||||
}
|
||||
assert.True(t, apierrors.IsNotFound(err))
|
||||
}
|
||||
|
||||
func TestSyncAndTerminate(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
appServer := newTestAppServer()
|
||||
appServer := newTestAppServer(t)
|
||||
testApp := newTestApp()
|
||||
testApp.Spec.Source.RepoURL = "https://github.com/argoproj/argo-cd.git"
|
||||
createReq := application.ApplicationCreateRequest{
|
||||
@@ -657,7 +1198,7 @@ func TestSyncAndTerminate(t *testing.T) {
|
||||
|
||||
func TestSyncHelm(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
appServer := newTestAppServer()
|
||||
appServer := newTestAppServer(t)
|
||||
testApp := newTestApp()
|
||||
testApp.Spec.Source.RepoURL = "https://argoproj.github.io/argo-helm"
|
||||
testApp.Spec.Source.Path = ""
|
||||
@@ -681,7 +1222,7 @@ func TestSyncHelm(t *testing.T) {
|
||||
|
||||
func TestSyncGit(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
appServer := newTestAppServer()
|
||||
appServer := newTestAppServer(t)
|
||||
testApp := newTestApp()
|
||||
testApp.Spec.Source.RepoURL = "https://github.com/org/test"
|
||||
testApp.Spec.Source.Path = "deploy"
|
||||
@@ -714,7 +1255,7 @@ func TestRollbackApp(t *testing.T) {
|
||||
Revision: "abc",
|
||||
Source: *testApp.Spec.Source.DeepCopy(),
|
||||
}}
|
||||
appServer := newTestAppServer(testApp)
|
||||
appServer := newTestAppServer(t, testApp)
|
||||
|
||||
updatedApp, err := appServer.Rollback(context.Background(), &application.ApplicationRollbackRequest{
|
||||
Name: &testApp.Name,
|
||||
@@ -734,56 +1275,63 @@ func TestUpdateAppProject(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
// nolint:staticcheck
|
||||
ctx = context.WithValue(ctx, "claims", &jwt.StandardClaims{Subject: "admin"})
|
||||
appServer := newTestAppServer(testApp)
|
||||
appServer := newTestAppServer(t, testApp)
|
||||
appServer.enf.SetDefaultRole("")
|
||||
|
||||
// Verify normal update works (without changing project)
|
||||
_ = appServer.enf.SetBuiltinPolicy(`p, admin, applications, update, default/test-app, allow`)
|
||||
_, err := appServer.Update(ctx, &application.ApplicationUpdateRequest{Application: testApp})
|
||||
assert.NoError(t, err)
|
||||
t.Run("update without changing project", func(t *testing.T) {
|
||||
_ = appServer.enf.SetBuiltinPolicy(`p, admin, applications, update, default/test-app, allow`)
|
||||
_, err := appServer.Update(ctx, &application.ApplicationUpdateRequest{Application: testApp})
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
// Verify caller cannot update to another project
|
||||
testApp.Spec.Project = "my-proj"
|
||||
_, err = appServer.Update(ctx, &application.ApplicationUpdateRequest{Application: testApp})
|
||||
assert.Equal(t, status.Code(err), codes.PermissionDenied)
|
||||
t.Run("cannot update to another project", func(t *testing.T) {
|
||||
testApp.Spec.Project = "my-proj"
|
||||
_, err := appServer.Update(ctx, &application.ApplicationUpdateRequest{Application: testApp})
|
||||
assert.Equal(t, status.Code(err), codes.PermissionDenied)
|
||||
})
|
||||
|
||||
// Verify inability to change projects without create privileges in new project
|
||||
_ = appServer.enf.SetBuiltinPolicy(`
|
||||
t.Run("cannot change projects without create privileges", func(t *testing.T) {
|
||||
_ = appServer.enf.SetBuiltinPolicy(`
|
||||
p, admin, applications, update, default/test-app, allow
|
||||
p, admin, applications, update, my-proj/test-app, allow
|
||||
`)
|
||||
_, err = appServer.Update(ctx, &application.ApplicationUpdateRequest{Application: testApp})
|
||||
statusErr := grpc.UnwrapGRPCStatus(err)
|
||||
assert.NotNil(t, statusErr)
|
||||
assert.Equal(t, codes.PermissionDenied, statusErr.Code())
|
||||
_, err := appServer.Update(ctx, &application.ApplicationUpdateRequest{Application: testApp})
|
||||
statusErr := grpc.UnwrapGRPCStatus(err)
|
||||
assert.NotNil(t, statusErr)
|
||||
assert.Equal(t, codes.PermissionDenied, statusErr.Code())
|
||||
})
|
||||
|
||||
// Verify inability to change projects without update privileges in new project
|
||||
_ = appServer.enf.SetBuiltinPolicy(`
|
||||
t.Run("cannot change projects without update privileges in new project", func(t *testing.T) {
|
||||
_ = appServer.enf.SetBuiltinPolicy(`
|
||||
p, admin, applications, update, default/test-app, allow
|
||||
p, admin, applications, create, my-proj/test-app, allow
|
||||
`)
|
||||
_, err = appServer.Update(ctx, &application.ApplicationUpdateRequest{Application: testApp})
|
||||
assert.Equal(t, status.Code(err), codes.PermissionDenied)
|
||||
_, err := appServer.Update(ctx, &application.ApplicationUpdateRequest{Application: testApp})
|
||||
assert.Equal(t, status.Code(err), codes.PermissionDenied)
|
||||
})
|
||||
|
||||
// Verify inability to change projects without update privileges in old project
|
||||
_ = appServer.enf.SetBuiltinPolicy(`
|
||||
t.Run("cannot change projects without update privileges in old project", func(t *testing.T) {
|
||||
_ = appServer.enf.SetBuiltinPolicy(`
|
||||
p, admin, applications, create, my-proj/test-app, allow
|
||||
p, admin, applications, update, my-proj/test-app, allow
|
||||
`)
|
||||
_, err = appServer.Update(ctx, &application.ApplicationUpdateRequest{Application: testApp})
|
||||
statusErr = grpc.UnwrapGRPCStatus(err)
|
||||
assert.NotNil(t, statusErr)
|
||||
assert.Equal(t, codes.PermissionDenied, statusErr.Code())
|
||||
_, err := appServer.Update(ctx, &application.ApplicationUpdateRequest{Application: testApp})
|
||||
statusErr := grpc.UnwrapGRPCStatus(err)
|
||||
assert.NotNil(t, statusErr)
|
||||
assert.Equal(t, codes.PermissionDenied, statusErr.Code())
|
||||
})
|
||||
|
||||
// Verify can update project with proper permissions
|
||||
_ = appServer.enf.SetBuiltinPolicy(`
|
||||
t.Run("can update project with proper permissions", func(t *testing.T) {
|
||||
// Verify can update project with proper permissions
|
||||
_ = appServer.enf.SetBuiltinPolicy(`
|
||||
p, admin, applications, update, default/test-app, allow
|
||||
p, admin, applications, create, my-proj/test-app, allow
|
||||
p, admin, applications, update, my-proj/test-app, allow
|
||||
`)
|
||||
updatedApp, err := appServer.Update(ctx, &application.ApplicationUpdateRequest{Application: testApp})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "my-proj", updatedApp.Spec.Project)
|
||||
updatedApp, err := appServer.Update(ctx, &application.ApplicationUpdateRequest{Application: testApp})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "my-proj", updatedApp.Spec.Project)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAppJsonPatch(t *testing.T) {
|
||||
@@ -791,7 +1339,7 @@ func TestAppJsonPatch(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
// nolint:staticcheck
|
||||
ctx = context.WithValue(ctx, "claims", &jwt.StandardClaims{Subject: "admin"})
|
||||
appServer := newTestAppServer(testApp)
|
||||
appServer := newTestAppServer(t, testApp)
|
||||
appServer.enf.SetDefaultRole("")
|
||||
|
||||
app, err := appServer.Patch(ctx, &application.ApplicationPatchRequest{Name: &testApp.Name, Patch: pointer.String("garbage")})
|
||||
@@ -816,7 +1364,7 @@ func TestAppMergePatch(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
// nolint:staticcheck
|
||||
ctx = context.WithValue(ctx, "claims", &jwt.StandardClaims{Subject: "admin"})
|
||||
appServer := newTestAppServer(testApp)
|
||||
appServer := newTestAppServer(t, testApp)
|
||||
appServer.enf.SetDefaultRole("")
|
||||
|
||||
app, err := appServer.Patch(ctx, &application.ApplicationPatchRequest{
|
||||
@@ -829,7 +1377,7 @@ func TestServer_GetApplicationSyncWindowsState(t *testing.T) {
|
||||
t.Run("Active", func(t *testing.T) {
|
||||
testApp := newTestApp()
|
||||
testApp.Spec.Project = "proj-maint"
|
||||
appServer := newTestAppServer(testApp)
|
||||
appServer := newTestAppServer(t, testApp)
|
||||
|
||||
active, err := appServer.GetApplicationSyncWindows(context.Background(), &application.ApplicationSyncWindowsQuery{Name: &testApp.Name})
|
||||
assert.NoError(t, err)
|
||||
@@ -838,7 +1386,7 @@ func TestServer_GetApplicationSyncWindowsState(t *testing.T) {
|
||||
t.Run("Inactive", func(t *testing.T) {
|
||||
testApp := newTestApp()
|
||||
testApp.Spec.Project = "default"
|
||||
appServer := newTestAppServer(testApp)
|
||||
appServer := newTestAppServer(t, testApp)
|
||||
|
||||
active, err := appServer.GetApplicationSyncWindows(context.Background(), &application.ApplicationSyncWindowsQuery{Name: &testApp.Name})
|
||||
assert.NoError(t, err)
|
||||
@@ -847,7 +1395,7 @@ func TestServer_GetApplicationSyncWindowsState(t *testing.T) {
|
||||
t.Run("ProjectDoesNotExist", func(t *testing.T) {
|
||||
testApp := newTestApp()
|
||||
testApp.Spec.Project = "none"
|
||||
appServer := newTestAppServer(testApp)
|
||||
appServer := newTestAppServer(t, testApp)
|
||||
|
||||
active, err := appServer.GetApplicationSyncWindows(context.Background(), &application.ApplicationSyncWindowsQuery{Name: &testApp.Name})
|
||||
assert.Contains(t, err.Error(), "not found")
|
||||
@@ -865,7 +1413,7 @@ func TestGetCachedAppState(t *testing.T) {
|
||||
Namespace: testNamespace,
|
||||
},
|
||||
}
|
||||
appServer := newTestAppServer(testApp, testProj)
|
||||
appServer := newTestAppServer(t, testApp, testProj)
|
||||
fakeClientSet := appServer.appclientset.(*apps.Clientset)
|
||||
fakeClientSet.AddReactor("get", "applications", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) {
|
||||
return true, &appsv1.Application{Spec: appsv1.ApplicationSpec{Source: &appsv1.ApplicationSource{}}}, nil
|
||||
@@ -1044,7 +1592,7 @@ func TestGetAppRefresh_NormalRefresh(t *testing.T) {
|
||||
defer cancel()
|
||||
testApp := newTestApp()
|
||||
testApp.ObjectMeta.ResourceVersion = "1"
|
||||
appServer := newTestAppServer(testApp)
|
||||
appServer := newTestAppServer(t, testApp)
|
||||
|
||||
var patched int32
|
||||
|
||||
@@ -1072,7 +1620,7 @@ func TestGetAppRefresh_HardRefresh(t *testing.T) {
|
||||
defer cancel()
|
||||
testApp := newTestApp()
|
||||
testApp.ObjectMeta.ResourceVersion = "1"
|
||||
appServer := newTestAppServer(testApp)
|
||||
appServer := newTestAppServer(t, testApp)
|
||||
|
||||
var getAppDetailsQuery *apiclient.RepoServerAppDetailsQuery
|
||||
mockRepoServiceClient := mocks.RepoServerServiceClient{}
|
||||
@@ -1122,7 +1670,7 @@ func TestInferResourcesStatusHealth(t *testing.T) {
|
||||
Name: "guestbook-stateful",
|
||||
Namespace: "default",
|
||||
}}
|
||||
appServer := newTestAppServer(testApp)
|
||||
appServer := newTestAppServer(t, testApp)
|
||||
appStateCache := appstate.NewCache(cacheClient, time.Minute)
|
||||
err := appStateCache.SetAppResourcesTree(testApp.Name, &appsv1.ApplicationTree{Nodes: []appsv1.ResourceNode{{
|
||||
ResourceRef: appsv1.ResourceRef{
|
||||
|
||||
@@ -23,6 +23,14 @@ func (s *subscriber) matches(event *appv1.ApplicationWatchEvent) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Broadcaster is an interface for broadcasting application informer watch events to multiple subscribers.
|
||||
type Broadcaster interface {
|
||||
Subscribe(ch chan *appv1.ApplicationWatchEvent, filters ...func(event *appv1.ApplicationWatchEvent) bool) func()
|
||||
OnAdd(interface{})
|
||||
OnUpdate(interface{}, interface{})
|
||||
OnDelete(interface{})
|
||||
}
|
||||
|
||||
type broadcasterHandler struct {
|
||||
lock sync.Mutex
|
||||
subscribers []*subscriber
|
||||
|
||||
66
server/application/mocks/Broadcaster.go
Normal file
66
server/application/mocks/Broadcaster.go
Normal file
@@ -0,0 +1,66 @@
|
||||
// Code generated by mockery v2.13.1. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
v1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// Broadcaster is an autogenerated mock type for the Broadcaster type
|
||||
type Broadcaster struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// OnAdd provides a mock function with given fields: _a0
|
||||
func (_m *Broadcaster) OnAdd(_a0 interface{}) {
|
||||
_m.Called(_a0)
|
||||
}
|
||||
|
||||
// OnDelete provides a mock function with given fields: _a0
|
||||
func (_m *Broadcaster) OnDelete(_a0 interface{}) {
|
||||
_m.Called(_a0)
|
||||
}
|
||||
|
||||
// OnUpdate provides a mock function with given fields: _a0, _a1
|
||||
func (_m *Broadcaster) OnUpdate(_a0 interface{}, _a1 interface{}) {
|
||||
_m.Called(_a0, _a1)
|
||||
}
|
||||
|
||||
// Subscribe provides a mock function with given fields: ch, filters
|
||||
func (_m *Broadcaster) Subscribe(ch chan *v1alpha1.ApplicationWatchEvent, filters ...func(*v1alpha1.ApplicationWatchEvent) bool) func() {
|
||||
_va := make([]interface{}, len(filters))
|
||||
for _i := range filters {
|
||||
_va[_i] = filters[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ch)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
var r0 func()
|
||||
if rf, ok := ret.Get(0).(func(chan *v1alpha1.ApplicationWatchEvent, ...func(*v1alpha1.ApplicationWatchEvent) bool) func()); ok {
|
||||
r0 = rf(ch, filters...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(func())
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
type mockConstructorTestingTNewBroadcaster interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}
|
||||
|
||||
// NewBroadcaster creates a new instance of Broadcaster. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
func NewBroadcaster(t mockConstructorTestingTNewBroadcaster) *Broadcaster {
|
||||
mock := &Broadcaster{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
@@ -769,6 +769,7 @@ func newArgoCDServiceSet(a *ArgoCDServer) *ArgoCDServiceSet {
|
||||
a.AppClientset,
|
||||
a.appLister,
|
||||
a.appInformer,
|
||||
nil,
|
||||
a.RepoClientset,
|
||||
a.Cache,
|
||||
kubectl,
|
||||
|
||||
@@ -429,7 +429,9 @@ func TestNamespacedInvalidAppProject(t *testing.T) {
|
||||
IgnoreErrors().
|
||||
CreateApp().
|
||||
Then().
|
||||
Expect(Error("", "application references project does-not-exist which does not exist"))
|
||||
// We're not allowed to infer whether the project exists based on this error message. Instead, we get a generic
|
||||
// permission denied error.
|
||||
Expect(Error("", "permission denied"))
|
||||
}
|
||||
|
||||
func TestNamespacedAppDeletion(t *testing.T) {
|
||||
|
||||
@@ -412,7 +412,9 @@ func TestInvalidAppProject(t *testing.T) {
|
||||
IgnoreErrors().
|
||||
CreateApp().
|
||||
Then().
|
||||
Expect(Error("", "application references project does-not-exist which does not exist"))
|
||||
// We're not allowed to infer whether the project exists based on this error message. Instead, we get a generic
|
||||
// permission denied error.
|
||||
Expect(Error("", "permission denied"))
|
||||
}
|
||||
|
||||
func TestAppDeletion(t *testing.T) {
|
||||
|
||||
@@ -811,6 +811,8 @@ func RestartRepoServer() {
|
||||
}
|
||||
FailOnErr(Run("", "kubectl", "rollout", "restart", "deployment", workload))
|
||||
FailOnErr(Run("", "kubectl", "rollout", "status", "deployment", workload))
|
||||
// wait longer to avoid error on s390x
|
||||
time.Sleep(10 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user