Skip to content

Commit

Permalink
feat: Configurable ArgoCD binary download links on Help page. Fixes #…
Browse files Browse the repository at this point in the history
…7698 (#7755)

feat: Configurable ArgoCD binary download links on Help page. Fixes #7698 (#7755)

Signed-off-by: Yuan Tang <terrytangyuan@gmail.com>
  • Loading branch information
terrytangyuan authored Nov 30, 2021
1 parent 394a2c8 commit d8cfafb
Show file tree
Hide file tree
Showing 9 changed files with 379 additions and 110 deletions.
7 changes: 7 additions & 0 deletions assets/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -3570,6 +3570,13 @@
"type": "object",
"title": "Help settings",
"properties": {
"binaryUrls": {
"type": "object",
"title": "the URLs for downloading argocd binaries",
"additionalProperties": {
"type": "string"
}
},
"chatText": {
"type": "string",
"title": "the text for getting chat help, defaults to \"Chat now!\""
Expand Down
5 changes: 5 additions & 0 deletions docs/operator-manual/argocd-cm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ data:
help.chatUrl: "https://mycorp.slack.com/argo-cd"
# the text for getting chat help, defaults to "Chat now!"
help.chatText: "Chat now!"
# The URLs to download additional ArgoCD binaries (besides the Linux amd64 binary included by default)
# for different OS architectures. If provided, additional download buttons will be displayed on the help page.
help.download.darwin-amd64: test-path1
help.download.darwin-arm64: test-path2
help.download.windows-amd64: test-path3

# A dex connector configuration (optional). See SSO configuration documentation:
# https://github.com/argoproj/argo-cd/blob/master/docs/operator-manual/sso
Expand Down
308 changes: 237 additions & 71 deletions pkg/apiclient/settings/settings.pb.go

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions server/settings/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,9 @@ func (s *Server) Get(ctx context.Context, q *settingspkg.SettingsQuery) (*settin
AnonymizeUsers: gaSettings.AnonymizeUsers,
},
Help: &settingspkg.Help{
ChatUrl: help.ChatURL,
ChatText: help.ChatText,
ChatUrl: help.ChatURL,
ChatText: help.ChatText,
BinaryUrls: help.BinaryURLs,
},
Plugins: plugins,
UserLoginsDisabled: userLoginsDisabled,
Expand Down
2 changes: 2 additions & 0 deletions server/settings/settings.proto
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ message Help {
string chatUrl = 1;
// the text for getting chat help, defaults to "Chat now!"
string chatText = 2;
// the URLs for downloading argocd binaries
map<string, string> binaryUrls = 3;
}

// Plugin settings
Expand Down
114 changes: 79 additions & 35 deletions ui/src/app/help/components/help.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,84 @@
import * as React from 'react';
import {Page} from '../../shared/components';
import {DataLoader, Page} from '../../shared/components';
import {Consumer} from '../../shared/context';
import {combineLatest} from 'rxjs';
import {services} from '../../shared/services';
import {map} from 'rxjs/operators';

require('./help.scss');

export const Help = () => (
<Consumer>
{ctx => (
<Page title='Help'>
<div className='row'>
<div className='columns large-4 small-6'>
<div className='help-box'>
<p>New to Argo CD?</p>
<a className='user-info-panel-buttons argo-button argo-button--base' href='https://argo-cd.readthedocs.io'>
Read the docs
</a>
</div>
</div>
<div className='columns large-4 small-6'>
<div className='help-box'>
<p>Want to download the CLI tool?</p>
<a href={`download/argocd-linux-${process.env.HOST_ARCH}`} className='user-info-panel-buttons argo-button argo-button--base'>
<i className='fab fa-linux ' /> Linux
</a>
</div>
</div>
<div className='columns large-4 small-6'>
<div className='help-box'>
<p>You want to develop against Argo CD's API?</p>
<a className='user-info-panel-buttons argo-button argo-button--base' href='/swagger-ui'>
Open the API docs
</a>
</div>
</div>
</div>
</Page>
)}
</Consumer>
);
export const Help = () => {
return (
<DataLoader
load={() =>
combineLatest([services.authService.settings()]).pipe(
map(items => {
return {
binaryUrls: items[0].help.binaryUrls || {}
};
})
)
}>
{({binaryUrls}: {binaryUrls: Record<string, string>}) => {
return (
<Consumer>
{() => (
<Page title='Help'>
<div className='row'>
<div className='columns large-4 small-6'>
<div className='help-box'>
<p>New to Argo CD?</p>
<a className='user-info-panel-buttons argo-button argo-button--base' href='https://argo-cd.readthedocs.io'>
Read the docs
</a>
</div>
</div>
<div className='columns large-4 small-6'>
<div className='help-box'>
<p>Want to download the CLI tool?</p>
<a href={`download/argocd-linux-${process.env.HOST_ARCH}`} className='user-info-panel-buttons argo-button argo-button--base'>
<i className='fab fa-linux' /> Linux (amd64)
</a>
&nbsp;
{binaryUrls.hasOwnProperty('linux-arm64') && (
<a href={`${binaryUrls['linux-arm64']}`} className='user-info-panel-buttons argo-button argo-button--base'>
<i className='fab fa-linux' /> Linux (arm64)
</a>
)}
&nbsp;
{binaryUrls.hasOwnProperty('darwin-amd64') && (
<a href={`${binaryUrls['darwin-amd64']}`} className='user-info-panel-buttons argo-button argo-button--base'>
<i className='fab fa-apple' /> MacOS (amd64)
</a>
)}
&nbsp;
{binaryUrls.hasOwnProperty('darwin-arm64') && (
<a href={`${binaryUrls['darwin-arm64']}`} className='user-info-panel-buttons argo-button argo-button--base'>
<i className='fab fa-apple' /> MacOS (arm64)
</a>
)}
&nbsp;
{binaryUrls.hasOwnProperty('windows-amd64') && (
<a href={`${binaryUrls['windows-amd64']}`} className='user-info-panel-buttons argo-button argo-button--base'>
<i className='fab fa-windows' /> Windows
</a>
)}
</div>
</div>
<div className='columns large-4 small-6'>
<div className='help-box'>
<p>You want to develop against Argo CD's API?</p>
<a className='user-info-panel-buttons argo-button argo-button--base' href='/swagger-ui'>
Open the API docs
</a>
</div>
</div>
</div>
</Page>
)}
</Consumer>
);
}}
</DataLoader>
);
};
1 change: 1 addition & 0 deletions ui/src/app/shared/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,7 @@ export interface AuthSettings {
help: {
chatUrl: string;
chatText: string;
binaryUrls: Record<string, string>;
};
plugins: Plugin[];
userLoginsDisabled: boolean;
Expand Down
22 changes: 20 additions & 2 deletions util/settings/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ type ArgoCDSettings struct {
UiBannerPosition string `json:"uiBannerPosition,omitempty"`
// PasswordPattern for password regular expression
PasswordPattern string `json:"passwordPattern,omitempty"`
// BinaryUrls contains the URLs for downloading argocd binaries
BinaryUrls map[string]string `json:"binaryUrls,omitempty"`
}

type GoogleAnalytics struct {
Expand All @@ -106,6 +108,8 @@ type Help struct {
ChatURL string `json:"chatUrl,omitempty"`
// the text for getting chat help, defaults to "Chat now!"
ChatText string `json:"chatText,omitempty"`
// the URLs for downloading argocd binaries
BinaryURLs map[string]string `json:"binaryUrl,omitempty"`
}

type OIDCConfig struct {
Expand Down Expand Up @@ -309,6 +313,8 @@ const (
settingUiBannerPermanentKey = "ui.bannerpermanent"
// settingUiBannerPositionKey designates the key for the position of the banner
settingUiBannerPositionKey = "ui.bannerposition"
// settingsBinaryUrlsKey designates the key for the argocd binary URLs
settingsBinaryUrlsKey = "help.download"
// globalProjectsKey designates the key for global project settings
globalProjectsKey = "globalProjects"
// initialPasswordSecretName is the name of the secret that will hold the initial admin password
Expand Down Expand Up @@ -925,8 +931,9 @@ func (mgr *SettingsManager) GetHelp() (*Help, error) {
chatText = "Chat now!"
}
return &Help{
ChatURL: argoCDCM.Data[helpChatURL],
ChatText: chatText,
ChatURL: argoCDCM.Data[helpChatURL],
ChatText: chatText,
BinaryURLs: getDownloadBinaryUrlsFromConfigMap(argoCDCM),
}, nil
}

Expand Down Expand Up @@ -1061,6 +1068,16 @@ func (mgr *SettingsManager) ensureSynced(forceResync bool) error {
return mgr.initialize(ctx)
}

func getDownloadBinaryUrlsFromConfigMap(argoCDCM *apiv1.ConfigMap) map[string]string {
binaryUrls := map[string]string{}
for _, archType := range []string{"darwin-amd64", "darwin-arm64", "windows-amd64", "linux-arm64", "linux-amd64"} {
if val, ok := argoCDCM.Data[settingsBinaryUrlsKey+"."+archType]; ok {
binaryUrls[archType] = val
}
}
return binaryUrls
}

// updateSettingsFromConfigMap transfers settings from a Kubernetes configmap into an ArgoCDSettings struct.
func updateSettingsFromConfigMap(settings *ArgoCDSettings, argoCDCM *apiv1.ConfigMap) {
settings.DexConfig = argoCDCM.Data[settingDexConfigKey]
Expand All @@ -1072,6 +1089,7 @@ func updateSettingsFromConfigMap(settings *ArgoCDSettings, argoCDCM *apiv1.Confi
settings.UiBannerContent = argoCDCM.Data[settingUiBannerContentKey]
settings.UiBannerPermanent = argoCDCM.Data[settingUiBannerPermanentKey] == "true"
settings.UiBannerPosition = argoCDCM.Data[settingUiBannerPositionKey]
settings.BinaryUrls = getDownloadBinaryUrlsFromConfigMap(argoCDCM)
if err := validateExternalURL(argoCDCM.Data[settingURLKey]); err != nil {
log.Warnf("Failed to validate URL in configmap: %v", err)
}
Expand Down
25 changes: 25 additions & 0 deletions util/settings/settings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,15 @@ func TestSettingsManager_GetHelp(t *testing.T) {
assert.Equal(t, "foo", h.ChatURL)
assert.Equal(t, "bar", h.ChatText)
})
t.Run("GetBinaryUrls", func(t *testing.T) {
_, settingsManager := fixtures(map[string]string{
"help.download.darwin-amd64": "amd64-path",
"help.download.unsupported": "nowhere",
})
h, err := settingsManager.GetHelp()
assert.NoError(t, err)
assert.Equal(t, map[string]string{"darwin-amd64": "amd64-path"}, h.BinaryURLs)
})
}

func TestGetOIDCConfig(t *testing.T) {
Expand Down Expand Up @@ -895,6 +904,22 @@ func Test_GetTLSConfiguration(t *testing.T) {
})
}

func TestDownloadArgoCDBinaryUrls(t *testing.T) {
_, settingsManager := fixtures(map[string]string{
"help.download.darwin-amd64": "some-url",
})
argoCDCM, err := settingsManager.getConfigMap()
assert.NoError(t, err)
assert.Equal(t, "some-url", argoCDCM.Data["help.download.darwin-amd64"])

_, settingsManager = fixtures(map[string]string{
"help.download.unsupported": "some-url",
})
argoCDCM, err = settingsManager.getConfigMap()
assert.NoError(t, err)
assert.Equal(t, "some-url", argoCDCM.Data["help.download.unsupported"])
}

func TestSecretKeyRef(t *testing.T) {
data := map[string]string{
"oidc.config": `name: Okta
Expand Down

0 comments on commit d8cfafb

Please sign in to comment.