mirror of
https://github.com/argoproj/argo-cd
synced 2026-04-21 17:07:16 +00:00
Adds support for ARGO_CD_[TARGET_REVISION|REVISION] and pass to Custom Tool/Helm/Jsonnet (#2415)
This commit is contained in:
parent
bbfb96cb01
commit
5706a17155
30 changed files with 462 additions and 151 deletions
17
Makefile
17
Makefile
|
|
@ -221,3 +221,20 @@ release-precheck: manifests
|
|||
|
||||
.PHONY: release
|
||||
release: pre-commit release-precheck image release-cli
|
||||
|
||||
.PHONY: build-docs
|
||||
build-docs:
|
||||
mkdocs build
|
||||
|
||||
.PHONY: serve-docs
|
||||
serve-docs:
|
||||
mkdocs serve
|
||||
|
||||
.PHONY: lint-docs
|
||||
lint-docs:
|
||||
# https://github.com/dkhamsing/awesome_bot
|
||||
find docs -name '*.md' -exec grep -l http {} + | xargs docker run --rm -v $(PWD):/mnt:ro dkhamsing/awesome_bot -t 3 --allow-dupe --allow-redirect --white-list `cat white-list | grep -v "#" | tr "\n" ','` --skip-save-results --
|
||||
|
||||
.PHONY: publish-docs
|
||||
publish-docs: lint-docs
|
||||
mkdocs gh-deploy
|
||||
|
|
@ -518,6 +518,10 @@ func setAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, ap
|
|||
setJsonnetOpt(&spec.Source, appOpts.jsonnetTlaStr, false)
|
||||
case "jsonnet-tla-code":
|
||||
setJsonnetOpt(&spec.Source, appOpts.jsonnetTlaCode, true)
|
||||
case "jsonnet-ext-var-str":
|
||||
setJsonnetOptExtVar(&spec.Source, appOpts.jsonnetExtVarStr, false)
|
||||
case "jsonnet-ext-var-code":
|
||||
setJsonnetOptExtVar(&spec.Source, appOpts.jsonnetExtVarCode, true)
|
||||
case "sync-policy":
|
||||
switch appOpts.syncPolicy {
|
||||
case "automated":
|
||||
|
|
@ -645,7 +649,15 @@ func setJsonnetOpt(src *argoappv1.ApplicationSource, tlaParameters []string, cod
|
|||
if src.Directory.IsZero() {
|
||||
src.Directory = nil
|
||||
}
|
||||
}
|
||||
|
||||
func setJsonnetOptExtVar(src *argoappv1.ApplicationSource, jsonnetExtVar []string, code bool) {
|
||||
if src.Directory == nil {
|
||||
src.Directory = &argoappv1.ApplicationSourceDirectory{}
|
||||
}
|
||||
for _, j := range jsonnetExtVar {
|
||||
src.Directory.Jsonnet.ExtVars = append(src.Directory.Jsonnet.ExtVars, argoappv1.NewJsonnetVar(j, code))
|
||||
}
|
||||
}
|
||||
|
||||
type appOptions struct {
|
||||
|
|
@ -671,6 +683,8 @@ type appOptions struct {
|
|||
configManagementPlugin string
|
||||
jsonnetTlaStr []string
|
||||
jsonnetTlaCode []string
|
||||
jsonnetExtVarStr []string
|
||||
jsonnetExtVarCode []string
|
||||
kustomizeImages []string
|
||||
}
|
||||
|
||||
|
|
@ -697,6 +711,8 @@ func addAppFlags(command *cobra.Command, opts *appOptions) {
|
|||
command.Flags().StringVar(&opts.configManagementPlugin, "config-management-plugin", "", "Config management plugin name")
|
||||
command.Flags().StringArrayVar(&opts.jsonnetTlaStr, "jsonnet-tla-str", []string{}, "Jsonnet top level string arguments")
|
||||
command.Flags().StringArrayVar(&opts.jsonnetTlaCode, "jsonnet-tla-code", []string{}, "Jsonnet top level code arguments")
|
||||
command.Flags().StringArrayVar(&opts.jsonnetExtVarStr, "jsonnet-ext-var-str", []string{}, "Jsonnet string ext var")
|
||||
command.Flags().StringArrayVar(&opts.jsonnetExtVarCode, "jsonnet-ext-var-code", []string{}, "Jsonnet ext var")
|
||||
command.Flags().StringArrayVar(&opts.kustomizeImages, "kustomize-image", []string{}, "Kustomize images (e.g. --kustomize-image node:8.15.0 --kustomize-image mysql=mariadb,alpine@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d)")
|
||||
}
|
||||
|
||||
|
|
@ -815,11 +831,12 @@ func getLocalObjects(app *argoappv1.Application, local, appLabelKey, kubeVersion
|
|||
}
|
||||
|
||||
func getLocalObjectsString(app *argoappv1.Application, local, appLabelKey, kubeVersion string, kustomizeOptions *argoappv1.KustomizeOptions) []string {
|
||||
res, err := repository.GenerateManifests(local, &repoapiclient.ManifestRequest{
|
||||
ApplicationSource: &app.Spec.Source,
|
||||
res, err := repository.GenerateManifests(local, app.Spec.Source.TargetRevision, &repoapiclient.ManifestRequest{
|
||||
Repo: &argoappv1.Repository{Repo: app.Spec.Source.RepoURL},
|
||||
AppLabelKey: appLabelKey,
|
||||
AppLabelValue: app.Name,
|
||||
Namespace: app.Spec.Destination.Namespace,
|
||||
ApplicationSource: &app.Spec.Source,
|
||||
KustomizeOptions: kustomizeOptions,
|
||||
KubeVersion: kubeVersion,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ $ curl $ARGOCD_SERVER/api/v1/applications --cookie "argocd.token=$ARGOCD_TOKEN"
|
|||
{"metadata":{"selfLink":"/apis/argoproj.io/v1alpha1/namespaces/argocd/applications","resourceVersion":"37755"},"items":...}
|
||||
```
|
||||
|
||||
> >v1.3
|
||||
> v1.3
|
||||
|
||||
Then pass using the HTTP `Authorization` header, prefixing with `Bearer `:
|
||||
|
||||
|
|
@ -28,5 +28,4 @@ Then pass using the HTTP `Authorization` header, prefixing with `Bearer `:
|
|||
$ curl $ARGOCD_SERVER/api/v1/applications -H "Authorization: Bearer $ARGOCD_TOKEN"
|
||||
{"metadata":{"selfLink":"/apis/argoproj.io/v1alpha1/namespaces/argocd/applications","resourceVersion":"37755"},"items":...}
|
||||
```
|
||||
|
||||
You sh
|
||||
|
||||
|
|
@ -7,19 +7,19 @@ The web site is build using `mkdocs` and `mkdocs-material`.
|
|||
To test:
|
||||
|
||||
```bash
|
||||
mkdocs serve
|
||||
make serve-docs
|
||||
```
|
||||
|
||||
Check for broken external links:
|
||||
|
||||
```bash
|
||||
find docs -name '*.md' -exec grep -l http {} + | xargs awesome_bot -t 3 --allow-dupe --allow-redirect -w argocd.example.com:443,argocd.example.com,kubernetes.default.svc:443,kubernetes.default.svc,mycluster.com,https://github.com/argoproj/my-private-repository,192.168.0.20,storage.googleapis.com,localhost:8080,localhost:6443,your-kubernetes-cluster-addr,10.97.164.88 --skip-save-results --
|
||||
make lint-docs
|
||||
```
|
||||
|
||||
## Deploying
|
||||
|
||||
```bash
|
||||
mkdocs gh-deploy
|
||||
make publish-docs
|
||||
```
|
||||
|
||||
## Analytics
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ Argo CD supports several different ways in which Kubernetes manifests can be def
|
|||
* [Kustomize](kustomize.md) applications
|
||||
* [Helm](helm.md) charts
|
||||
* [Ksonnet](ksonnet.md) applications
|
||||
* A directory of YAML/JSON/Jsonnet manifests
|
||||
* A directory of YAML/JSON/Jsonnet manifests, including [Jsonnet](jsonnet.md).
|
||||
* Any [custom config management tool](config-management-plugins.md) configured as a config management plugin
|
||||
|
||||
## Development
|
||||
|
|
|
|||
10
docs/user-guide/build-environment.md
Normal file
10
docs/user-guide/build-environment.md
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# Build Environment
|
||||
|
||||
[Custom tools](config-management-plugins.md), [Helm](helm.md), and [Jsonnet](jsonnet.md) support the following build env vars:
|
||||
|
||||
* `ARGOCD_APP_NAME` - name of application
|
||||
* `ARGOCD_APP_NAMESPACE` - destination application namespace.
|
||||
* `ARGOCD_APP_REVISION` - the resolved revision, e.g. `f913b6cbf58aa5ae5ca1f8a2b149477aebcbd9d8`
|
||||
* `ARGOCD_APP_SOURCE_PATH` - the path of the app within the repo
|
||||
* `ARGOCD_APP_SOURCE_REPO_URL` the repo's URL
|
||||
* `ARGOCD_APP_SOURCE_TARGET_REVISION` - the target revision from the spec, e.g. `master`.
|
||||
|
|
@ -31,13 +31,9 @@ More config management plugin examples are available in [argocd-example-apps](ht
|
|||
|
||||
Commands have access to
|
||||
|
||||
(1) The system environment variables
|
||||
(2) Argo CD environment variables:
|
||||
|
||||
* `ARGOCD_APP_NAME` - name of application
|
||||
* `ARGOCD_APP_NAMESPACE` - destination application namespace.
|
||||
|
||||
(3) Variables in the application spec:
|
||||
1. The system environment variables
|
||||
2. [Standard build environment](build-environment.md)
|
||||
3. Variables in the application spec:
|
||||
|
||||
> v1.2
|
||||
|
||||
|
|
|
|||
|
|
@ -113,3 +113,25 @@ value, in the values.yaml such that the value is stable between each comparison.
|
|||
```bash
|
||||
argocd app set redis -p password=abc123
|
||||
```
|
||||
|
||||
## Build Environment
|
||||
|
||||
Helm apps have access to the [standard build environment](build-environment.md) via substitution as parameters.
|
||||
|
||||
E.g. via the CLI:
|
||||
|
||||
```bash
|
||||
argocd app create APPNAME \
|
||||
--helm-set-string 'app=${ARGOCD_APP_NAME}'
|
||||
```
|
||||
|
||||
Or via declarative syntax:
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
source:
|
||||
helm:
|
||||
parameters:
|
||||
- name: app
|
||||
value: $ARGOCD_APP_NAME
|
||||
```
|
||||
|
|
|
|||
28
docs/user-guide/jsonnet.md
Normal file
28
docs/user-guide/jsonnet.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# Jsonnet
|
||||
|
||||
Any file matching `*.jsonnet` in a directory app is treated as a Jsonnet file.
|
||||
|
||||
## Build Environment
|
||||
|
||||
Jsonnet apps have access to the [standard build environment](build-environment.md) via substitution into *TLAs* and *external variables*.
|
||||
|
||||
E.g. via the CLI:
|
||||
|
||||
```bash
|
||||
argocd app create APPNAME \
|
||||
--jsonnet-ext-str 'app=${ARGOCD_APP_NAME}' \
|
||||
--jsonnet-tla-str 'ns=${ARGOCD_APP_NAMESPACE}'
|
||||
```
|
||||
|
||||
Or by declarative syntax:
|
||||
|
||||
```yaml
|
||||
directory:
|
||||
jsonnet:
|
||||
extVars:
|
||||
- name: app
|
||||
value: $ARGOCD_APP_NAME
|
||||
tlas:
|
||||
- name: ns
|
||||
value: $ARGOCD_APP_NAMESPACE
|
||||
```
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
# Ksonnet
|
||||
|
||||
!!! tip Warning "Ksonnet is defunct and no longer supported."
|
||||
|
||||
## Environments
|
||||
Ksonnet has a first class concept of an "environment." To create an application from a ksonnet
|
||||
app directory, an environment must be specified. For example, the following command creates the
|
||||
|
|
@ -32,4 +34,6 @@ When overriding ksonnet parameters in Argo CD, the component name should also be
|
|||
argocd app set guestbook-default -p guestbook-ui=image=gcr.io/heptio-images/ks-guestbook-demo:0.1
|
||||
```
|
||||
|
||||
## Build Environment
|
||||
|
||||
We do not support the [standard build environment](build-environment.md) for Ksonnet.
|
||||
|
|
|
|||
|
|
@ -35,3 +35,7 @@ metadata:
|
|||
data:
|
||||
kustomize.buildOptions: --load_restrictor none
|
||||
```
|
||||
|
||||
## Build Environment
|
||||
|
||||
Kustomize does not support parameters and therefore cannot support the standard [build environment](build-environment.md).
|
||||
|
|
@ -47,6 +47,7 @@ nav:
|
|||
- user-guide/kustomize.md
|
||||
- user-guide/helm.md
|
||||
- user-guide/ksonnet.md
|
||||
- user-guide/jsonnet.md
|
||||
- user-guide/config-management-plugins.md
|
||||
- user-guide/tool_detection.md
|
||||
- user-guide/projects.md
|
||||
|
|
@ -57,6 +58,7 @@ nav:
|
|||
- user-guide/compare-options.md
|
||||
- user-guide/sync-options.md
|
||||
- user-guide/parameters.md
|
||||
- user-guide/build-environment.md
|
||||
- user-guide/tracking_strategies.md
|
||||
- user-guide/resource_hooks.md
|
||||
- user-guide/selective_sync.md
|
||||
|
|
|
|||
|
|
@ -98,6 +98,17 @@ func (e Env) Environ() []string {
|
|||
return environ
|
||||
}
|
||||
|
||||
// does an operation similar to `envstubst` tool,
|
||||
// but unlike envsubst it does not change missing names into empty string
|
||||
// see https://linux.die.net/man/1/envsubst
|
||||
func (e Env) Envsubst(s string) string {
|
||||
for _, v := range e {
|
||||
s = strings.ReplaceAll(s, fmt.Sprintf("$%s", v.Name), v.Value)
|
||||
s = strings.ReplaceAll(s, fmt.Sprintf("${%s}", v.Name), v.Value)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// ApplicationSource contains information about github repository, path within repository and target application environment.
|
||||
type ApplicationSource struct {
|
||||
// RepoURL is the repository URL of the application manifests
|
||||
|
|
@ -277,6 +288,15 @@ type JsonnetVar struct {
|
|||
Code bool `json:"code,omitempty" protobuf:"bytes,3,opt,name=code"`
|
||||
}
|
||||
|
||||
func NewJsonnetVar(s string, code bool) JsonnetVar {
|
||||
parts := strings.SplitN(s, "=", 2)
|
||||
if len(parts) == 2 {
|
||||
return JsonnetVar{Name: parts[0], Value: parts[1], Code: code}
|
||||
} else {
|
||||
return JsonnetVar{Name: s, Code: code}
|
||||
}
|
||||
}
|
||||
|
||||
// ApplicationSourceJsonnet holds jsonnet specific options
|
||||
type ApplicationSourceJsonnet struct {
|
||||
// ExtVars is a list of Jsonnet External Variables
|
||||
|
|
|
|||
|
|
@ -1002,6 +1002,13 @@ func TestEnv_IsZero(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestEnv_Envsubst(t *testing.T) {
|
||||
env := Env{&EnvEntry{"FOO", "bar"}}
|
||||
assert.Equal(t, "", env.Envsubst(""))
|
||||
assert.Equal(t, "bar", env.Envsubst("$FOO"))
|
||||
assert.Equal(t, "bar", env.Envsubst("${FOO}"))
|
||||
}
|
||||
|
||||
func TestEnv_Environ(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
|
@ -1442,6 +1449,13 @@ func newTestApp() *Application {
|
|||
return a
|
||||
}
|
||||
|
||||
func TestNewJsonnetVar(t *testing.T) {
|
||||
assert.Equal(t, JsonnetVar{}, NewJsonnetVar("", false))
|
||||
assert.Equal(t, JsonnetVar{Name: "a"}, NewJsonnetVar("a=", false))
|
||||
assert.Equal(t, JsonnetVar{Name: "a", Code: true}, NewJsonnetVar("a=", true))
|
||||
assert.Equal(t, JsonnetVar{Name: "a", Value: "b", Code: true}, NewJsonnetVar("a=b", true))
|
||||
}
|
||||
|
||||
func testCond(t ApplicationConditionType, msg string, lastTransitionTime *metav1.Time) ApplicationCondition {
|
||||
return ApplicationCondition{
|
||||
Type: t,
|
||||
|
|
|
|||
|
|
@ -40,11 +40,6 @@ import (
|
|||
"github.com/argoproj/argo-cd/util/text"
|
||||
)
|
||||
|
||||
const (
|
||||
PluginEnvAppName = "ARGOCD_APP_NAME"
|
||||
PluginEnvAppNamespace = "ARGOCD_APP_NAMESPACE"
|
||||
)
|
||||
|
||||
// Service implements ManifestService interface
|
||||
type Service struct {
|
||||
repoLock *util.KeyLock
|
||||
|
|
@ -190,7 +185,7 @@ func (s *Service) GenerateManifest(c context.Context, q *apiclient.ManifestReque
|
|||
}
|
||||
err := s.runRepoOperation(c, q.Repo, q.ApplicationSource, getCached, func(appPath string, revision string) error {
|
||||
var err error
|
||||
res, err = GenerateManifests(appPath, q)
|
||||
res, err = GenerateManifests(appPath, revision, q)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -212,7 +207,7 @@ func getHelmRepos(repositories []*v1alpha1.Repository) []helm.HelmRepository {
|
|||
return repos
|
||||
}
|
||||
|
||||
func helmTemplate(appPath string, q *apiclient.ManifestRequest) ([]*unstructured.Unstructured, error) {
|
||||
func helmTemplate(appPath string, env *v1alpha1.Env, q *apiclient.ManifestRequest) ([]*unstructured.Unstructured, error) {
|
||||
templateOpts := &helm.TemplateOpts{
|
||||
Name: q.AppLabelValue,
|
||||
Namespace: q.Namespace,
|
||||
|
|
@ -250,7 +245,12 @@ func helmTemplate(appPath string, q *apiclient.ManifestRequest) ([]*unstructured
|
|||
if templateOpts.Name == "" {
|
||||
templateOpts.Name = q.AppLabelValue
|
||||
}
|
||||
|
||||
for i, j := range templateOpts.Set {
|
||||
templateOpts.Set[i] = env.Envsubst(j)
|
||||
}
|
||||
for i, j := range templateOpts.SetString {
|
||||
templateOpts.SetString[i] = env.Envsubst(j)
|
||||
}
|
||||
h, err := helm.NewHelmApp(appPath, getHelmRepos(q.Repos))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -278,31 +278,36 @@ func helmTemplate(appPath string, q *apiclient.ManifestRequest) ([]*unstructured
|
|||
}
|
||||
|
||||
// GenerateManifests generates manifests from a path
|
||||
func GenerateManifests(appPath string, q *apiclient.ManifestRequest) (*apiclient.ManifestResponse, error) {
|
||||
func GenerateManifests(appPath, revision string, q *apiclient.ManifestRequest) (*apiclient.ManifestResponse, error) {
|
||||
var targetObjs []*unstructured.Unstructured
|
||||
var dest *v1alpha1.ApplicationDestination
|
||||
|
||||
appSourceType, err := GetAppSourceType(q.ApplicationSource, appPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
repoURL := ""
|
||||
if q.Repo != nil {
|
||||
repoURL = q.Repo.Repo
|
||||
}
|
||||
env := newEnv(q, revision)
|
||||
|
||||
switch appSourceType {
|
||||
case v1alpha1.ApplicationSourceTypeKsonnet:
|
||||
targetObjs, dest, err = ksShow(q.AppLabelKey, appPath, q.ApplicationSource.Ksonnet)
|
||||
case v1alpha1.ApplicationSourceTypeHelm:
|
||||
targetObjs, err = helmTemplate(appPath, q)
|
||||
targetObjs, err = helmTemplate(appPath, env, q)
|
||||
case v1alpha1.ApplicationSourceTypeKustomize:
|
||||
k := kustomize.NewKustomizeApp(appPath, q.Repo.GetGitCreds(), repoURL)
|
||||
targetObjs, _, err = k.Build(q.ApplicationSource.Kustomize, q.KustomizeOptions)
|
||||
case v1alpha1.ApplicationSourceTypePlugin:
|
||||
targetObjs, err = runConfigManagementPlugin(appPath, q, q.Repo.GetGitCreds())
|
||||
targetObjs, err = runConfigManagementPlugin(appPath, env, q, q.Repo.GetGitCreds())
|
||||
case v1alpha1.ApplicationSourceTypeDirectory:
|
||||
var directory *v1alpha1.ApplicationSourceDirectory
|
||||
if directory = q.ApplicationSource.Directory; directory == nil {
|
||||
directory = &v1alpha1.ApplicationSourceDirectory{}
|
||||
}
|
||||
targetObjs, err = findManifests(appPath, *directory)
|
||||
targetObjs, err = findManifests(appPath, env, *directory)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -355,6 +360,17 @@ func GenerateManifests(appPath string, q *apiclient.ManifestRequest) (*apiclient
|
|||
return &res, nil
|
||||
}
|
||||
|
||||
func newEnv(q *apiclient.ManifestRequest, revision string) *v1alpha1.Env {
|
||||
return &v1alpha1.Env{
|
||||
&v1alpha1.EnvEntry{Name: "ARGOCD_APP_NAME", Value: q.AppLabelValue},
|
||||
&v1alpha1.EnvEntry{Name: "ARGOCD_APP_NAMESPACE", Value: q.Namespace},
|
||||
&v1alpha1.EnvEntry{Name: "ARGOCD_APP_REVISION", Value: revision},
|
||||
&v1alpha1.EnvEntry{Name: "ARGOCD_APP_SOURCE_REPO_URL", Value: q.Repo.Repo},
|
||||
&v1alpha1.EnvEntry{Name: "ARGOCD_APP_SOURCE_PATH", Value: q.ApplicationSource.Path},
|
||||
&v1alpha1.EnvEntry{Name: "ARGOCD_APP_SOURCE_TARGET_REVISION", Value: q.ApplicationSource.TargetRevision},
|
||||
}
|
||||
}
|
||||
|
||||
// GetAppSourceType returns explicit application source type or examines a directory and determines its application source type
|
||||
func GetAppSourceType(source *v1alpha1.ApplicationSource, path string) (v1alpha1.ApplicationSourceType, error) {
|
||||
appSourceType, err := source.ExplicitType()
|
||||
|
|
@ -426,7 +442,7 @@ func ksShow(appLabelKey, appPath string, ksonnetOpts *v1alpha1.ApplicationSource
|
|||
var manifestFile = regexp.MustCompile(`^.*\.(yaml|yml|json|jsonnet)$`)
|
||||
|
||||
// findManifests looks at all yaml files in a directory and unmarshals them into a list of unstructured objects
|
||||
func findManifests(appPath string, directory v1alpha1.ApplicationSourceDirectory) ([]*unstructured.Unstructured, error) {
|
||||
func findManifests(appPath string, env *v1alpha1.Env, directory v1alpha1.ApplicationSourceDirectory) ([]*unstructured.Unstructured, error) {
|
||||
var objs []*unstructured.Unstructured
|
||||
err := filepath.Walk(appPath, func(path string, f os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
|
|
@ -455,7 +471,7 @@ func findManifests(appPath string, directory v1alpha1.ApplicationSourceDirectory
|
|||
}
|
||||
objs = append(objs, &obj)
|
||||
} else if strings.HasSuffix(f.Name(), ".jsonnet") {
|
||||
vm := makeJsonnetVm(directory.Jsonnet)
|
||||
vm := makeJsonnetVm(directory.Jsonnet, env)
|
||||
vm.Importer(&jsonnet.FileImporter{
|
||||
JPaths: []string{appPath},
|
||||
})
|
||||
|
|
@ -499,9 +515,14 @@ func findManifests(appPath string, directory v1alpha1.ApplicationSourceDirectory
|
|||
return objs, nil
|
||||
}
|
||||
|
||||
func makeJsonnetVm(sourceJsonnet v1alpha1.ApplicationSourceJsonnet) *jsonnet.VM {
|
||||
func makeJsonnetVm(sourceJsonnet v1alpha1.ApplicationSourceJsonnet, env *v1alpha1.Env) *jsonnet.VM {
|
||||
vm := jsonnet.MakeVM()
|
||||
|
||||
for i, j := range sourceJsonnet.TLAs {
|
||||
sourceJsonnet.TLAs[i].Value = env.Envsubst(j.Value)
|
||||
}
|
||||
for i, j := range sourceJsonnet.ExtVars {
|
||||
sourceJsonnet.ExtVars[i].Value = env.Envsubst(j.Value)
|
||||
}
|
||||
for _, arg := range sourceJsonnet.TLAs {
|
||||
if arg.Code {
|
||||
vm.TLACode(arg.Name, arg.Value)
|
||||
|
|
@ -539,12 +560,12 @@ func findPlugin(plugins []*v1alpha1.ConfigManagementPlugin, name string) *v1alph
|
|||
return nil
|
||||
}
|
||||
|
||||
func runConfigManagementPlugin(appPath string, q *apiclient.ManifestRequest, creds git.Creds) ([]*unstructured.Unstructured, error) {
|
||||
func runConfigManagementPlugin(appPath string, envVars *v1alpha1.Env, q *apiclient.ManifestRequest, creds git.Creds) ([]*unstructured.Unstructured, error) {
|
||||
plugin := findPlugin(q.Plugins, q.ApplicationSource.Plugin.Name)
|
||||
if plugin == nil {
|
||||
return nil, fmt.Errorf("Config management plugin with name '%s' is not supported.", q.ApplicationSource.Plugin.Name)
|
||||
}
|
||||
env := append(os.Environ(), fmt.Sprintf("%s=%s", PluginEnvAppName, q.AppLabelValue), fmt.Sprintf("%s=%s", PluginEnvAppNamespace, q.Namespace))
|
||||
env := append(os.Environ(), envVars.Environ()...)
|
||||
if creds != nil {
|
||||
closer, environ, err := creds.Environ()
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ func TestGenerateYamlManifestInDir(t *testing.T) {
|
|||
assert.Equal(t, countOfManifests, len(res1.Manifests))
|
||||
|
||||
// this will test concatenated manifests to verify we split YAMLs correctly
|
||||
res2, err := GenerateManifests("./testdata/concatenated", &q)
|
||||
res2, err := GenerateManifests("./testdata/concatenated", "", &q)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 3, len(res2.Manifests))
|
||||
})
|
||||
|
|
@ -296,9 +296,10 @@ func TestRunCustomTool(t *testing.T) {
|
|||
|
||||
func TestGenerateFromUTF16(t *testing.T) {
|
||||
q := apiclient.ManifestRequest{
|
||||
Repo: &argoappv1.Repository{},
|
||||
ApplicationSource: &argoappv1.ApplicationSource{},
|
||||
}
|
||||
res1, err := GenerateManifests("./testdata/utf-16", &q)
|
||||
res1, err := GenerateManifests("./testdata/utf-16", "", &q)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, len(res1.Manifests))
|
||||
}
|
||||
|
|
@ -409,3 +410,22 @@ func TestGetRevisionMetadata(t *testing.T) {
|
|||
assert.EqualValues(t, []string{"tag1", "tag2"}, res.Tags)
|
||||
|
||||
}
|
||||
|
||||
func Test_newEnv(t *testing.T) {
|
||||
assert.Equal(t, &argoappv1.Env{
|
||||
&argoappv1.EnvEntry{Name: "ARGOCD_APP_NAME", Value: "my-app-name"},
|
||||
&argoappv1.EnvEntry{Name: "ARGOCD_APP_NAMESPACE", Value: "my-namespace"},
|
||||
&argoappv1.EnvEntry{Name: "ARGOCD_APP_REVISION", Value: "my-revision"},
|
||||
&argoappv1.EnvEntry{Name: "ARGOCD_APP_SOURCE_REPO_URL", Value: "https://github.com/my-org/my-repo"},
|
||||
&argoappv1.EnvEntry{Name: "ARGOCD_APP_SOURCE_PATH", Value: "my-path"},
|
||||
&argoappv1.EnvEntry{Name: "ARGOCD_APP_SOURCE_TARGET_REVISION", Value: "my-target-revision"},
|
||||
}, newEnv(&apiclient.ManifestRequest{
|
||||
AppLabelValue: "my-app-name",
|
||||
Namespace: "my-namespace",
|
||||
Repo: &argoappv1.Repository{Repo: "https://github.com/my-org/my-repo"},
|
||||
ApplicationSource: &argoappv1.ApplicationSource{
|
||||
Path: "my-path",
|
||||
TargetRevision: "my-target-revision",
|
||||
},
|
||||
}, "my-revision"))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
log "github.com/sirupsen/logrus"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
|
|
@ -87,8 +87,8 @@ func (a *Actions) CreateFromFile(handler func(app *Application)) *Actions {
|
|||
}
|
||||
}
|
||||
|
||||
if len(a.context.jsonnetTLAStr) > 0 || len(a.context.parameters) > 0 {
|
||||
logrus.Fatal("Application parameters or json tlas are not supported")
|
||||
if len(a.context.parameters) > 0 {
|
||||
log.Fatal("Application parameters or json tlas are not supported")
|
||||
}
|
||||
|
||||
if a.context.directoryRecurse {
|
||||
|
|
@ -133,14 +133,6 @@ func (a *Actions) Create(args ...string) *Actions {
|
|||
|
||||
args = append(args, "--project", a.context.project)
|
||||
|
||||
for _, jsonnetTLAParameter := range a.context.jsonnetTLAStr {
|
||||
args = append(args, "--jsonnet-tla-str", jsonnetTLAParameter)
|
||||
}
|
||||
|
||||
for _, jsonnetTLAParameter := range a.context.jsonnetTLACode {
|
||||
args = append(args, "--jsonnet-tla-code", jsonnetTLAParameter)
|
||||
}
|
||||
|
||||
if a.context.namePrefix != "" {
|
||||
args = append(args, "--nameprefix", a.context.namePrefix)
|
||||
}
|
||||
|
|
@ -157,6 +149,8 @@ func (a *Actions) Create(args ...string) *Actions {
|
|||
args = append(args, "--revision", a.context.revision)
|
||||
}
|
||||
|
||||
// are you adding new context values? if you only use them for this func, then use args instead
|
||||
|
||||
a.runCli(args...)
|
||||
|
||||
return a
|
||||
|
|
@ -224,6 +218,8 @@ func (a *Actions) Sync(args ...string) *Actions {
|
|||
args = append(args, "--force")
|
||||
}
|
||||
|
||||
// are you adding new context values? if you only use them for this func, then use args instead
|
||||
|
||||
a.runCli(args...)
|
||||
|
||||
return a
|
||||
|
|
|
|||
|
|
@ -24,8 +24,6 @@ type Context struct {
|
|||
destServer string
|
||||
env string
|
||||
parameters []string
|
||||
jsonnetTLAStr []string
|
||||
jsonnetTLACode []string
|
||||
namePrefix string
|
||||
nameSuffix string
|
||||
resource string
|
||||
|
|
@ -171,16 +169,6 @@ func (c *Context) Parameter(parameter string) *Context {
|
|||
return c
|
||||
}
|
||||
|
||||
func (c *Context) JsonnetTLAStrParameter(parameter string) *Context {
|
||||
c.jsonnetTLAStr = append(c.jsonnetTLAStr, parameter)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Context) JsonnetTLACodeParameter(parameter string) *Context {
|
||||
c.jsonnetTLACode = append(c.jsonnetTLACode, parameter)
|
||||
return c
|
||||
}
|
||||
|
||||
// group:kind:name
|
||||
func (c *Context) SelectedResource(resource string) *Context {
|
||||
c.resource = resource
|
||||
|
|
|
|||
|
|
@ -148,10 +148,10 @@ func TestHelmSet(t *testing.T) {
|
|||
Path("helm").
|
||||
When().
|
||||
Create().
|
||||
AppSet("--helm-set", "foo=bar", "--helm-set", "foo=baz").
|
||||
AppSet("--helm-set", "foo=bar", "--helm-set", "foo=baz", "--helm-set", "app=$ARGOCD_APP_NAME").
|
||||
Then().
|
||||
And(func(app *Application) {
|
||||
assert.Equal(t, []HelmParameter{{Name: "foo", Value: "baz"}}, app.Spec.Source.Helm.Parameters)
|
||||
assert.Equal(t, []HelmParameter{{Name: "foo", Value: "baz"}, {Name: "app", Value: "$ARGOCD_APP_NAME"}}, app.Spec.Source.Helm.Parameters)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -160,10 +160,41 @@ func TestHelmSetString(t *testing.T) {
|
|||
Path("helm").
|
||||
When().
|
||||
Create().
|
||||
AppSet("--helm-set-string", "foo=bar", "--helm-set-string", "foo=baz").
|
||||
AppSet("--helm-set-string", "foo=bar", "--helm-set-string", "foo=baz", "--helm-set-string", "app=$ARGOCD_APP_NAME").
|
||||
Then().
|
||||
And(func(app *Application) {
|
||||
assert.Equal(t, []HelmParameter{{Name: "foo", Value: "baz", ForceString: true}}, app.Spec.Source.Helm.Parameters)
|
||||
assert.Equal(t, []HelmParameter{{Name: "foo", Value: "baz", ForceString: true}, {Name: "app", Value: "$ARGOCD_APP_NAME", ForceString: true}}, app.Spec.Source.Helm.Parameters)
|
||||
})
|
||||
}
|
||||
|
||||
// ensure we can use envsubst in "set" variables
|
||||
func TestHelmSetEnv(t *testing.T) {
|
||||
Given(t).
|
||||
Path("helm-values").
|
||||
When().
|
||||
Create().
|
||||
AppSet("--helm-set", "foo=$ARGOCD_APP_NAME").
|
||||
Sync().
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
And(func(app *Application) {
|
||||
assert.Equal(t, Name(), FailOnErr(Run(".", "kubectl", "-n", DeploymentNamespace(), "get", "cm", "my-map", "-o", "jsonpath={.data.foo}")).(string))
|
||||
})
|
||||
}
|
||||
|
||||
func TestHelmSetStringEnv(t *testing.T) {
|
||||
Given(t).
|
||||
Path("helm-values").
|
||||
When().
|
||||
Create().
|
||||
AppSet("--helm-set-string", "foo=$ARGOCD_APP_NAME").
|
||||
Sync().
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
And(func(app *Application) {
|
||||
assert.Equal(t, Name(), FailOnErr(Run(".", "kubectl", "-n", DeploymentNamespace(), "get", "cm", "my-map", "-o", "jsonpath={.data.foo}")).(string))
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,8 +5,9 @@ import (
|
|||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
. "github.com/argoproj/argo-cd/errors"
|
||||
. "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/test/e2e/fixture"
|
||||
. "github.com/argoproj/argo-cd/test/e2e/fixture"
|
||||
. "github.com/argoproj/argo-cd/test/e2e/fixture/app"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
)
|
||||
|
|
@ -20,7 +21,7 @@ func TestJsonnetAppliedCorrectly(t *testing.T) {
|
|||
Then().
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
And(func(app *Application) {
|
||||
manifests, err := fixture.RunCli("app", "manifests", app.Name, "--source", "live")
|
||||
manifests, err := RunCli("app", "manifests", app.Name, "--source", "live")
|
||||
assert.NoError(t, err)
|
||||
resources, err := kube.SplitYAML(manifests)
|
||||
assert.NoError(t, err)
|
||||
|
|
@ -44,15 +45,13 @@ func TestJsonnetAppliedCorrectly(t *testing.T) {
|
|||
func TestJsonnetTlaParameterAppliedCorrectly(t *testing.T) {
|
||||
Given(t).
|
||||
Path("jsonnet-tla").
|
||||
JsonnetTLAStrParameter("name=testing-tla").
|
||||
JsonnetTLACodeParameter("replicas=3").
|
||||
When().
|
||||
Create().
|
||||
Create("--jsonnet-tla-str", "name=testing-tla", "--jsonnet-tla-code", "replicas=0").
|
||||
Sync().
|
||||
Then().
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
And(func(app *Application) {
|
||||
manifests, err := fixture.RunCli("app", "manifests", app.Name, "--source", "live")
|
||||
manifests, err := RunCli("app", "manifests", app.Name, "--source", "live")
|
||||
assert.NoError(t, err)
|
||||
resources, err := kube.SplitYAML(manifests)
|
||||
assert.NoError(t, err)
|
||||
|
|
@ -69,6 +68,35 @@ func TestJsonnetTlaParameterAppliedCorrectly(t *testing.T) {
|
|||
|
||||
deployment := resources[index]
|
||||
assert.Equal(t, "testing-tla", deployment.GetName())
|
||||
assert.Equal(t, int64(3), *kube.GetDeploymentReplicas(deployment))
|
||||
assert.Equal(t, int64(0), *kube.GetDeploymentReplicas(deployment))
|
||||
})
|
||||
}
|
||||
|
||||
func TestJsonnetTlaEnv(t *testing.T) {
|
||||
Given(t).
|
||||
Path("jsonnet-tla-cm").
|
||||
When().
|
||||
Create("--jsonnet-tla-str", "foo=$ARGOCD_APP_NAME", "--jsonnet-tla-code", "bar='$ARGOCD_APP_NAME'").
|
||||
Sync().
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
And(func(app *Application) {
|
||||
assert.Equal(t, Name(), FailOnErr(Run(".", "kubectl", "-n", DeploymentNamespace(), "get", "cm", "my-map", "-o", "jsonpath={.data.foo}")).(string))
|
||||
assert.Equal(t, Name(), FailOnErr(Run(".", "kubectl", "-n", DeploymentNamespace(), "get", "cm", "my-map", "-o", "jsonpath={.data.bar}")).(string))
|
||||
})
|
||||
}
|
||||
func TestJsonnetExtVarEnv(t *testing.T) {
|
||||
Given(t).
|
||||
Path("jsonnet-ext-var").
|
||||
When().
|
||||
Create("--jsonnet-ext-var-str", "foo=$ARGOCD_APP_NAME", "--jsonnet-ext-var-code", "bar='$ARGOCD_APP_NAME'").
|
||||
Sync().
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
And(func(app *Application) {
|
||||
assert.Equal(t, Name(), FailOnErr(Run(".", "kubectl", "-n", DeploymentNamespace(), "get", "cm", "my-map", "-o", "jsonpath={.data.foo}")).(string))
|
||||
assert.Equal(t, Name(), FailOnErr(Run(".", "kubectl", "-n", DeploymentNamespace(), "get", "cm", "my-map", "-o", "jsonpath={.data.bar}")).(string))
|
||||
})
|
||||
}
|
||||
|
|
|
|||
2
test/e2e/testdata/helm-values/Chart.yaml
vendored
Normal file
2
test/e2e/testdata/helm-values/Chart.yaml
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
version: 1.0.0
|
||||
name: helm-values
|
||||
6
test/e2e/testdata/helm-values/templates/config-map.yaml
vendored
Normal file
6
test/e2e/testdata/helm-values/templates/config-map.yaml
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: my-map
|
||||
data:
|
||||
foo: {{.Values.foo}}
|
||||
1
test/e2e/testdata/helm-values/values.yaml
vendored
Normal file
1
test/e2e/testdata/helm-values/values.yaml
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
foo: bar
|
||||
11
test/e2e/testdata/jsonnet-ext-var/config-map.jsonnet
vendored
Normal file
11
test/e2e/testdata/jsonnet-ext-var/config-map.jsonnet
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
apiVersion: 'v1',
|
||||
kind: 'ConfigMap',
|
||||
metadata: {
|
||||
name: 'my-map',
|
||||
},
|
||||
data: {
|
||||
foo: std.extVar('foo'),
|
||||
bar: std.extVar('bar'),
|
||||
}
|
||||
}
|
||||
12
test/e2e/testdata/jsonnet-tla-cm/config-map.jsonnet
vendored
Normal file
12
test/e2e/testdata/jsonnet-tla-cm/config-map.jsonnet
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
function(foo='foo', bar='bar')
|
||||
{
|
||||
apiVersion: 'v1',
|
||||
kind: 'ConfigMap',
|
||||
metadata: {
|
||||
name: 'my-map',
|
||||
},
|
||||
data: {
|
||||
foo: foo,
|
||||
bar: bar,
|
||||
}
|
||||
}
|
||||
|
|
@ -4,10 +4,11 @@ import {FieldApi, FormApi, FormField as ReactFormField, Text, TextArea} from 're
|
|||
|
||||
import {ArrayInputField, CheckboxField, EditablePanel, EditablePanelItem, Expandable, TagsInputField} from '../../../shared/components';
|
||||
import * as models from '../../../shared/models';
|
||||
import {AuthSettings} from '../../../shared/models';
|
||||
import {ApplicationSourceDirectory, AuthSettings} from '../../../shared/models';
|
||||
import {services} from '../../../shared/services';
|
||||
import {ImageTagFieldEditor} from './kustomize';
|
||||
import * as kustomize from './kustomize-image';
|
||||
import { VarsInputField } from './vars-input-field';
|
||||
|
||||
const TextWithMetadataField = ReactFormField((props: {metadata: { value: string }, fieldApi: FieldApi, className: string }) => {
|
||||
const { fieldApi: {getValue, setValue}} = props;
|
||||
|
|
@ -228,13 +229,28 @@ export const ApplicationParameters = (props: {
|
|||
),
|
||||
});
|
||||
} else if (props.details.type === 'Directory') {
|
||||
const directory = app.spec.source.directory || {} as ApplicationSourceDirectory;
|
||||
attributes.push({
|
||||
title: 'DIRECTORY RECURSE',
|
||||
view: (!!(app.spec.source.directory && app.spec.source.directory.recurse)).toString(),
|
||||
view: (!!directory.recurse).toString(),
|
||||
edit: (formApi: FormApi) => (
|
||||
<FormField formApi={formApi} field='spec.source.directory.recurse' component={CheckboxField}/>
|
||||
),
|
||||
});
|
||||
attributes.push({
|
||||
title: 'TOP-LEVEL ARGUMENTS',
|
||||
view: (directory.jsonnet && directory.jsonnet.tlas || []).map((i, j) => <p key={j}>{i.name}='{i.value}' {i.code && 'code'}</p>),
|
||||
edit: (formApi: FormApi) => (
|
||||
<FormField field='spec.source.directory.jsonnet.tlas' formApi={formApi} component={VarsInputField}/>
|
||||
),
|
||||
});
|
||||
attributes.push({
|
||||
title: 'EXTERNAL VARIABLES',
|
||||
view: (directory.jsonnet && directory.jsonnet.extVars || []).map((i, j) => <p key={j}>{i.name}='{i.value}' {i.code && 'code'}</p>),
|
||||
edit: (formApi: FormApi) => (
|
||||
<FormField field='spec.source.directory.jsonnet.extVars' formApi={formApi} component={VarsInputField}/>
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
import { Checkbox } from 'argo-ui';
|
||||
import * as React from 'react';
|
||||
import * as ReactForm from 'react-form';
|
||||
|
||||
import { ArrayInput, hasNameAndValue, NameValueEditor } from '../../../shared/components';
|
||||
|
||||
export interface Var {
|
||||
name: string;
|
||||
value: string;
|
||||
code: boolean;
|
||||
}
|
||||
|
||||
const VarInputEditor = (item: Var, onChange: (item: Var) => any) => (
|
||||
<React.Fragment>
|
||||
{NameValueEditor(item, onChange)}
|
||||
|
||||
<Checkbox checked={!!item.code} onChange={(val) => onChange({...item, code: val})} />
|
||||
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
export const VarsInputField = ReactForm.FormField((props: { fieldApi: ReactForm.FieldApi }) => {
|
||||
const {fieldApi: {getValue, setValue}} = props;
|
||||
const val = getValue() || [];
|
||||
return <ArrayInput editor={VarInputEditor} items={val} onChange={setValue} valid={hasNameAndValue}/>;
|
||||
});
|
||||
|
|
@ -21,105 +21,90 @@ import * as ReactForm from 'react-form';
|
|||
It does not allow re-ordering of elements (maybe in a v2).
|
||||
*/
|
||||
|
||||
class Item {
|
||||
public name: string;
|
||||
public value: string;
|
||||
export interface NameValue {
|
||||
name: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
class Props {
|
||||
public items: Item[];
|
||||
public onChange: (items: Item[]) => void;
|
||||
export const NameValueEditor = (item: NameValue, onChange: (item: NameValue) => any) => (
|
||||
<React.Fragment>
|
||||
<input placeholder='Name' value={item.name || ''} onChange={(e) => onChange({...item, name: e.target.value})} title='Name'/>
|
||||
|
||||
=
|
||||
|
||||
<input placeholder='Value' value={item.value || ''} onChange={(e) => onChange({...item, value: e.target.value})} title='Value'/>
|
||||
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
interface Props<T> {
|
||||
items: T[];
|
||||
onChange: (items: T[]) => void;
|
||||
editor: (item: T, onChange: (updated: T) => any) => React.ReactNode;
|
||||
valid: (item: T) => boolean;
|
||||
}
|
||||
|
||||
class State {
|
||||
public items: Item[];
|
||||
public newItem: Item;
|
||||
}
|
||||
export function ArrayInput<T>(props: Props<T>) {
|
||||
const addItem = (item: T) => {
|
||||
props.onChange([...props.items, item]);
|
||||
};
|
||||
|
||||
export class ArrayInput extends React.Component<Props, State> {
|
||||
constructor(props: Readonly<Props>) {
|
||||
super(props);
|
||||
this.state = {newItem: {name: '', value: ''}, items: props.items};
|
||||
}
|
||||
const replaceItem = (item: T, i: number) => {
|
||||
const items = props.items.slice();
|
||||
items[i] = item;
|
||||
props.onChange(items);
|
||||
};
|
||||
|
||||
public render() {
|
||||
const addItem = (i: Item) => {
|
||||
this.setState((s) => {
|
||||
s.items.push(i);
|
||||
this.props.onChange(s.items);
|
||||
return {items: s.items, newItem: {name: '', value: ''}};
|
||||
});
|
||||
};
|
||||
const replaceItem = (i: Item, j: number) => {
|
||||
this.setState((s) => {
|
||||
s.items[j] = i;
|
||||
this.props.onChange(s.items);
|
||||
return s;
|
||||
});
|
||||
};
|
||||
const removeItem = (j: number) => {
|
||||
this.setState((s) => {
|
||||
s.items.splice(j, 1);
|
||||
this.props.onChange(s.items);
|
||||
return s;
|
||||
});
|
||||
};
|
||||
const setName = (name: string) => {
|
||||
this.setState((s) => ({items: s.items, newItem: {name, value: s.newItem.value}}));
|
||||
};
|
||||
const setValue = (value: string) => {
|
||||
this.setState((s) => ({items: s.items, newItem: {name: s.newItem.name, value}}));
|
||||
};
|
||||
return (
|
||||
<div className='argo-field' style={{border: 0}}>
|
||||
const removeItem = (i: number) => {
|
||||
const items = props.items.slice();
|
||||
items.splice(i, 1);
|
||||
props.onChange(items);
|
||||
};
|
||||
const [newItem, setNewItem] = React.useState({} as T);
|
||||
|
||||
return (
|
||||
<div className='argo-field' style={{border: 0}}>
|
||||
<div>
|
||||
{props.items.map((item, i) => (
|
||||
<div key={`item-${i}`}>
|
||||
{props.editor(item, (updated: T) => replaceItem(updated, i))}
|
||||
|
||||
<button>
|
||||
<i className='fa fa-times' style={{cursor: 'pointer'}} onClick={() => removeItem(i)}/>
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
<div>
|
||||
{this.state.items.map((i, j) => (
|
||||
<div key={`item-${j}`}>
|
||||
<input value={this.state.items[j].name}
|
||||
onChange={(e) => replaceItem({name: e.target.value, value: i.value}, j)}/>
|
||||
|
||||
=
|
||||
|
||||
<input value={this.state.items[j].value}
|
||||
onChange={(e) => replaceItem({name: i.name, value: e.target.value}, j)}/>
|
||||
|
||||
<button >
|
||||
<i className='fa fa-times' style={{cursor: 'pointer'}} onClick={() => removeItem(j)}/>
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div>
|
||||
<input placeholder='Name' value={this.state.newItem.name}
|
||||
onChange={(e) => setName(e.target.value)}/>
|
||||
{props.editor(newItem, setNewItem)}
|
||||
|
||||
=
|
||||
|
||||
<input placeholder='Value' value={this.state.newItem.value}
|
||||
onChange={(e) => setValue(e.target.value)}/>
|
||||
|
||||
<button disabled={this.state.newItem.name === '' || this.state.newItem.value === ''}
|
||||
onClick={() => addItem(this.state.newItem)}>
|
||||
<button disabled={!props.valid(newItem)} onClick={() => {
|
||||
addItem(newItem);
|
||||
setNewItem({} as T);
|
||||
}}>
|
||||
<i style={{cursor: 'pointer'}} className='fa fa-plus'/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function hasNameAndValue(item: {name?: string, value?: string}) {
|
||||
return (item.name || '').trim() !== '' && (item.value || '').trim() !== '';
|
||||
}
|
||||
|
||||
export const ArrayInputField = ReactForm.FormField((props: { fieldApi: ReactForm.FieldApi }) => {
|
||||
const {fieldApi: {getValue, setValue}} = props;
|
||||
return <ArrayInput items={getValue() || []} onChange={setValue}/>;
|
||||
return <ArrayInput editor={NameValueEditor} items={getValue() || []} onChange={setValue} valid={hasNameAndValue} />;
|
||||
});
|
||||
|
||||
export const MapInputField = ReactForm.FormField((props: { fieldApi: ReactForm.FieldApi }) => {
|
||||
const {fieldApi: {getValue, setValue}} = props;
|
||||
const items = new Array<Item>();
|
||||
const items = new Array<NameValue>();
|
||||
const map = getValue() || {};
|
||||
Object.keys(map).forEach((key) => items.push({ name: key, value: map[key] }));
|
||||
return (
|
||||
<ArrayInput items={items} onChange={(array) => {
|
||||
<ArrayInput editor={NameValueEditor} items={items} valid={hasNameAndValue} onChange={(array) => {
|
||||
const newMap = {} as any;
|
||||
array.forEach((item) => newMap[item.name] = item.value);
|
||||
setValue(newMap);
|
||||
|
|
|
|||
|
|
@ -176,18 +176,30 @@ export interface ApplicationSourceKsonnet {
|
|||
parameters: KsonnetParameter[];
|
||||
}
|
||||
|
||||
export interface PluginEnv {
|
||||
export interface EnvEntry {
|
||||
name: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface ApplicationSourcePlugin {
|
||||
name: string;
|
||||
env: PluginEnv[];
|
||||
env: EnvEntry[];
|
||||
}
|
||||
|
||||
export interface JsonnetVar {
|
||||
name: string;
|
||||
value: string;
|
||||
code: boolean;
|
||||
}
|
||||
|
||||
interface ApplicationSourceJsonnet {
|
||||
extVars: JsonnetVar[];
|
||||
tlas: JsonnetVar[];
|
||||
}
|
||||
|
||||
export interface ApplicationSourceDirectory {
|
||||
recurse: boolean;
|
||||
jsonnet?: ApplicationSourceJsonnet;
|
||||
}
|
||||
|
||||
export interface SyncPolicy {
|
||||
|
|
@ -478,7 +490,7 @@ export interface KustomizeAppSpec {
|
|||
|
||||
export interface PluginAppSpec {
|
||||
name: string;
|
||||
env: PluginEnv[];
|
||||
env: EnvEntry[];
|
||||
}
|
||||
|
||||
export interface ObjectReference {
|
||||
|
|
|
|||
23
white-list
Normal file
23
white-list
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# a list of sites we ignore when checking for broken links in mkdocs
|
||||
10.97.164.88
|
||||
192.168.0.20
|
||||
argocd.example.com
|
||||
api.github.com/user
|
||||
cd.apps.argoproj.io
|
||||
git.example.com
|
||||
github.com/argoproj/another-private-repo
|
||||
github.com/argoproj/my-private-repository
|
||||
github.com/argoproj/other-private-repo
|
||||
github.com/argoproj/private-repo
|
||||
github.com/otherproj/another-private-repo
|
||||
ksonnet.io
|
||||
raw.githubusercontent.com/argoproj/argo-cd
|
||||
repo.example.com
|
||||
server.example.com
|
||||
kubernetes.default.svc
|
||||
kubernetes.default.svc:443
|
||||
localhost:6443
|
||||
localhost:8080
|
||||
mycluster.com
|
||||
storage.googleapis.com
|
||||
your-kubernetes-cluster-addr
|
||||
Loading…
Reference in a new issue