Repository: argoproj/argo-cd
Pull request #4495 (2020-10-06 09:15:30)

chore: Fixed a deprecated notation in Kustomize
Kustomize `bases` was deprecated, so we changed it to `resources`.
https://kubernetes-sigs.github.io/kustomize/api-reference/kustomization/bases/

Checklist:

* [x] Either (a) I've created an [enhancement proposal](https://github.com/argoproj/argo-cd/issues/new/choose) and discussed it with the community, (b) this is a bug fix, or (c) this does not need to be in the release notes.
* [x] The title of the PR states what changed and the related issues number (used for the release note).
* [ ] I've updated both the CLI and UI to expose my feature, or I plan to submit a second PR with them.
* [ ] Does this PR require documentation updates?
* [ ] I've updated documentation as required by this PR.
* [ ] Optional. My organization is added to USERS.md.
* [x] I've signed the CLA and my build is green ([troubleshooting builds](https://argoproj.github.io/argo-cd/developer-guide/ci/)). 


codecov-commenter: # [Codecov](https://codecov.io/gh/argoproj/argo-cd/pull/4495?src=pr&el=h1) Report
> Merging [#4495](https://codecov.io/gh/argoproj/argo-cd/pull/4495?src=pr&el=desc) into [master](https://codecov.io/gh/argoproj/argo-cd/commit/da57c9f1c871f227b3db7c0be586021e803d65f5?el=desc) will **not change** coverage.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/argoproj/argo-cd/pull/4495/graphs/tree.svg?width=650&height=150&src=pr&token=kf5kwuegwE)](https://codecov.io/gh/argoproj/argo-cd/pull/4495?src=pr&el=tree)

```diff
@@           Coverage Diff           @@
##           master    #4495   +/-   ##
=======================================
  Coverage   41.16%   41.16%           
=======================================
  Files         122      122           
  Lines       16588    16588           
=======================================
  Hits         6828     6828           
  Misses       8777     8777           
  Partials      983      983           
```



------

[Continue to review full report at Codecov](https://codecov.io/gh/argoproj/argo-cd/pull/4495?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/argoproj/argo-cd/pull/4495?src=pr&el=footer). Last update [da57c9f...3d787f5](https://codecov.io/gh/argoproj/argo-cd/pull/4495?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/USERS.md b/USERS.md
index 8a01674a8..575eab852 100644
--- a/USERS.md
+++ b/USERS.md
@@ -65,6 +65,7 @@ Currently, the following organizations are **officially** using Argo CD:
 1. [Swisscom](https://www.swisscom.ch)
 1. [Swissquote](https://github.com/swissquote)
 1. [Syncier](https://syncier.com/)
+1. [TableCheck](https://tablecheck.com/)
 1. [Tesla](https://tesla.com/)
 1. [ThousandEyes](https://www.thousandeyes.com/)
 1. [Ticketmaster](https://ticketmaster.com)
diff --git a/docs/developer-guide/dependencies.md b/docs/developer-guide/dependencies.md
new file mode 100644
index 000000000..374d85eed
--- /dev/null
+++ b/docs/developer-guide/dependencies.md
@@ -0,0 +1,33 @@
+# Managing Dependencies
+
+## GitOps Engine (`github.com/argoproj/gitops-engine`)
+
+### Repository
+
+https://github.com/argoproj/gitops-engine
+
+### Pulling changes from `gitops-engine` 
+
+After your GitOps Engine PR has been merged, ArgoCD needs to be updated to pull in the version of the GitOps engine that contains your change.  Here are the steps:
+
+* Retrieve the SHA hash for your commit. You will use this in the next step.
+* From the `argo-cd` folder, run the following command
+
+    `go get github.com/argoproj/gitops-engine@<git-commit-sha>`
+
+    If you get an error message `invalid version: unknown revision` then you got the wrong SHA hash
+
+* Run:
+
+    `go mod tidy`
+
+* The following files are changed:
+
+    - `go.mod`
+    - `go.sum`
+
+* Create an ArgoCD PR with a `refactor:` type in its title for the two file changes.
+
+### Tips:
+* See https://github.com/argoproj/argo-cd/pull/4434 as an example
+* The PR might require additional, dependent changes in ArgoCD that are directly impacted by the changes made in the engine.  
diff --git a/manifests/cluster-install/kustomization.yaml b/manifests/cluster-install/kustomization.yaml
index 742718e42..3f9744f06 100644
--- a/manifests/cluster-install/kustomization.yaml
+++ b/manifests/cluster-install/kustomization.yaml
@@ -1,6 +1,6 @@
 apiVersion: kustomize.config.k8s.io/v1beta1
 kind: Kustomization
 
-bases:
+resources:
 - ../namespace-install
 - ../cluster-rbac
diff --git a/manifests/ha/cluster-install/kustomization.yaml b/manifests/ha/cluster-install/kustomization.yaml
index 22914d4aa..f117e10a0 100644
--- a/manifests/ha/cluster-install/kustomization.yaml
+++ b/manifests/ha/cluster-install/kustomization.yaml
@@ -1,6 +1,6 @@
 apiVersion: kustomize.config.k8s.io/v1beta1
 kind: Kustomization
 
-bases:
+resources:
 - ../namespace-install
 - ../../cluster-rbac
diff --git a/manifests/ha/namespace-install/kustomization.yaml b/manifests/ha/namespace-install/kustomization.yaml
index 8752d2421..3cf4567ed 100644
--- a/manifests/ha/namespace-install/kustomization.yaml
+++ b/manifests/ha/namespace-install/kustomization.yaml
@@ -1,6 +1,6 @@
 apiVersion: kustomize.config.k8s.io/v1beta1
 kind: Kustomization
 
-bases:
+resources:
 - ../../crds
 - ../base
diff --git a/manifests/namespace-install/kustomization.yaml b/manifests/namespace-install/kustomization.yaml
index 23ce844a9..ca0389293 100644
--- a/manifests/namespace-install/kustomization.yaml
+++ b/manifests/namespace-install/kustomization.yaml
@@ -1,6 +1,6 @@
 apiVersion: kustomize.config.k8s.io/v1beta1
 kind: Kustomization
 
-bases:
+resources:
 - ../crds
 - ../base
diff --git a/mkdocs.yml b/mkdocs.yml
index b46d01df6..5bbd0a4c3 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -92,6 +92,7 @@ nav:
     - developer-guide/debugging-remote-environment.md
     - developer-guide/api-docs.md
     - developer-guide/test-e2e.md
+    - developer-guide/dependencies.md
     - developer-guide/ci.md
     - developer-guide/releasing.md
     - developer-guide/site.md
diff --git a/server/version/version.go b/server/version/version.go
index a151f23b1..806cfcbd4 100644
--- a/server/version/version.go
+++ b/server/version/version.go
@@ -34,16 +34,15 @@ func (s *Server) Version(context.Context, *empty.Empty) (*version.VersionMessage
 		}
 	}
 	if s.kustomizeVersion == "" {
-		kustomizeVersion, err := kustomize.Version()
+		kustomizeVersion, err := kustomize.Version(true)
 		if err == nil {
 			s.kustomizeVersion = kustomizeVersion
 		} else {
 			s.kustomizeVersion = err.Error()
 		}
-
 	}
 	if s.helmVersion == "" {
-		helmVersion, err := helm.Version()
+		helmVersion, err := helm.Version(true)
 		if err == nil {
 			s.helmVersion = helmVersion
 		} else {
diff --git a/ui/src/app/app.tsx b/ui/src/app/app.tsx
index 68b3ce44f..450627d16 100644
--- a/ui/src/app/app.tsx
+++ b/ui/src/app/app.tsx
@@ -9,6 +9,7 @@ import applications from './applications';
 import help from './help';
 import login from './login';
 import settings from './settings';
+import {VersionPanel} from './shared/components/version-info/version-info-panel';
 import {Provider} from './shared/context';
 import {services} from './shared/services';
 import requests from './shared/services/requests';
@@ -52,7 +53,7 @@ const navItems = [
     }
 ];
 
-const versionInfo = services.version.version();
+const versionLoader = services.version.version();
 
 async function isExpiredSSO() {
     try {
@@ -92,7 +93,7 @@ requests.onError.subscribe(async err => {
     }
 });
 
-export class App extends React.Component<{}, {popupProps: PopupProps; error: Error}> {
+export class App extends React.Component<{}, {popupProps: PopupProps; showVersionPanel: boolean; error: Error}> {
     public static childContextTypes = {
         history: PropTypes.object,
         apis: PropTypes.object
@@ -108,7 +109,7 @@ export class App extends React.Component<{}, {popupProps: PopupProps; error: Err
 
     constructor(props: {}) {
         super(props);
-        this.state = {popupProps: null, error: null};
+        this.state = {popupProps: null, error: null, showVersionPanel: false};
         this.popupManager = new PopupManager();
         this.notificationsManager = new NotificationsManager();
         this.navigationManager = new NavigationManager(history);
@@ -186,11 +187,15 @@ export class App extends React.Component<{}, {popupProps: PopupProps; error: Err
                                                     <Layout
                                                         navItems={navItems}
                                                         version={() => (
-                                                            <DataLoader load={() => versionInfo}>
-                                                                {msg => (
-                                                                    <Tooltip content={msg.Version}>
-                                                                        <span style={{whiteSpace: 'nowrap'}}>{msg.Version}</span>
-                                                                    </Tooltip>
+                                                            <DataLoader load={() => versionLoader}>
+                                                                {version => (
+                                                                    <React.Fragment>
+                                                                        <Tooltip content={version.Version}>
+                                                                            <a style={{color: 'white'}} onClick={() => this.setState({showVersionPanel: true})}>
+                                                                                {version.Version}
+                                                                            </a>
+                                                                        </Tooltip>
+                                                                    </React.Fragment>
                                                                 )}
                                                             </DataLoader>
                                                         )}>
@@ -219,6 +224,7 @@ export class App extends React.Component<{}, {popupProps: PopupProps; error: Err
                     </Provider>
                 </PageContext.Provider>
                 <Notifications notifications={this.notificationsManager.notifications} />
+                <VersionPanel version={versionLoader} isShown={this.state.showVersionPanel} onClose={() => this.setState({showVersionPanel: false})} />
             </React.Fragment>
         );
     }
diff --git a/ui/src/app/applications/components/application-details/application-details.scss b/ui/src/app/applications/components/application-details/application-details.scss
index bb9a77a61..1e478e338 100644
--- a/ui/src/app/applications/components/application-details/application-details.scss
+++ b/ui/src/app/applications/components/application-details/application-details.scss
@@ -71,6 +71,17 @@
         }
     }
 
+    &__resource-icon{
+        text-align: center;
+        position: absolute;
+        left: 0;
+        top: 10px;
+        width: 60px;
+        line-height: 1;
+        color: $argo-color-gray-5;
+        font-size: 0.8em;
+    }
+
     .application-resource-tree {
         margin: 0 auto;
     }
diff --git a/ui/src/app/applications/components/application-details/application-resource-list.tsx b/ui/src/app/applications/components/application-details/application-resource-list.tsx
index b0d3c469b..a5069ed3f 100644
--- a/ui/src/app/applications/components/application-details/application-resource-list.tsx
+++ b/ui/src/app/applications/components/application-details/application-resource-list.tsx
@@ -3,6 +3,7 @@ import * as React from 'react';
 
 import * as models from '../../../shared/models';
 import {ResourceIcon} from '../resource-icon';
+import {ResourceLabel} from '../resource-label';
 import {ComparisonStatusIcon, HealthStatusIcon, nodeKey} from '../utils';
 
 export const ApplicationResourceList = ({
@@ -30,7 +31,11 @@ export const ApplicationResourceList = ({
                 <div key={nodeKey(res)} className='argo-table-list__row' onClick={() => onNodeClick(nodeKey(res))}>
                     <div className='row'>
                         <div className='columns small-1 xxxlarge-1'>
-                            <ResourceIcon kind={res.kind} />
+                            <div className='application-details__resource-icon'>
+                                <ResourceIcon kind={res.kind} />
+                                <br />
+                                <div>{ResourceLabel({kind: res.kind})}</div>
+                            </div>
                         </div>
                         <div className='columns small-2 xxxlarge-2'>{res.name}</div>
                         <div className='columns small-3 xxxlarge-4'>{[res.group, res.kind].filter(item => !!item).join('/')}</div>
diff --git a/ui/src/app/shared/components/version-info/version-info-panel.tsx b/ui/src/app/shared/components/version-info/version-info-panel.tsx
new file mode 100644
index 000000000..757550e47
--- /dev/null
+++ b/ui/src/app/shared/components/version-info/version-info-panel.tsx
@@ -0,0 +1,111 @@
+import {DataLoader, SlidingPanel, Tooltip} from 'argo-ui';
+import * as React from 'react';
+import {VersionMessage} from '../../models';
+
+interface VersionPanelProps {
+    isShown: boolean;
+    onClose: () => void;
+    version: Promise<VersionMessage>;
+}
+
+type CopyState = 'success' | 'failed' | undefined;
+
+export class VersionPanel extends React.Component<VersionPanelProps, {copyState: CopyState}> {
+    private readonly header = 'Argo CD Server Version';
+
+    constructor(props: VersionPanelProps) {
+        super(props);
+        this.state = {copyState: undefined};
+    }
+
+    public render() {
+        return (
+            <DataLoader load={() => this.props.version}>
+                {version => {
+                    return (
+                        <SlidingPanel header={this.header} isShown={this.props.isShown} onClose={() => this.props.onClose()} hasCloseButton={true} isNarrow={true}>
+                            <div className='argo-table-list'>{this.buildVersionTable(version)}</div>
+                            <div>
+                                <Tooltip content='Copy all version info as JSON'>{this.getCopyButton(version)}</Tooltip>
+                            </div>
+                        </SlidingPanel>
+                    );
+                }}
+            </DataLoader>
+        );
+    }
+
+    /**
+     * Formats the version data and renders the table rows.
+     */
+    private buildVersionTable(version: VersionMessage): JSX.Element {
+        const formattedVersion = {
+            'Argo CD': version.Version,
+            'Build Date': version.BuildDate,
+            'Go': version.GoVersion,
+            'Compiler': version.Compiler,
+            'Platform': version.Platform,
+            'ksonnet': version.KsonnetVersion,
+            'kustomize': version.KustomizeVersion,
+            'Helm': version.HelmVersion,
+            'kubectl': version.KubectlVersion
+        };
+
+        return (
+            <React.Fragment>
+                {Object.entries(formattedVersion).map(([key, value]) => {
+                    return (
+                        <div className='argo-table-list__row' key={key}>
+                            <div className='row'>
+                                <div className='columns small-4' title={key}>
+                                    <strong>{key}</strong>
+                                </div>
+                                <div className='columns'>
+                                    <Tooltip content={value}>
+                                        <span>{value}</span>
+                                    </Tooltip>
+                                </div>
+                            </div>
+                        </div>
+                    );
+                })}
+            </React.Fragment>
+        );
+    }
+
+    private getCopyButton(version: VersionMessage): JSX.Element {
+        let img: string;
+        let text: string;
+        if (this.state.copyState === 'success') {
+            img = 'fa-check';
+            text = 'Copied';
+        } else if (this.state.copyState === 'failed') {
+            img = 'fa-times';
+            text = 'Copy Failed';
+        } else {
+            img = 'fa-copy';
+            text = 'Copy JSON';
+        }
+
+        return (
+            <button className='argo-button argo-button--base' style={{marginTop: '1em', minWidth: '18ch'}} onClick={() => this.onCopy(version)}>
+                <i className={'fa ' + img} />
+                &nbsp;&nbsp;{text}
+            </button>
+        );
+    }
+
+    private async onCopy(version: VersionMessage): Promise<void> {
+        const stringifiedVersion = JSON.stringify(version, undefined, 4);
+        try {
+            await navigator.clipboard.writeText(stringifiedVersion);
+            this.setState({copyState: 'success'});
+        } catch (err) {
+            this.setState({copyState: 'failed'});
+        }
+
+        setTimeout(() => {
+            this.setState({copyState: undefined});
+        }, 750);
+    }
+}
diff --git a/ui/src/app/shared/models.ts b/ui/src/app/shared/models.ts
index fbdd05983..a8c166d8d 100644
--- a/ui/src/app/shared/models.ts
+++ b/ui/src/app/shared/models.ts
@@ -697,6 +697,15 @@ export interface ApplicationSyncWindowState {
 
 export interface VersionMessage {
     Version: string;
+    BuildDate: string;
+    GoVersion: string;
+    Compiler: string;
+    Platform: string;
+    KsonnetVersion: string;
+    KustomizeVersion: string;
+    HelmVersion: string;
+    KubectlVersion: string;
+    JsonnetVersion: string;
 }
 
 export interface Token {
diff --git a/util/helm/helm.go b/util/helm/helm.go
index 67b662d3c..5f44e33df 100644
--- a/util/helm/helm.go
+++ b/util/helm/helm.go
@@ -86,8 +86,16 @@ func (h *helm) Dispose() {
 	h.cmd.Close()
 }
 
-func Version() (string, error) {
-	cmd := exec.Command("helm", "version", "--client")
+func Version(shortForm bool) (string, error) {
+	executable := "helm"
+	cmdArgs := []string{"version", "--client"}
+	if shortForm {
+		cmdArgs = append(cmdArgs, "--short")
+	}
+	cmd := exec.Command(executable, cmdArgs...)
+	// example version output:
+	// long: "version.BuildInfo{Version:\"v3.3.1\", GitCommit:\"249e5215cde0c3fa72e27eb7a30e8d55c9696144\", GitTreeState:\"clean\", GoVersion:\"go1.14.7\"}"
+	// short: "v3.3.1+g249e521"
 	version, err := executil.RunWithRedactor(cmd, redactor)
 	if err != nil {
 		return "", fmt.Errorf("could not get helm version: %s", err)
diff --git a/util/kustomize/kustomize.go b/util/kustomize/kustomize.go
index 862e8e8f2..057fcfb63 100644
--- a/util/kustomize/kustomize.go
+++ b/util/kustomize/kustomize.go
@@ -187,13 +187,35 @@ func IsKustomization(path string) bool {
 	return false
 }
 
-func Version() (string, error) {
-	cmd := exec.Command("kustomize", "version")
-	out, err := executil.Run(cmd)
+func Version(shortForm bool) (string, error) {
+	executable := "kustomize"
+	cmdArgs := []string{"version"}
+	if shortForm {
+		cmdArgs = append(cmdArgs, "--short")
+	}
+	cmd := exec.Command(executable, cmdArgs...)
+	// example version output:
+	// long: "{Version:kustomize/v3.8.1 GitCommit:0b359d0ef0272e6545eda0e99aacd63aef99c4d0 BuildDate:2020-07-16T00:58:46Z GoOs:linux GoArch:amd64}"
+	// short: "{kustomize/v3.8.1  2020-07-16T00:58:46Z  }"
+	version, err := executil.Run(cmd)
 	if err != nil {
 		return "", fmt.Errorf("could not get kustomize version: %s", err)
 	}
-	return strings.TrimSpace(out), nil
+	version = strings.TrimSpace(version)
+	if shortForm {
+		// trim the curly braces
+		version = strings.TrimPrefix(version, "{")
+		version = strings.TrimSuffix(version, "}")
+		version = strings.TrimSpace(version)
+
+		// remove double space in middle
+		version = strings.ReplaceAll(version, "  ", " ")
+
+		// remove extra 'kustomize/' before version
+		version = strings.TrimPrefix(version, "kustomize/")
+
+	}
+	return version, nil
 }
 
 func getImageParameters(objs []*unstructured.Unstructured) []Image {


Test patch:
diff --git a/util/helm/helm_test.go b/util/helm/helm_test.go
index c31e67ca4..27a79aad1 100644
--- a/util/helm/helm_test.go
+++ b/util/helm/helm_test.go
@@ -165,7 +165,7 @@ func TestHelmArgCleaner(t *testing.T) {
 }
 
 func TestVersion(t *testing.T) {
-	ver, err := Version()
+	ver, err := Version(false)
 	assert.NoError(t, err)
 	assert.NotEmpty(t, ver)
 }
diff --git a/util/kustomize/kustomize_test.go b/util/kustomize/kustomize_test.go
index 9ad0c38ac..2996b2b7e 100644
--- a/util/kustomize/kustomize_test.go
+++ b/util/kustomize/kustomize_test.go
@@ -101,7 +101,7 @@ func TestParseKustomizeBuildOptions(t *testing.T) {
 }
 
 func TestVersion(t *testing.T) {
-	ver, err := Version()
+	ver, err := Version(false)
 	assert.NoError(t, err)
 	assert.NotEmpty(t, ver)
 }