diff --git a/.gitignore b/.gitignore index 9118c9bb6fd..2c6813a2132 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ .vscode .protractor-fail-fast .DS_Store +!/vendor/** cypress-a11y-report.json /bin /gopath diff --git a/Dockerfile b/Dockerfile index 16d25a68093..876c287b6b3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ ################################################## # # go backend build -FROM registry.ci.openshift.org/ocp/builder:rhel-8-golang-1.19-openshift-4.14 AS gobuilder +FROM registry.ci.openshift.org/ocp/builder:rhel-8-golang-1.20-openshift-4.14 AS gobuilder RUN mkdir -p /go/src/github.com/openshift/console/ ADD . /go/src/github.com/openshift/console/ WORKDIR /go/src/github.com/openshift/console/ diff --git a/cmd/bridge/main.go b/cmd/bridge/main.go index c0d4f067960..7306b662456 100644 --- a/cmd/bridge/main.go +++ b/cmd/bridge/main.go @@ -92,6 +92,7 @@ func main() { fK8sModeOffClusterThanos := fs.String("k8s-mode-off-cluster-thanos", "", "DEV ONLY. URL of the cluster's Thanos server.") fK8sModeOffClusterAlertmanager := fs.String("k8s-mode-off-cluster-alertmanager", "", "DEV ONLY. URL of the cluster's AlertManager server.") fK8sModeOffClusterManagedClusterProxy := fs.String("k8s-mode-off-cluster-managed-cluster-proxy", "", "DEV ONLY. Public URL of the ACM/MCE cluster proxy.") + fK8sModeOffClusterServiceAccountBearerTokenFile := fs.String("k8s-mode-off-cluster-service-account-bearer-token-file", "", "DEV ONLY. bearer token file for the service account used for internal K8s API server calls.") fK8sAuth := fs.String("k8s-auth", "service-account", "service-account | bearer-token | oidc | openshift") fK8sAuthBearerToken := fs.String("k8s-auth-bearer-token", "", "Authorization token to send with proxied Kubernetes API requests.") @@ -354,6 +355,8 @@ func main() { fmt.Fprintln(os.Stderr, "Cannot provide both --kubectl-client-secret and --kubectrl-client-secret-file") os.Exit(1) } + // Blacklisted headers + srv.ProxyHeaderDenyList = []string{"Cookie", "X-CSRFToken", "X-CSRF-Token"} if *fLogLevel != "" { klog.Warningf("DEPRECATED: --log-level is now deprecated, use verbosity flag --v=Level instead") @@ -395,6 +398,14 @@ func main() { RootCAs: rootCAs, }) + srv.InternalProxiedK8SClientConfig = &rest.Config{ + Host: k8sEndpoint.String(), + BearerTokenFile: k8sInClusterBearerToken, + TLSClientConfig: rest.TLSClientConfig{ + CAFile: k8sInClusterCA, + }, + } + bearerToken, err := ioutil.ReadFile(k8sInClusterBearerToken) if err != nil { klog.Fatalf("failed to read bearer token: %v", err) @@ -402,7 +413,7 @@ func main() { srv.K8sProxyConfig = &proxy.Config{ TLSClientConfig: tlsConfig, - HeaderBlacklist: []string{"Cookie", "X-CSRFToken"}, + HeaderBlacklist: srv.ProxyHeaderDenyList, Endpoint: k8sEndpoint, } @@ -430,33 +441,33 @@ func main() { srv.ThanosProxyConfig = &proxy.Config{ TLSClientConfig: serviceProxyTLSConfig, - HeaderBlacklist: []string{"Cookie", "X-CSRFToken"}, + HeaderBlacklist: srv.ProxyHeaderDenyList, Endpoint: &url.URL{Scheme: "https", Host: openshiftThanosHost, Path: "/api"}, } srv.ThanosTenancyProxyConfig = &proxy.Config{ TLSClientConfig: serviceProxyTLSConfig, - HeaderBlacklist: []string{"Cookie", "X-CSRFToken"}, + HeaderBlacklist: srv.ProxyHeaderDenyList, Endpoint: &url.URL{Scheme: "https", Host: openshiftThanosTenancyHost, Path: "/api"}, } srv.ThanosTenancyProxyForRulesConfig = &proxy.Config{ TLSClientConfig: serviceProxyTLSConfig, - HeaderBlacklist: []string{"Cookie", "X-CSRFToken"}, + HeaderBlacklist: srv.ProxyHeaderDenyList, Endpoint: &url.URL{Scheme: "https", Host: openshiftThanosTenancyForRulesHost, Path: "/api"}, } srv.AlertManagerProxyConfig = &proxy.Config{ TLSClientConfig: serviceProxyTLSConfig, - HeaderBlacklist: []string{"Cookie", "X-CSRFToken"}, + HeaderBlacklist: srv.ProxyHeaderDenyList, Endpoint: &url.URL{Scheme: "https", Host: openshiftAlertManagerHost, Path: "/api"}, } srv.AlertManagerUserWorkloadProxyConfig = &proxy.Config{ TLSClientConfig: serviceProxyTLSConfig, - HeaderBlacklist: []string{"Cookie", "X-CSRFToken"}, + HeaderBlacklist: srv.ProxyHeaderDenyList, Endpoint: &url.URL{Scheme: "https", Host: *fAlertmanagerUserWorkloadHost, Path: "/api"}, } srv.AlertManagerTenancyProxyConfig = &proxy.Config{ TLSClientConfig: serviceProxyTLSConfig, - HeaderBlacklist: []string{"Cookie", "X-CSRFToken"}, + HeaderBlacklist: srv.ProxyHeaderDenyList, Endpoint: &url.URL{Scheme: "https", Host: *fAlertmanagerTenancyHost, Path: "/api"}, } srv.TerminalProxyTLSConfig = serviceProxyTLSConfig @@ -464,7 +475,7 @@ func main() { srv.GitOpsProxyConfig = &proxy.Config{ TLSClientConfig: serviceProxyTLSConfig, - HeaderBlacklist: []string{"Cookie", "X-CSRFToken"}, + HeaderBlacklist: srv.ProxyHeaderDenyList, Endpoint: &url.URL{Scheme: "https", Host: openshiftGitOpsHost}, } // TODO remove multicluster @@ -487,9 +498,18 @@ func main() { }, } + srv.InternalProxiedK8SClientConfig = &rest.Config{ + Host: k8sEndpoint.String(), + Transport: &http.Transport{TLSClientConfig: serviceProxyTLSConfig}, + } + + if *fK8sModeOffClusterServiceAccountBearerTokenFile != "" { + srv.InternalProxiedK8SClientConfig.BearerTokenFile = *fK8sModeOffClusterServiceAccountBearerTokenFile + } + srv.K8sProxyConfig = &proxy.Config{ TLSClientConfig: serviceProxyTLSConfig, - HeaderBlacklist: []string{"Cookie", "X-CSRFToken"}, + HeaderBlacklist: srv.ProxyHeaderDenyList, Endpoint: k8sEndpoint, UseProxyFromEnvironment: true, } @@ -499,17 +519,17 @@ func main() { offClusterThanosURL.Path += "/api" srv.ThanosTenancyProxyConfig = &proxy.Config{ TLSClientConfig: serviceProxyTLSConfig, - HeaderBlacklist: []string{"Cookie", "X-CSRFToken"}, + HeaderBlacklist: srv.ProxyHeaderDenyList, Endpoint: offClusterThanosURL, } srv.ThanosTenancyProxyForRulesConfig = &proxy.Config{ TLSClientConfig: serviceProxyTLSConfig, - HeaderBlacklist: []string{"Cookie", "X-CSRFToken"}, + HeaderBlacklist: srv.ProxyHeaderDenyList, Endpoint: offClusterThanosURL, } srv.ThanosProxyConfig = &proxy.Config{ TLSClientConfig: serviceProxyTLSConfig, - HeaderBlacklist: []string{"Cookie", "X-CSRFToken"}, + HeaderBlacklist: srv.ProxyHeaderDenyList, Endpoint: offClusterThanosURL, } } @@ -519,17 +539,17 @@ func main() { offClusterAlertManagerURL.Path += "/api" srv.AlertManagerProxyConfig = &proxy.Config{ TLSClientConfig: serviceProxyTLSConfig, - HeaderBlacklist: []string{"Cookie", "X-CSRFToken"}, + HeaderBlacklist: srv.ProxyHeaderDenyList, Endpoint: offClusterAlertManagerURL, } srv.AlertManagerTenancyProxyConfig = &proxy.Config{ TLSClientConfig: serviceProxyTLSConfig, - HeaderBlacklist: []string{"Cookie", "X-CSRFToken"}, + HeaderBlacklist: srv.ProxyHeaderDenyList, Endpoint: offClusterAlertManagerURL, } srv.AlertManagerUserWorkloadProxyConfig = &proxy.Config{ TLSClientConfig: serviceProxyTLSConfig, - HeaderBlacklist: []string{"Cookie", "X-CSRFToken"}, + HeaderBlacklist: srv.ProxyHeaderDenyList, Endpoint: offClusterAlertManagerURL, } } @@ -541,7 +561,7 @@ func main() { offClusterGitOpsURL := bridge.ValidateFlagIsURL("k8s-mode-off-cluster-gitops", *fK8sModeOffClusterGitOps) srv.GitOpsProxyConfig = &proxy.Config{ TLSClientConfig: serviceProxyTLSConfig, - HeaderBlacklist: []string{"Cookie", "X-CSRFToken"}, + HeaderBlacklist: srv.ProxyHeaderDenyList, Endpoint: offClusterGitOpsURL, } } @@ -578,7 +598,7 @@ func main() { } srv.ClusterManagementProxyConfig = &proxy.Config{ TLSClientConfig: oscrypto.SecureTLSConfig(&tls.Config{}), - HeaderBlacklist: []string{"Cookie", "X-CSRFToken"}, + HeaderBlacklist: srv.ProxyHeaderDenyList, Endpoint: clusterManagementURL, } diff --git a/dynamic-demo-plugin/package.json b/dynamic-demo-plugin/package.json index da885a22f37..0eb3905f664 100644 --- a/dynamic-demo-plugin/package.json +++ b/dynamic-demo-plugin/package.json @@ -16,7 +16,7 @@ "@openshift-console/dynamic-plugin-sdk": "file:../frontend/packages/console-dynamic-plugin-sdk/dist/core", "@openshift-console/dynamic-plugin-sdk-webpack": "file:../frontend/packages/console-dynamic-plugin-sdk/dist/webpack", "@openshift-console/plugin-shared": "file:../frontend/packages/console-plugin-shared/dist", - "@patternfly/react-core": "4.276.8", + "@patternfly/react-core": "4.276.11", "@patternfly/react-table": "4.113.0", "@types/react": "16.8.13", "@types/react-measure": "^2.0.6", diff --git a/dynamic-demo-plugin/yarn.lock b/dynamic-demo-plugin/yarn.lock index df1043591b0..612418b5928 100644 --- a/dynamic-demo-plugin/yarn.lock +++ b/dynamic-demo-plugin/yarn.lock @@ -96,7 +96,7 @@ version "0.0.0-fixed" dependencies: "@patternfly/quickstarts" "2.4.0" - "@patternfly/react-core" "4.276.8" + "@patternfly/react-core" "4.276.11" "@patternfly/react-table" "4.113.0" classnames "2.x" immutable "3.x" @@ -146,14 +146,14 @@ "@patternfly/react-core" "^4.276.6" "@patternfly/react-styles" "^4.92.6" -"@patternfly/react-core@4.276.8", "@patternfly/react-core@^4.276.6", "@patternfly/react-core@^4.276.8": - version "4.276.8" - resolved "https://registry.yarnpkg.com/@patternfly/react-core/-/react-core-4.276.8.tgz#7ef52830dcdda954bd5bec40132da6eef49aba6f" - integrity sha512-dn322rEzBeiVztZEuCZMUUittNb8l1hk30h9ZN31FLZLLVtXGlThFNV9ieqOJYA9zrYxYZrHMkTnOxSWVacMZg== +"@patternfly/react-core@4.276.11", "@patternfly/react-core@^4.276.6", "@patternfly/react-core@^4.276.8": + version "4.276.11" + resolved "https://registry.yarnpkg.com/@patternfly/react-core/-/react-core-4.276.11.tgz#df59cff4386caf9d0443f9cb4a30922702df5b53" + integrity sha512-ApoBNo0g5GioS4ezCERf13vuVi8aO6rjHnjQr5ogQR2lYR93sBq0FDt60kG+rVwAraKbtjJBjFNAYhKEhER9sQ== dependencies: - "@patternfly/react-icons" "^4.93.6" - "@patternfly/react-styles" "^4.92.6" - "@patternfly/react-tokens" "^4.94.6" + "@patternfly/react-icons" "^4.93.7" + "@patternfly/react-styles" "^4.92.8" + "@patternfly/react-tokens" "^4.94.7" focus-trap "6.9.2" react-dropzone "9.0.0" tippy.js "5.1.2" @@ -164,11 +164,21 @@ resolved "https://registry.yarnpkg.com/@patternfly/react-icons/-/react-icons-4.93.6.tgz#4aff18724afa30157e3ffd6a6414951dbb39dcb3" integrity sha512-ZrXegc/81oiuTIeWvoHb3nG/eZODbB4rYmekBEsrbiysyO7m/sUFoi/RLvgFINrRoh6YCJqL5fj06Jg6d7jX1g== +"@patternfly/react-icons@^4.93.7": + version "4.93.7" + resolved "https://registry.yarnpkg.com/@patternfly/react-icons/-/react-icons-4.93.7.tgz#130acbcda4835cf0f26802aaddbb75664709ad4b" + integrity sha512-3kr35dgba7Qz5CSzmfH0rIjSvBC5xkmiknf3SvVUVxaiVA7KRowID8viYHeZlf3v/Oa3sEewaH830Q0t+nWsZQ== + "@patternfly/react-styles@^4.92.6": version "4.92.6" resolved "https://registry.yarnpkg.com/@patternfly/react-styles/-/react-styles-4.92.6.tgz#a72c5f0b7896ce1c419d1db79f8e39ba6632057d" integrity sha512-b8uQdEReMyeoMzjpMri845QxqtupY/tIFFYfVeKoB2neno8gkcW1RvDdDe62LF88q45OktCwAe/8A99ker10Iw== +"@patternfly/react-styles@^4.92.8": + version "4.92.8" + resolved "https://registry.yarnpkg.com/@patternfly/react-styles/-/react-styles-4.92.8.tgz#2f05455dfe8095a16df27c0d185a7f46f9451d74" + integrity sha512-K4lUU8O4HiCX9NeuNUIrPgN3wlGERCxJVio+PAjd8hpJD/PKnjFfOJ9u6/Cii3qLy/5ZviWPRNHbGiwA/+YUhg== + "@patternfly/react-table@4.113.0": version "4.113.0" resolved "https://registry.yarnpkg.com/@patternfly/react-table/-/react-table-4.113.0.tgz#e9c92b5f323863c1bd546574f02083d1a76c7a81" @@ -186,6 +196,11 @@ resolved "https://registry.yarnpkg.com/@patternfly/react-tokens/-/react-tokens-4.94.6.tgz#47c715721ad3dd315a523f352ba1a0de2b03f0bc" integrity sha512-tm7C6nat+uKMr1hrapis7hS3rN9cr41tpcCKhx6cod6FLU8KwF2Yt5KUxakhIOCEcE/M/EhXhAw/qejp8w0r7Q== +"@patternfly/react-tokens@^4.94.7": + version "4.94.7" + resolved "https://registry.yarnpkg.com/@patternfly/react-tokens/-/react-tokens-4.94.7.tgz#4453b9abe91a4ffa38e3ebec28927d9b91e3320c" + integrity sha512-h+ducOLDMSxcuec3+YY3x+stM5ZUSnrl/lC/eVmjypil2El08NuE2MNEPMQWdhrod6VRRZFMNqZw/m82iv6U1A== + "@remix-run/router@1.7.1": version "1.7.1" resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.7.1.tgz#fea7ac35ae4014637c130011f59428f618730498" diff --git a/frontend/__tests__/components/monitoring.spec.tsx b/frontend/__tests__/components/monitoring.spec.tsx deleted file mode 100644 index 42fc33b6b03..00000000000 --- a/frontend/__tests__/components/monitoring.spec.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { getRouteLabelFieldErrors } from '../../public/components/monitoring/receiver-forms/routing-labels-editor'; - -describe('Routing Label Editor', () => { - it('validates label names correctly', () => { - // label names cannot start with digit, and may only contain alphanumeric and '_' - // these are valid - let labelErrors = getRouteLabelFieldErrors([{ name: 'aa', value: '', isRegex: false }]); - expect(labelErrors['0_name']).toBe(undefined); - labelErrors = getRouteLabelFieldErrors([{ name: '_bb', value: '', isRegex: false }]); - expect(labelErrors['0_name']).toBe(undefined); - labelErrors = getRouteLabelFieldErrors([{ name: '_cc98a7_', value: '', isRegex: false }]); - expect(labelErrors['0_name']).toBe(undefined); - // these are invalid - labelErrors = getRouteLabelFieldErrors([{ name: '9abc', value: '', isRegex: false }]); - expect(labelErrors['0_name']).toBeTruthy(); - labelErrors = getRouteLabelFieldErrors([{ name: 'aa&^%', value: '', isRegex: false }]); - expect(labelErrors['0_name']).toBeTruthy(); - labelErrors = getRouteLabelFieldErrors([{ name: '_abc_%', value: '', isRegex: false }]); - expect(labelErrors['0_name']).toBeTruthy(); - }); -}); diff --git a/frontend/__tests__/components/routes/create-route.spec.tsx b/frontend/__tests__/components/routes/create-route.spec.tsx index c3f09d6503b..abc6fcb8d2a 100644 --- a/frontend/__tests__/components/routes/create-route.spec.tsx +++ b/frontend/__tests__/components/routes/create-route.spec.tsx @@ -72,7 +72,7 @@ describe('Create Route', () => { }, }, weight: 100, - alternateServices: [ + alternateBackends: [ { key: 'alternate-backend-2', name: 'service2', @@ -97,7 +97,7 @@ describe('Create Route', () => { }, }, weight: 100, - alternateServices: [ + alternateBackends: [ { key: 'alternate-backend-2', name: 'service2', @@ -123,7 +123,7 @@ describe('Create Route', () => { }, }, weight: 100, - alternateServices: [ + alternateBackends: [ { key: 'alternate-backend-2', name: 'service2', diff --git a/frontend/__tests__/reducers/features.spec.tsx b/frontend/__tests__/reducers/features.spec.tsx index 1d3aa451e8f..f8ae538f484 100644 --- a/frontend/__tests__/reducers/features.spec.tsx +++ b/frontend/__tests__/reducers/features.spec.tsx @@ -48,6 +48,7 @@ describe('featureReducer', () => { CONSOLE_NOTIFICATION: undefined, CONSOLE_EXTERNAL_LOG_LINK: undefined, CONSOLE_YAML_SAMPLE: undefined, + DEVCONSOLE_PROXY: true, }), ); }); diff --git a/frontend/integration-tests/tests/alertmanager.scenario.ts b/frontend/integration-tests/tests/alertmanager.scenario.ts deleted file mode 100644 index 02a0b0209ff..00000000000 --- a/frontend/integration-tests/tests/alertmanager.scenario.ts +++ /dev/null @@ -1,519 +0,0 @@ -import { browser, ExpectedConditions as until } from 'protractor'; -import * as _ from 'lodash'; -import { safeLoad } from 'js-yaml'; - -import { checkLogs, checkErrors, firstElementByTestID, appHost } from '../protractor.conf'; -import { fillInput } from '@console/shared/src/test-utils/utils'; -import { dropdownMenuForTestID } from '../views/form.view'; -import { - AlertmanagerConfig, - AlertmanagerReceiver, -} from '@console/internal/components/monitoring/alertmanager/alertmanager-config'; -import * as crudView from '../views/crud.view'; -import * as yamlView from '../views/yaml.view'; -import * as monitoringView from '../views/monitoring.view'; -import * as horizontalnavView from '../views/horizontal-nav.view'; -import { execSync } from 'child_process'; - -const getGlobalsAndReceiverConfig = (configName: string, yamlStr: string) => { - const config: AlertmanagerConfig = safeLoad(yamlStr); - const receiverConfig: AlertmanagerReceiver = _.find(config.receivers, { name: 'MyReceiver' }); - return { - globals: config.global, - receiverConfig: receiverConfig[configName][0], - }; -}; - -describe('Alertmanager: PagerDuty Receiver Form', () => { - afterAll(() => { - execSync( - `kubectl patch secret 'alertmanager-main' -n 'openshift-monitoring' --type='json' -p='[{ op: 'replace', path: '/data/alertmanager.yaml', value: ${monitoringView.defaultAlertmanagerYaml}}]'`, - ); - }); - - afterEach(() => { - checkLogs(); - checkErrors(); - }); - - it('creates PagerDuty Receiver correctly', async () => { - await browser.get(`${appHost}/monitoring/alertmanagerconfig`); - await crudView.isLoaded(); - await firstElementByTestID('create-receiver').click(); - await crudView.isLoaded(); - await firstElementByTestID('receiver-name').sendKeys('MyReceiver'); - await firstElementByTestID('dropdown-button').click(); - await crudView.isLoaded(); - await dropdownMenuForTestID('pagerduty_configs').click(); - await crudView.isLoaded(); - await firstElementByTestID('integration-key').sendKeys(''); - await firstElementByTestID('label-name-0').sendKeys('severity'); - await firstElementByTestID('label-value-0').sendKeys('warning'); - - expect(firstElementByTestID('pagerduty-url').getAttribute('value')).toEqual( - 'https://events.pagerduty.com/v2/enqueue', - ); - - // adv config options - await monitoringView.showAdvancedConfiguration.click(); - expect(firstElementByTestID('send-resolved-alerts').getAttribute('checked')).toBeTruthy(); - expect(firstElementByTestID('pagerduty-client').getAttribute('value')).toEqual( - '{{ template "pagerduty.default.client" . }}', - ); - expect(firstElementByTestID('pagerduty-client-url').getAttribute('value')).toEqual( - '{{ template "pagerduty.default.clientURL" . }}', - ); - expect(firstElementByTestID('pagerduty-description').getAttribute('value')).toEqual( - '{{ template "pagerduty.default.description" .}}', - ); - expect(firstElementByTestID('pagerduty-severity').getAttribute('value')).toEqual('error'); - await monitoringView.saveButton.click(); - await crudView.isLoaded(); - await monitoringView.wait(until.elementToBeClickable(crudView.nameFilter)); - await crudView.nameFilter.clear(); - await crudView.nameFilter.sendKeys('MyReceiver'); - monitoringView.getFirstRowAsText().then((text) => { - expect(text).toEqual('MyReceiver pagerduty severity = warning'); - }); - }); - - it('saves globals correctly', async () => { - await browser.get(`${appHost}/monitoring/alertmanagerconfig/receivers/MyReceiver/edit`); - await browser.wait(until.presenceOf(firstElementByTestID('cancel'))); - - // monitoringView.saveAsDefault checkbox disabled when url equals global url - expect(monitoringView.saveAsDefault.isEnabled()).toBeFalsy(); - - // changing url, enables monitoringView.saveAsDefault unchecked, should save pagerduty_url with Receiver - await fillInput( - firstElementByTestID('pagerduty-url'), - 'http://pagerduty-url-specific-to-receiver', - ); - expect(monitoringView.saveAsDefault.isEnabled()).toBeTruthy(); - expect(monitoringView.saveAsDefault.getAttribute('checked')).toBeFalsy(); - await monitoringView.saveButton.click(); - await crudView.isLoaded(); - - //pagerduty_url should be saved with Receiver and not global - await horizontalnavView.clickHorizontalTab('YAML'); - await yamlView.isLoaded(); - let yamlStr = await yamlView.getEditorContent(); - let configs = getGlobalsAndReceiverConfig('pagerduty_configs', yamlStr); - expect(_.has(configs.globals, 'pagerduty_url')).toBeFalsy(); - expect(configs.receiverConfig.url).toBe('http://pagerduty-url-specific-to-receiver'); - - // save pagerduty_url as default/global - await browser.get(`${appHost}/monitoring/alertmanagerconfig/receivers/MyReceiver/edit`); - await browser.wait(until.presenceOf(firstElementByTestID('cancel'))); - await fillInput(firstElementByTestID('pagerduty-url'), 'http://global-pagerduty-url'); - await crudView.isLoaded(); - expect(monitoringView.saveAsDefault.isEnabled()).toBeTruthy(); - monitoringView.saveAsDefault.click(); - expect(monitoringView.saveAsDefault.getAttribute('checked')).toBeTruthy(); - await monitoringView.saveButton.click(); - await crudView.isLoaded(); - - // pagerduty_url should be saved as global, not with Receiver - await horizontalnavView.clickHorizontalTab('YAML'); - await yamlView.isLoaded(); - yamlStr = await yamlView.getEditorContent(); - configs = getGlobalsAndReceiverConfig('pagerduty_configs', yamlStr); - expect(configs.globals.pagerduty_url).toBe('http://global-pagerduty-url'); - expect(_.has(configs.receiverConfig, 'url')).toBeFalsy(); - - // save pagerduty url to receiver with an existing global - await browser.get(`${appHost}/monitoring/alertmanagerconfig/receivers/MyReceiver/edit`); - await browser.wait(until.presenceOf(firstElementByTestID('cancel'))); - await fillInput( - firstElementByTestID('pagerduty-url'), - 'http://pagerduty-url-specific-to-receiver', - ); - expect(monitoringView.saveAsDefault.isEnabled()).toBeTruthy(); - expect(monitoringView.saveAsDefault.getAttribute('checked')).toBeFalsy(); - await monitoringView.saveButton.click(); - await crudView.isLoaded(); - - // pagerduty_url should be saved with Receiver, as well as having a global pagerduty_url prop - await horizontalnavView.clickHorizontalTab('YAML'); - await yamlView.isLoaded(); - yamlStr = await yamlView.getEditorContent(); - configs = getGlobalsAndReceiverConfig('pagerduty_configs', yamlStr); - expect(configs.globals.pagerduty_url).toBe('http://global-pagerduty-url'); - expect(configs.receiverConfig.url).toBe('http://pagerduty-url-specific-to-receiver'); - }); - - it('saves advanced configuration fields correctly', async () => { - await browser.get(`${appHost}/monitoring/alertmanagerconfig/receivers/MyReceiver/edit`); - await browser.wait(until.presenceOf(firstElementByTestID('cancel'))); - - await monitoringView.showAdvancedConfiguration.click(); - - // change first 3 props so that they are diff from global values, thus saved to Receiver config - expect(monitoringView.sendResolvedAlerts.getAttribute('checked')).toBeTruthy(); - await monitoringView.sendResolvedAlerts.click(); - expect(monitoringView.sendResolvedAlerts.getAttribute('checked')).toBeFalsy(); - await fillInput(firstElementByTestID('pagerduty-client'), 'updated-client'); - await fillInput(firstElementByTestID('pagerduty-client-url'), 'http://updated-client-url'); - - await monitoringView.saveButton.click(); - await crudView.isLoaded(); - - // 3 changed fields should be saved with Receiver. description and severity should not be - // saved with Receiver since they equal global values - await horizontalnavView.clickHorizontalTab('YAML'); - await yamlView.isLoaded(); - let yamlStr = await yamlView.getEditorContent(); - let configs = getGlobalsAndReceiverConfig('pagerduty_configs', yamlStr); - expect(configs.receiverConfig.send_resolved).toBeFalsy(); - expect(configs.receiverConfig.client).toBe('updated-client'); - expect(configs.receiverConfig.client_url).toBe('http://updated-client-url'); - expect(configs.receiverConfig.description).toBe(undefined); - expect(configs.receiverConfig.severity).toBe(undefined); - - // restore default values for the 3, change desc and severity -which should then be saved - // with Receiver while initial 3 are removed from Receiver config - await browser.get(`${appHost}/monitoring/alertmanagerconfig/receivers/MyReceiver/edit`); - await browser.wait(until.presenceOf(firstElementByTestID('cancel'))); - await monitoringView.showAdvancedConfiguration.click(); - expect(monitoringView.sendResolvedAlerts.getAttribute('checked')).toBeFalsy(); - await monitoringView.sendResolvedAlerts.click(); - expect(monitoringView.sendResolvedAlerts.getAttribute('checked')).toBeTruthy(); - await fillInput( - firstElementByTestID('pagerduty-client'), - '{{ template "pagerduty.default.client" . }}', - ); - await fillInput( - firstElementByTestID('pagerduty-client-url'), - '{{ template "pagerduty.default.clientURL" . }}', - ); - await fillInput(firstElementByTestID('pagerduty-description'), 'new description'); - await fillInput(firstElementByTestID('pagerduty-severity'), 'warning'); - await monitoringView.saveButton.click(); - await crudView.isLoaded(); - - await horizontalnavView.clickHorizontalTab('YAML'); - await yamlView.isLoaded(); - yamlStr = await yamlView.getEditorContent(); - configs = getGlobalsAndReceiverConfig('pagerduty_configs', yamlStr); - expect(configs.receiverConfig.send_resolved).toBe(undefined); - expect(configs.receiverConfig.client).toBe(undefined); - expect(configs.receiverConfig.client_url).toBe(undefined); - expect(configs.receiverConfig.description).toBe('new description'); - expect(configs.receiverConfig.severity).toBe('warning'); - }); -}); - -describe('Alertmanager: Email Receiver Form', () => { - afterAll(() => { - execSync( - `kubectl patch secret 'alertmanager-main' -n 'openshift-monitoring' --type='json' -p='[{ op: 'replace', path: '/data/alertmanager.yaml', value: ${monitoringView.defaultAlertmanagerYaml}}]'`, - ); - }); - - afterEach(() => { - checkLogs(); - checkErrors(); - }); - - it('creates Email Receiver correctly', async () => { - await browser.get(`${appHost}/monitoring/alertmanagerconfig`); - await crudView.isLoaded(); - await firstElementByTestID('create-receiver').click(); - await crudView.isLoaded(); - await fillInput(firstElementByTestID('receiver-name'), 'MyReceiver'); - await firstElementByTestID('dropdown-button').click(); - await crudView.isLoaded(); - await dropdownMenuForTestID('email_configs').click(); - await crudView.isLoaded(); - - // check defaults - expect(monitoringView.saveAsDefault.isEnabled()).toBeFalsy(); // prior to smtp change, monitoringView.saveAsDefault disabled - expect(firstElementByTestID('email-hello').getAttribute('value')).toEqual('localhost'); - expect(firstElementByTestID('email-require-tls').getAttribute('checked')).toBeTruthy(); - // adv fields - await monitoringView.showAdvancedConfiguration.click(); - expect(monitoringView.sendResolvedAlerts.getAttribute('checked')).toBeFalsy(); - expect(firstElementByTestID('email-html').getAttribute('value')).toEqual( - '{{ template "email.default.html" . }}', - ); - - // change required fields - await fillInput(firstElementByTestID('email-to'), 'you@there.com'); - await fillInput(firstElementByTestID('email-from'), 'me@here.com'); - expect(monitoringView.saveAsDefault.isEnabled()).toBeTruthy(); // monitoringView.saveAsDefault enabled - await fillInput(firstElementByTestID('email-smarthost'), 'smarthost:8080'); - await fillInput(firstElementByTestID('label-name-0'), 'severity'); - await fillInput(firstElementByTestID('label-value-0'), 'warning'); - await monitoringView.saveButton.click(); - await crudView.isLoaded(); - - // all required fields should be saved with Receiver and not globally - await horizontalnavView.clickHorizontalTab('YAML'); - await yamlView.isLoaded(); - const yamlStr = await yamlView.getEditorContent(); - const configs = getGlobalsAndReceiverConfig('email_configs', yamlStr); - expect(_.has(configs.globals, 'email_to')).toBeFalsy(); - expect(_.has(configs.globals, 'smtp_from')).toBeFalsy(); - expect(_.has(configs.globals, 'smtp_smarthost')).toBeFalsy(); - expect(_.has(configs.globals, 'smtp_require_tls')).toBeFalsy(); - expect(configs.receiverConfig.to).toBe('you@there.com'); - expect(configs.receiverConfig.from).toBe('me@here.com'); - expect(configs.receiverConfig.smarthost).toBe('smarthost:8080'); - expect(_.has(configs.receiverConfig, 'require_tls')).toBeFalsy(); // unchanged from global value - }); - - it('saves globals and advanced fields correctly', async () => { - await browser.get(`${appHost}/monitoring/alertmanagerconfig/receivers/MyReceiver/edit`); - await browser.wait(until.presenceOf(firstElementByTestID('cancel'))); - - // Check updated form fields - expect(firstElementByTestID('email-to').getAttribute('value')).toEqual('you@there.com'); - expect(monitoringView.saveAsDefault.isEnabled()).toBeTruthy(); // smtp_from different from global - expect(monitoringView.saveAsDefault.getAttribute('checked')).toBeFalsy(); - expect(firstElementByTestID('email-from').getAttribute('value')).toEqual('me@here.com'); - expect(firstElementByTestID('email-hello').getAttribute('value')).toEqual('localhost'); - - // Change smtp global fields - await fillInput(firstElementByTestID('email-auth-username'), 'username'); - await fillInput(firstElementByTestID('email-auth-password'), 'password'); - await fillInput(firstElementByTestID('email-auth-identity'), 'identity'); - await fillInput(firstElementByTestID('email-auth-secret'), 'secret'); - await firstElementByTestID('email-require-tls').click(); - - // Change advanced fields - await monitoringView.showAdvancedConfiguration.click(); - await monitoringView.sendResolvedAlerts.click(); - await fillInput(firstElementByTestID('email-html'), 'myhtml'); - await monitoringView.saveButton.click(); // monitoringView.saveAsDefault not checked, so all should be saved with Reciever - await crudView.isLoaded(); - - // all fields saved to receiver since save as default not checked - await horizontalnavView.clickHorizontalTab('YAML'); - await yamlView.isLoaded(); - - let yamlStr = await yamlView.getEditorContent(); - let configs = getGlobalsAndReceiverConfig('email_configs', yamlStr); - expect(_.has(configs.globals, 'smtp_auth_username')).toBeFalsy(); - expect(configs.receiverConfig.auth_username).toBe('username'); - expect(configs.receiverConfig.auth_password).toBe('password'); - expect(configs.receiverConfig.auth_identity).toBe('identity'); - expect(configs.receiverConfig.auth_secret).toBe('secret'); - expect(configs.receiverConfig.require_tls).toBeFalsy(); - // adv fields - expect(configs.receiverConfig.send_resolved).toBeTruthy(); - expect(configs.receiverConfig.html).toBe('myhtml'); - - // Save As Default - await browser.get(`${appHost}/monitoring/alertmanagerconfig/receivers/MyReceiver/edit`); - await browser.wait(until.presenceOf(firstElementByTestID('cancel'))); - monitoringView.saveAsDefault.click(); - await monitoringView.saveButton.click(); - await crudView.isLoaded(); - // global fields saved in config.global - await horizontalnavView.clickHorizontalTab('YAML'); - await yamlView.isLoaded(); - yamlStr = await yamlView.getEditorContent(); - configs = getGlobalsAndReceiverConfig('email_configs', yamlStr); - expect(configs.globals.smtp_from).toBe('me@here.com'); - expect(configs.globals.smtp_hello).toBe('localhost'); - expect(configs.globals.smtp_smarthost).toBe('smarthost:8080'); - expect(configs.globals.smtp_auth_username).toBe('username'); - expect(configs.globals.smtp_auth_password).toBe('password'); - expect(configs.globals.smtp_auth_identity).toBe('identity'); - expect(configs.globals.smtp_auth_secret).toBe('secret'); - expect(configs.globals.smtp_require_tls).toBeFalsy(); - // non-global fields should still be saved with Receiver - expect(configs.receiverConfig.to).toBe('you@there.com'); - }); -}); - -describe('Alertmanager: Slack Receiver Form', () => { - afterAll(() => { - execSync( - `kubectl patch secret 'alertmanager-main' -n 'openshift-monitoring' --type='json' -p='[{ op: 'replace', path: '/data/alertmanager.yaml', value: ${monitoringView.defaultAlertmanagerYaml}}]'`, - ); - }); - - afterEach(() => { - checkLogs(); - checkErrors(); - }); - - it('creates Slack Receiver correctly', async () => { - await browser.get(`${appHost}/monitoring/alertmanagerconfig`); - await crudView.isLoaded(); - await firstElementByTestID('create-receiver').click(); - await crudView.isLoaded(); - await fillInput(firstElementByTestID('receiver-name'), 'MyReceiver'); - await firstElementByTestID('dropdown-button').click(); - await crudView.isLoaded(); - await dropdownMenuForTestID('slack_configs').click(); - await crudView.isLoaded(); - - // check defaults - expect(monitoringView.saveAsDefault.isEnabled()).toBeFalsy(); - // adv fields - await monitoringView.showAdvancedConfiguration.click(); - expect(monitoringView.sendResolvedAlerts.getAttribute('checked')).toBeFalsy(); - expect(firstElementByTestID('slack-icon-url').getAttribute('value')).toEqual( - '{{ template "slack.default.iconurl" .}}', - ); - expect(firstElementByTestID('slack-icon-emoji').isPresent()).toBe(false); - await firstElementByTestID('slack-icon-type-emoji').click(); - expect(firstElementByTestID('slack-icon-url').isPresent()).toBe(false); - expect(firstElementByTestID('slack-icon-emoji').getAttribute('value')).toEqual( - '{{ template "slack.default.iconemoji" .}}', - ); - expect(firstElementByTestID('slack-username').getAttribute('value')).toEqual( - '{{ template "slack.default.username" . }}', - ); - expect(firstElementByTestID('slack-link-names').getAttribute('checked')).toBeFalsy(); - - // change required fields - await fillInput(firstElementByTestID('slack-api-url'), 'http://myslackapi'); - expect(monitoringView.saveAsDefault.isEnabled()).toBeTruthy(); // monitoringView.saveAsDefault enabled - await fillInput(firstElementByTestID('slack-channel'), 'myslackchannel'); - await fillInput(firstElementByTestID('label-name-0'), 'severity'); - await fillInput(firstElementByTestID('label-value-0'), 'warning'); - await monitoringView.saveButton.click(); - await crudView.isLoaded(); - - // all required fields should be saved with Receiver and not globally - await horizontalnavView.clickHorizontalTab('YAML'); - await yamlView.isLoaded(); - const yamlStr = await yamlView.getEditorContent(); - const configs = getGlobalsAndReceiverConfig('slack_configs', yamlStr); - expect(_.has(configs.globals, 'slack_api_url')).toBeFalsy(); - expect(configs.receiverConfig.channel).toBe('myslackchannel'); - expect(configs.receiverConfig.api_url).toBe('http://myslackapi'); - // make sure adv fields are not saved since they equal their global values - expect(_.has(configs.receiverConfig, 'send_resolved')).toBeFalsy(); - expect(_.has(configs.receiverConfig, 'username')).toBeFalsy(); - }); - - it('saves globals and advanced fields correctly', async () => { - await browser.get(`${appHost}/monitoring/alertmanagerconfig/receivers/MyReceiver/edit`); - await browser.wait(until.presenceOf(firstElementByTestID('cancel'))); - - // Check updated form fields - expect(firstElementByTestID('slack-channel').getAttribute('value')).toEqual('myslackchannel'); - expect(monitoringView.saveAsDefault.isEnabled()).toBeTruthy(); // different from global - expect(firstElementByTestID('slack-api-url').getAttribute('value')).toEqual( - 'http://myslackapi', - ); - - // Change advanced fields - await monitoringView.showAdvancedConfiguration.click(); - await monitoringView.sendResolvedAlerts.click(); - await fillInput(firstElementByTestID('slack-icon-url'), 'http://myslackicon'); - await fillInput(firstElementByTestID('slack-username'), 'slackuser'); - await firstElementByTestID('slack-link-names').click(); - - monitoringView.saveAsDefault.click(); - await monitoringView.saveButton.click(); - await crudView.isLoaded(); - await browser.get(`${appHost}/monitoring/alertmanagerconfig/receivers/MyReceiver/edit`); - await crudView.isLoaded(); - // check updated advanced form fields - await monitoringView.showAdvancedConfiguration.click(); - expect(monitoringView.sendResolvedAlerts.getAttribute('checked')).toBeTruthy(); - expect(firstElementByTestID('slack-icon-url').getAttribute('value')).toEqual( - 'http://myslackicon', - ); - expect(firstElementByTestID('slack-icon-emoji').isPresent()).toBe(false); - expect(firstElementByTestID('slack-username').getAttribute('value')).toEqual('slackuser'); - expect(firstElementByTestID('slack-link-names').getAttribute('checked')).toBeTruthy(); - - // check saved to slack receiver config yaml - await browser.get(`${appHost}/monitoring/alertmanageryaml`); - await yamlView.isLoaded(); - const yamlStr = await yamlView.getEditorContent(); - const configs = getGlobalsAndReceiverConfig('slack_configs', yamlStr); - expect(configs.globals.slack_api_url).toBe('http://myslackapi'); - expect(_.has(configs.receiverConfig, 'api_url')).toBeFalsy(); - expect(configs.receiverConfig.channel).toBe('myslackchannel'); - // advanced fields - expect(configs.receiverConfig.send_resolved).toBeTruthy(); - expect(configs.receiverConfig.icon_url).toBe('http://myslackicon'); - expect(configs.receiverConfig.username).toBe('slackuser'); - expect(configs.receiverConfig.link_names).toBeTruthy(); - }); -}); - -describe('Alertmanager: Webhook Receiver Form', () => { - afterAll(() => { - execSync( - `kubectl patch secret 'alertmanager-main' -n 'openshift-monitoring' --type='json' -p='[{ op: 'replace', path: '/data/alertmanager.yaml', value: ${monitoringView.defaultAlertmanagerYaml}}]'`, - ); - }); - - afterEach(() => { - checkLogs(); - checkErrors(); - }); - - it('creates Webhook Receiver correctly', async () => { - await browser.get(`${appHost}/monitoring/alertmanagerconfig`); - await crudView.isLoaded(); - await firstElementByTestID('create-receiver').click(); - await crudView.isLoaded(); - await fillInput(firstElementByTestID('receiver-name'), 'MyReceiver'); - await firstElementByTestID('dropdown-button').click(); - await crudView.isLoaded(); - await dropdownMenuForTestID('webhook_configs').click(); - await crudView.isLoaded(); - - // check adv field default value - await monitoringView.showAdvancedConfiguration.click(); - expect(monitoringView.sendResolvedAlerts.getAttribute('checked')).toBeTruthy(); - - // change required fields - await fillInput(firstElementByTestID('webhook-url'), 'http://mywebhookurl'); - await fillInput(firstElementByTestID('label-name-0'), 'severity'); - await fillInput(firstElementByTestID('label-value-0'), 'warning'); - await monitoringView.saveButton.click(); - await crudView.isLoaded(); - - // all required fields should be saved with Receiver - await horizontalnavView.clickHorizontalTab('YAML'); - await yamlView.isLoaded(); - const yamlStr = await yamlView.getEditorContent(); - const configs = getGlobalsAndReceiverConfig('webhook_configs', yamlStr); - expect(configs.receiverConfig.url).toBe('http://mywebhookurl'); - expect(_.has(configs.receiverConfig, 'send_resolved')).toBeFalsy(); - }); - - it('edits Webhook Receiver and saves advanced fields correctly', async () => { - await browser.get(`${appHost}/monitoring/alertmanagerconfig/receivers/MyReceiver/edit`); - await browser.wait(until.presenceOf(firstElementByTestID('cancel'))); - - // Check updated form fields - expect(firstElementByTestID('webhook-url').getAttribute('value')).toEqual( - 'http://mywebhookurl', - ); - - await fillInput(firstElementByTestID('webhook-url'), 'http://myupdatedwebhookurl'); - // Change advanced fields - await monitoringView.showAdvancedConfiguration.click(); - await monitoringView.sendResolvedAlerts.click(); - - await monitoringView.saveButton.click(); - await crudView.isLoaded(); - await browser.get(`${appHost}/monitoring/alertmanagerconfig/receivers/MyReceiver/edit`); - await crudView.isLoaded(); - // check updated advanced form fields - await monitoringView.showAdvancedConfiguration.click(); - expect(monitoringView.sendResolvedAlerts.getAttribute('checked')).toBeFalsy(); - - // check saved to slack receiver config yaml - await browser.get(`${appHost}/monitoring/alertmanageryaml`); - await yamlView.isLoaded(); - await horizontalnavView.clickHorizontalTab('YAML'); - await yamlView.isLoaded(); - const yamlStr = await yamlView.getEditorContent(); - const configs = getGlobalsAndReceiverConfig('webhook_configs', yamlStr); - expect(configs.receiverConfig.url).toBe('http://myupdatedwebhookurl'); - // advanced field - expect(configs.receiverConfig.send_resolved).toBeFalsy(); - }); -}); diff --git a/frontend/integration-tests/tests/monitoring.scenario.ts b/frontend/integration-tests/tests/monitoring.scenario.ts deleted file mode 100644 index da6121d0b24..00000000000 --- a/frontend/integration-tests/tests/monitoring.scenario.ts +++ /dev/null @@ -1,234 +0,0 @@ -import { browser, ExpectedConditions as until } from 'protractor'; - -import { checkLogs, checkErrors, firstElementByTestID, appHost } from '../protractor.conf'; -import { dropdownMenuForTestID } from '../views/form.view'; -import * as crudView from '../views/crud.view'; -import * as yamlView from '../views/yaml.view'; -import * as monitoringView from '../views/monitoring.view'; -import * as sidenavView from '../views/sidenav.view'; -import * as horizontalnavView from '../views/horizontal-nav.view'; -import { execSync } from 'child_process'; - -describe('Alertmanager: YAML', () => { - afterEach(() => { - checkLogs(); - checkErrors(); - }); - - it('displays the Alertmanager YAML page', async () => { - await sidenavView.clickNavLink(['Administration', 'Cluster Settings']); - await crudView.isLoaded(); - await horizontalnavView.clickHorizontalTab('Configuration'); - await crudView.isLoaded(); - await monitoringView.wait(until.elementToBeClickable(firstElementByTestID('Alertmanager'))); - expect(firstElementByTestID('Alertmanager').getText()).toContain('Alertmanager'); - await firstElementByTestID('Alertmanager').click(); - await crudView.isLoaded(); - await horizontalnavView.clickHorizontalTab('YAML'); - await yamlView.isLoaded(); - expect(yamlView.yamlEditor.isPresent()).toBe(true); - }); - - it('saves Alertmanager YAML', async () => { - expect(monitoringView.successAlert.isPresent()).toBe(false); - await yamlView.saveButton.click(); - await yamlView.isLoaded(); - expect(monitoringView.successAlert.isPresent()).toBe(true); - }); -}); - -describe('Alertmanager: Configuration', () => { - afterAll(() => { - execSync( - `kubectl patch secret 'alertmanager-main' -n 'openshift-monitoring' --type='json' -p='[{ op: 'replace', path: '/data/alertmanager.yaml', value: ${monitoringView.defaultAlertmanagerYaml}}]'`, - ); - }); - - afterEach(() => { - checkLogs(); - checkErrors(); - }); - - it('displays the Alertmanager Configuration Details page', async () => { - await browser.get(`${appHost}/monitoring/alertmanagerconfig`); - await crudView.isLoaded(); - expect(monitoringView.alertRoutingHeader.getText()).toContain('Alert routing'); - }); - - it('launches Alert Routing modal, edits and saves correctly', async () => { - await crudView.isLoaded(); - expect(monitoringView.alertRoutingEditButton.isPresent()).toBe(true); - await monitoringView.alertRoutingEditButton.click(); - await crudView.isLoaded(); - - await browser.wait(until.elementToBeClickable(firstElementByTestID('input-group-by'))); - await firstElementByTestID('input-group-by').sendKeys(', cluster'); - await firstElementByTestID('input-group-wait').clear(); - await firstElementByTestID('input-group-wait').sendKeys('60s'); - await firstElementByTestID('input-group-interval').clear(); - await firstElementByTestID('input-group-interval').sendKeys('10m'); - await firstElementByTestID('input-repeat-interval').clear(); - await firstElementByTestID('input-repeat-interval').sendKeys('24h'); - - await monitoringView.saveButton.click(); - await crudView.isLoaded(); - expect(firstElementByTestID('group_by_value').getText()).toContain(', cluster'); - expect(firstElementByTestID('group_wait_value').getText()).toEqual('60s'); - expect(firstElementByTestID('group_interval_value').getText()).toEqual('10m'); - expect(firstElementByTestID('repeat_interval_value').getText()).toEqual('24h'); - }); - - it('creates a receiver correctly', async () => { - await crudView.isLoaded(); - await firstElementByTestID('create-receiver').click(); - await crudView.isLoaded(); - - // these are hidden and disabled until receiverType selected - expect(firstElementByTestID('pagerduty-receiver-form').isPresent()).toBe(false); - expect(firstElementByTestID('receiver-routing-labels-editor').isPresent()).toBe(false); - expect(monitoringView.saveButton.isEnabled()).toBe(false); - - await firstElementByTestID('receiver-name').sendKeys('MyReceiver'); - await firstElementByTestID('dropdown-button').click(); - await crudView.isLoaded(); - - await dropdownMenuForTestID('pagerduty_configs').click(); - await crudView.isLoaded(); - - // these should be shown after receiverType selected - expect(firstElementByTestID('pagerduty-receiver-form').isPresent()).toBe(true); - expect(firstElementByTestID('receiver-routing-labels-editor').isPresent()).toBe(true); - - expect(firstElementByTestID('pagerduty-key-label').getText()).toEqual('Routing key'); - await firstElementByTestID('integration-type-prometheus').click(); - expect(firstElementByTestID('pagerduty-key-label').getText()).toEqual('Service key'); - - // pagerduty subform should still be invalid at this point, thus save button should be disabled - expect(monitoringView.saveButton.isEnabled()).toBe(false); - await firstElementByTestID('integration-key').sendKeys(''); - - // labels - expect(firstElementByTestID('invalid-label-name-error').isPresent()).toBe(false); - await firstElementByTestID('label-name-0').sendKeys('9abcgo'); // invalid, cannot start with digit - expect(firstElementByTestID('invalid-label-name-error').isPresent()).toBe(true); - await firstElementByTestID('label-name-0').clear(); - await firstElementByTestID('label-name-0').sendKeys('_abcd'); // valid, can start with and contain '_' - expect(firstElementByTestID('invalid-label-name-error').isPresent()).toBe(false); - await firstElementByTestID('label-name-0').clear(); - await firstElementByTestID('label-name-0').sendKeys('abcd@#$R@T%'); // invalid chars - expect(firstElementByTestID('invalid-label-name-error').isPresent()).toBe(true); - await firstElementByTestID('label-name-0').clear(); - expect(firstElementByTestID('duplicate-label-name-error').isPresent()).toBe(false); - await firstElementByTestID('label-name-0').sendKeys('severity'); - expect(firstElementByTestID('invalid-label-name-error').isPresent()).toBe(false); - await firstElementByTestID('label-value-0').sendKeys('warning'); - await firstElementByTestID('add-routing-label').click(); - await firstElementByTestID('label-name-1').sendKeys('severity'); - await firstElementByTestID('label-value-1').sendKeys('warning'); - expect(firstElementByTestID('duplicate-label-name-error').isPresent()).toBe(true); - await firstElementByTestID('remove-routing-label').click(); - expect(firstElementByTestID('duplicate-label-name-error').isPresent()).toBe(false); - - expect(monitoringView.saveButton.isEnabled()).toBe(true); // subform valid & labels provided, save should be enabled at this point - await monitoringView.saveButton.click(); - await crudView.isLoaded(); - await monitoringView.wait(until.elementToBeClickable(crudView.nameFilter)); - await crudView.nameFilter.clear(); - await crudView.nameFilter.sendKeys('MyReceiver'); - monitoringView.getFirstRowAsText().then((text) => { - expect(text).toEqual('MyReceiver pagerduty severity = warning'); - }); - }); - - it('edits a receiver correctly', async () => { - await crudView.isLoaded(); - await monitoringView.wait(until.elementToBeClickable(crudView.nameFilter)); - await crudView.nameFilter.clear(); - await crudView.nameFilter.sendKeys('MyReceiver'); - expect(crudView.resourceRows.count()).toBe(1); - await monitoringView.clickFirstRowKebabAction('Edit Receiver'); - await browser.wait(until.presenceOf(firstElementByTestID('cancel'))); - expect(firstElementByTestID('receiver-name').getAttribute('value')).toEqual('MyReceiver'); - expect(firstElementByTestID('dropdown-button').getText()).toEqual('PagerDuty'); - expect(firstElementByTestID('pagerduty-key-label').getText()).toEqual('Service key'); - expect(firstElementByTestID('integration-key').getAttribute('value')).toEqual( - '', - ); - expect(firstElementByTestID('label-name-0').getAttribute('value')).toEqual('severity'); - expect(firstElementByTestID('label-value-0').getAttribute('value')).toEqual('warning'); - - // Edit Values - - await firstElementByTestID('receiver-name').clear(); - await firstElementByTestID('receiver-name').sendKeys('MyEditedReceiver'); - await firstElementByTestID('label-name-0').clear(); - await firstElementByTestID('label-name-0').sendKeys('cluster'); - await firstElementByTestID('label-value-0').clear(); - await firstElementByTestID('label-value-0').sendKeys('MyCluster'); - - await monitoringView.saveButton.click(); - await crudView.isLoaded(); - await monitoringView.wait(until.elementToBeClickable(crudView.nameFilter)); - await crudView.nameFilter.clear(); - await crudView.nameFilter.sendKeys('MyEditedReceiver'); - monitoringView.getFirstRowAsText().then((text) => { - expect(text).toEqual('MyEditedReceiver pagerduty cluster = MyCluster'); - }); - }); - - it('deletes a receiver correctly', async () => { - await horizontalnavView.clickHorizontalTab('Details'); - await crudView.isLoaded(); - await monitoringView.wait(until.elementToBeClickable(crudView.nameFilter)); - await crudView.nameFilter.clear(); - await crudView.nameFilter.sendKeys('MyEditedReceiver'); - expect(crudView.resourceRows.count()).toBe(1); - - await monitoringView.clickFirstRowKebabAction('Delete Receiver'); - await browser.wait(until.presenceOf(monitoringView.saveButton)); - await monitoringView.saveButton.click(); - - await crudView.isLoaded(); - await monitoringView.wait(until.elementToBeClickable(crudView.nameFilter)); - await crudView.nameFilter.clear(); - await crudView.nameFilter.sendKeys('MyEditedReceiver'); - expect(crudView.resourceRows.count()).toBe(0); - }); - - it('prevents deletion of default receiver', async () => { - await crudView.isLoaded(); - await monitoringView.wait(until.elementToBeClickable(crudView.nameFilter)); - await crudView.nameFilter.clear(); - await crudView.nameFilter.sendKeys('Default'); - await monitoringView.openFirstRowKebabMenu(); - expect(monitoringView.disabledDeleteReceiverMenuItem.isPresent()).toBe(true); - }); - - it('prevents deletion and form edit of a receiver with sub-route', async () => { - // add receiver with sub-route - const yaml = `route: - routes: - - match: - service: database - receiver: team-DB-pager - routes: - - match: - owner: team-X - receiver: team-X-pager -receivers: -- name: 'team-X-pager' -- name: 'team-DB-pager'`; - await crudView.isLoaded(); - await horizontalnavView.clickHorizontalTab('YAML'); - await yamlView.isLoaded(); - await yamlView.setEditorContent(yaml); - await yamlView.saveButton.click(); - await yamlView.isLoaded(); - expect(monitoringView.successAlert.isPresent()).toBe(true); - - await horizontalnavView.clickHorizontalTab('Details'); - await monitoringView.openFirstRowKebabMenu(); - expect(monitoringView.disabledDeleteReceiverMenuItem.isPresent()).toBe(true); - expect(crudView.actionForLabel('Edit Receiver').isPresent()).toBe(true); - }); -}); diff --git a/frontend/integration-tests/views/monitoring.view.ts b/frontend/integration-tests/views/monitoring.view.ts deleted file mode 100644 index bd066578a18..00000000000 --- a/frontend/integration-tests/views/monitoring.view.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { Base64 } from 'js-base64'; - -import { $, $$, browser, by, element, ExpectedConditions as until } from 'protractor'; -import * as crudView from '../views/crud.view'; -import { firstElementByTestID } from '../protractor.conf'; - -export const wait = async (condition) => await browser.wait(condition, 20000); - -export const labels = $$('.co-label'); -export const saveButton = $('button[type=submit]'); - -// YAML form -export const successAlert = $('.pf-m-success'); - -// Configuration Overview -export const alertRoutingHeader = $('[data-test-section-heading="Alert routing"]'); -export const alertRoutingEditButton = $('.co-alert-manager-config__edit-alert-routing-btn'); -export const disabledDeleteReceiverMenuItem = $( - '.pf-c-dropdown__menu-item.pf-m-disabled[data-test-action="Delete Receiver"]', -); - -const firstRow = element.all(by.css(`[data-test-rows="resource-row"]`)).first(); - -export const openFirstRowKebabMenu = () => { - return firstRow - .$('[data-test-id="kebab-button"]') - .click() - .then(() => browser.wait(until.visibilityOf($('[data-test-id="action-items"]')))); -}; - -export const clickFirstRowKebabAction = (actionLabel: string) => { - return firstRow - .$('[data-test-id="kebab-button"]') - .click() - .then(() => browser.wait(until.elementToBeClickable(crudView.actionForLabel(actionLabel)))) - .then(() => crudView.actionForLabel(actionLabel).click()); -}; - -export const getFirstRowAsText = () => { - return firstRow.getText().then((text) => { - return text.replace(/[\n\r]/g, ' '); - }); -}; - -export const saveAsDefault = firstElementByTestID('save-as-default'); -export const sendResolvedAlerts = firstElementByTestID('send-resolved-alerts'); -export const showAdvancedConfiguration = $('button.pf-c-expandable-section__toggle'); -export const defaultAlertmanagerYaml = Base64.encode(`"global": - "resolve_timeout": "5m" -"inhibit_rules": -- "equal": - - "namespace" - - "alertname" - "source_match": - "severity": "critical" - "target_match_re": - "severity": "warning|info" -- "equal": - - "namespace" - - "alertname" - "source_match": - "severity": "warning" - "target_match_re": - "severity": "info" -"receivers": -- "name": "Default" -- "name": "Watchdog" -- "name": "Critical" -"route": - "group_by": - - "namespace" - "group_interval": "5m" - "group_wait": "30s" - "receiver": "Default" - "repeat_interval": "12h" - "routes": - - "match": - "alertname": "Watchdog" - "receiver": "Watchdog" - - "match": - "severity": "critical" - "receiver": "Critical"`); diff --git a/frontend/package.json b/frontend/package.json index b4dea945f7c..d6885b56a27 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -148,11 +148,11 @@ "@patternfly/quickstarts": "2.4.0", "@patternfly/react-catalog-view-extension": "4.96.0", "@patternfly/react-charts": "6.94.19", - "@patternfly/react-core": "4.276.8", + "@patternfly/react-core": "4.276.11", "@patternfly/react-log-viewer": "4.87.100", "@patternfly/react-table": "4.113.0", "@patternfly/react-tokens": "4.94.6", - "@patternfly/react-topology": "4.91.40", + "@patternfly/react-topology": "4.93.6", "@patternfly/react-virtualized-extension": "4.88.115", "@patternfly/react-user-feedback": "1.0.2", "@prometheus-io/codemirror-promql": "^0.37.0", @@ -181,6 +181,7 @@ "i18next-http-backend": "^1.0.21", "i18next-v4-format-converter": "^1.0.3", "immutable": "3.x", + "istextorbinary": "^9.5.0", "js-base64": "^2.5.1", "js-yaml": "^3.13.1", "json-schema": "^0.3.0", diff --git a/frontend/packages/console-app/console-extensions.json b/frontend/packages/console-app/console-extensions.json index 40e6e4db85a..9cd7dbe41f3 100644 --- a/frontend/packages/console-app/console-extensions.json +++ b/frontend/packages/console-app/console-extensions.json @@ -595,9 +595,6 @@ "dataAttributes": { "data-quickstart-id": "qs-nav-monitoring" } - }, - "flags": { - "required": ["PROMETHEUS", "MONITORING", "CAN_GET_NS"] } }, { diff --git a/frontend/packages/console-app/locales/en/console-app.json b/frontend/packages/console-app/locales/en/console-app.json index 47e05cdfe9b..de960d093bb 100644 --- a/frontend/packages/console-app/locales/en/console-app.json +++ b/frontend/packages/console-app/locales/en/console-app.json @@ -383,6 +383,7 @@ "Reprovision pending": "Reprovision pending", "Only one {{ machineHealthCheckLabel }} resource should match this node.": "Only one {{ machineHealthCheckLabel }} resource should match this node.", "Not configured": "Not configured", + "No conditions": "No conditions", "CPU": "CPU", "Memory": "Memory", "Filesystem": "Filesystem", diff --git a/frontend/packages/console-app/src/components/modals/clone/clone-pvc-modal.tsx b/frontend/packages/console-app/src/components/modals/clone/clone-pvc-modal.tsx index e03f40c3e8e..424e77afe96 100644 --- a/frontend/packages/console-app/src/components/modals/clone/clone-pvc-modal.tsx +++ b/frontend/packages/console-app/src/components/modals/clone/clone-pvc-modal.tsx @@ -23,6 +23,7 @@ import { validate, resourceObjPath, convertToBaseValue, + humanizeBinaryBytesWithoutB, } from '@console/internal/components/utils'; import { useK8sGet } from '@console/internal/components/utils/k8s-get-hook'; import { HandlePromiseProps } from '@console/internal/components/utils/promise-component'; @@ -47,8 +48,9 @@ const ClonePVCModal = withHandlePromise((props: ClonePVCModalProps) => { const { t } = useTranslation(); const { close, cancel, resource, handlePromise, errorMessage, inProgress } = props; const { name: pvcName, namespace } = resource?.metadata; - const defaultSize: string[] = validate.split(getRequestedPVCSize(resource)); - const pvcRequestedSize = `${defaultSize[0]} ${dropdownUnits[defaultSize[1]]}`; + const baseValue = convertToBaseValue(getRequestedPVCSize(resource)); + const defaultSize: string[] = validate.split(humanizeBinaryBytesWithoutB(baseValue).string); + const pvcRequestedSize = humanizeBinaryBytes(baseValue).string; const [clonePVCName, setClonePVCName] = React.useState(`${pvcName}-clone`); const [requestedSize, setRequestedSize] = React.useState(defaultSize[0] || ''); diff --git a/frontend/packages/console-app/src/components/network-policies/network-policy-form.tsx b/frontend/packages/console-app/src/components/network-policies/network-policy-form.tsx index 2645c1d1d10..5f3d2b016b3 100644 --- a/frontend/packages/console-app/src/components/network-policies/network-policy-form.tsx +++ b/frontend/packages/console-app/src/components/network-policies/network-policy-form.tsx @@ -19,6 +19,7 @@ import { ExternalLink, getNetworkPolicyDocURL, history, + isManaged, resourcePathFromModel, } from '@console/internal/components/utils'; import { NetworkPolicyModel } from '@console/internal/models'; @@ -217,13 +218,15 @@ export const NetworkPolicyForm: React.FC = ({ formData,

{t('Refer to your cluster administrator to know which network provider is used.')}

-

- {t('console-app~More information:')}  - -

+ {!isManaged() && ( +

+ {t('console-app~More information:')}  + +

+ )} )}
diff --git a/frontend/packages/console-app/src/components/nodes/node-dashboard/NodeHealth.tsx b/frontend/packages/console-app/src/components/nodes/node-dashboard/NodeHealth.tsx index f068be08173..5abe3db3797 100644 --- a/frontend/packages/console-app/src/components/nodes/node-dashboard/NodeHealth.tsx +++ b/frontend/packages/console-app/src/components/nodes/node-dashboard/NodeHealth.tsx @@ -200,28 +200,34 @@ export const getMachineHealth = ( } let failingConditions: number = 0; const conditions = _.flatten( - matchingHC.map((hc) => - hc.spec.unhealthyConditions.map((c) => { - const failing = isConditionFailing(node, c); - if (failing) { - failingConditions++; - } - return { - ...c, - failing, - }; - }), + matchingHC.map( + (hc) => + hc.spec?.unhealthyConditions?.map((c) => { + const failing = isConditionFailing(node, c); + if (failing) { + failingConditions++; + } + return { + ...c, + failing, + }; + }) ?? [], ), ); return { - state: failingConditions || matchingHC.length > 1 ? HealthState.WARNING : HealthState.OK, + state: + failingConditions || matchingHC.length > 1 || conditions.length === 0 + ? HealthState.WARNING + : HealthState.OK, details: matchingHC.length > 1 ? 'Multiple resources' : failingConditions ? `${pluralize(failingConditions, 'condition')} failing` - : `${pluralize(conditions.length, 'condition')} passing`, + : conditions.length > 0 + ? `${pluralize(conditions.length, 'condition')} passing` + : i18next.t('console-app~No conditions'), conditions, matchingHC, }; diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/api/common-types.ts b/frontend/packages/console-dynamic-plugin-sdk/src/api/common-types.ts index da656a9376d..bdc48c73555 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/src/api/common-types.ts +++ b/frontend/packages/console-dynamic-plugin-sdk/src/api/common-types.ts @@ -101,7 +101,8 @@ type K8sVerb = | 'patch' | 'delete' | 'deletecollection' - | 'watch'; + | 'watch' + | 'impersonate'; enum BadgeType { DEV = 'Dev Preview', diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts b/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts index 3cca375b373..11f2a0216c9 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts +++ b/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts @@ -78,7 +78,8 @@ export type K8sVerb = | 'patch' | 'delete' | 'deletecollection' - | 'watch'; + | 'watch' + | 'impersonate'; export type AccessReviewResourceAttributes = { group?: string; diff --git a/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx b/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx index cc4311e7176..450c54ed016 100644 --- a/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx +++ b/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx @@ -24,7 +24,7 @@ const CodeEditor = React.forwardRef((props, ref) const [usesValue] = React.useState(value !== undefined); const editorDidMount = React.useCallback( (editor, monaco) => { - const currentLanguage = editor.getModel().getModeId(); + const currentLanguage = editor.getModel()?.getModeId(); editor.layout(); editor.focus(); switch (currentLanguage) { @@ -37,7 +37,7 @@ const CodeEditor = React.forwardRef((props, ref) default: break; } - monaco.editor.getModels()[0].updateOptions({ tabSize: 2 }); + monaco.editor.getModels()[0]?.updateOptions({ tabSize: 2 }); onSave && editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_S, onSave); // eslint-disable-line no-bitwise }, [onSave, usesValue], diff --git a/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts b/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts index 3bf8040ef27..af8208382ce 100644 --- a/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts +++ b/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts @@ -16,7 +16,12 @@ const MODEL_URI = 'inmemory://model.yaml'; const MONACO_URI = monaco.Uri.parse(MODEL_URI); const createDocument = (model) => { - return TextDocument.create(MODEL_URI, model.getModeId(), model.getVersionId(), model.getValue()); + return TextDocument.create( + MODEL_URI, + model?.getModeId(), + model?.getVersionId(), + model?.getValue(), + ); }; // Unfortunately, `editor.focus()` doesn't work when hiding the shortcuts @@ -182,7 +187,7 @@ export const enableYAMLValidation = ( ) => { const pendingValidationRequests = new Map(); - const getModel = () => monaco.editor.getModels()[0]; + const getModel = () => monaco.editor?.getModels()[0]; const cleanPendingValidation = (document) => { const request = pendingValidationRequests.get(document.uri); @@ -221,7 +226,7 @@ export const enableYAMLValidation = ( tryFolding(); } - getModel().onDidChangeContent(() => { + getModel()?.onDidChangeContent(() => { tryFolding(); const document = createDocument(getModel()); diff --git a/frontend/packages/console-shared/src/components/formik-fields/NumberSpinnerField.tsx b/frontend/packages/console-shared/src/components/formik-fields/NumberSpinnerField.tsx index 046a29867a4..7c1fa806b19 100644 --- a/frontend/packages/console-shared/src/components/formik-fields/NumberSpinnerField.tsx +++ b/frontend/packages/console-shared/src/components/formik-fields/NumberSpinnerField.tsx @@ -7,7 +7,16 @@ import { useFormikValidationFix } from '../../hooks'; import { FieldProps } from './field-types'; import { getFieldId } from './field-utils'; -const NumberSpinnerField: React.FC = ({ label, helpText, required, ...props }) => { +interface NumberSpinnerFieldProps extends FieldProps { + setOutputAsIntegerFlag?: boolean; +} + +const NumberSpinnerField: React.FC = ({ + label, + helpText, + required, + ...props +}) => { const [field, { touched, error }] = useField(props.name); const { setFieldValue, setFieldTouched } = useFormikContext(); const fieldId = getFieldId(props.name, 'number-spinner'); @@ -19,9 +28,14 @@ const NumberSpinnerField: React.FC = ({ label, helpText, required, . const handleChange: React.ReactEventHandler = React.useCallback( (event) => { field.onChange(event); - setFieldValue(props.name, event.currentTarget.value); + setFieldValue( + props.name, + props?.setOutputAsIntegerFlag + ? _.toInteger(event.currentTarget.value) + : event.currentTarget.value, + ); }, - [field, props.name, setFieldValue], + [field, props.name, setFieldValue, props?.setOutputAsIntegerFlag], ); return ( diff --git a/frontend/packages/console-shared/src/components/quick-search/QuickSearchList.scss b/frontend/packages/console-shared/src/components/quick-search/QuickSearchList.scss index 3f17c04080a..327a2e59098 100644 --- a/frontend/packages/console-shared/src/components/quick-search/QuickSearchList.scss +++ b/frontend/packages/console-shared/src/components/quick-search/QuickSearchList.scss @@ -56,4 +56,7 @@ display: flex; flex-direction: column; } + &__secondary-label { + margin-left: var(--pf-v5-global--BorderWidth--md); + } } diff --git a/frontend/packages/console-shared/src/components/quick-search/QuickSearchList.tsx b/frontend/packages/console-shared/src/components/quick-search/QuickSearchList.tsx index 816ac4b0999..9a431756d93 100644 --- a/frontend/packages/console-shared/src/components/quick-search/QuickSearchList.tsx +++ b/frontend/packages/console-shared/src/components/quick-search/QuickSearchList.tsx @@ -131,16 +131,23 @@ const QuickSearchList: React.FC = ({ - + + + + + {item?.secondaryLabel && ( + + + + )} + - {item.secondaryLabel ?? ( - - {item.provider} - - )} + + {item.provider} + , diff --git a/frontend/packages/console-shared/src/constants/common.ts b/frontend/packages/console-shared/src/constants/common.ts index 8557276adf8..9320a33e4ee 100644 --- a/frontend/packages/console-shared/src/constants/common.ts +++ b/frontend/packages/console-shared/src/constants/common.ts @@ -85,6 +85,7 @@ export enum FLAGS { CONSOLE_NOTIFICATION = 'CONSOLE_NOTIFICATION', CONSOLE_EXTERNAL_LOG_LINK = 'CONSOLE_EXTERNAL_LOG_LINK', CONSOLE_YAML_SAMPLE = 'CONSOLE_YAML_SAMPLE', + DEVCONSOLE_PROXY = 'DEVCONSOLE_PROXY', } export const CONFIG_STORAGE_CONSOLE = 'console'; diff --git a/frontend/packages/console-shared/src/hooks/useServicesWatcher.ts b/frontend/packages/console-shared/src/hooks/useServicesWatcher.ts index 761d7d3272d..5d7bc54fd3a 100644 --- a/frontend/packages/console-shared/src/hooks/useServicesWatcher.ts +++ b/frontend/packages/console-shared/src/hooks/useServicesWatcher.ts @@ -6,7 +6,7 @@ import { getServicesForResource } from '../utils'; export const useServicesWatcher = ( resource: K8sResourceKind, ): { loaded: boolean; loadError: string; services: K8sResourceKind[] } => { - const { namespace } = resource.metadata; + const namespace = resource?.metadata?.namespace || ''; const [allServices, loaded, loadError] = useK8sWatchResource({ isList: true, kind: 'Service', diff --git a/frontend/packages/console-shared/src/types/backend-api.ts b/frontend/packages/console-shared/src/types/backend-api.ts new file mode 100644 index 00000000000..cc8e049ca9f --- /dev/null +++ b/frontend/packages/console-shared/src/types/backend-api.ts @@ -0,0 +1,5 @@ +export type DevConsoleEndpointResponse = { + statusCode: number; + headers: Record; + body: string; +}; diff --git a/frontend/packages/console-shared/src/types/index.ts b/frontend/packages/console-shared/src/types/index.ts index 4ae2dd1cb7a..967b2dabd3a 100644 --- a/frontend/packages/console-shared/src/types/index.ts +++ b/frontend/packages/console-shared/src/types/index.ts @@ -3,3 +3,4 @@ export * from './node'; export * from './resource'; export * from './route-params'; export * from './tableColumn'; +export * from './backend-api'; diff --git a/frontend/packages/console-shared/src/utils/proxy.ts b/frontend/packages/console-shared/src/utils/proxy.ts deleted file mode 100644 index c66dc53aad6..00000000000 --- a/frontend/packages/console-shared/src/utils/proxy.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { safeLoad } from 'js-yaml'; -import { consoleFetchJSON } from '@console/dynamic-plugin-sdk/src/lib-core'; -import { HttpError } from '@console/dynamic-plugin-sdk/src/utils/error/http-error'; - -export const API_PROXY_URL = '/api/dev-console/proxy/internet'; - -type ProxyRequest = { - method: string; - url: string; - headers?: Record; - queryparams?: Record; - body?: string; -}; - -export type ProxyResponse = { - statusCode: number; - headers: Record; - body: string; -}; - -const isJSONString = (str: string): boolean => { - try { - JSON.parse(str); - } catch (e) { - return false; - } - return true; -}; - -export const convertHeaders = (headers): Record => { - return Object.keys(headers).reduce((output, key) => { - output[key] = [headers[key]]; - return output; - }, {}); -}; - -/** - * Calls the proxy in our backend to bypass CORS headers. - */ -export const consoleProxyFetch = async (proxyRequest: ProxyRequest): Promise => { - const proxyResponse: ProxyResponse = await consoleFetchJSON.post(API_PROXY_URL, proxyRequest); - if (!proxyResponse.statusCode) { - throw new Error('Unexpected proxy response: Status code is missing!'); - } - if (proxyResponse.statusCode < 200 || proxyResponse.statusCode >= 300) { - throw new HttpError( - `Unexpected status code: ${proxyResponse.statusCode}`, - proxyResponse.statusCode, - null, - proxyResponse, - ); - } - return proxyResponse; -}; - -export const consoleProxyFetchJSON = (proxyRequest: ProxyRequest): Promise => { - return consoleProxyFetch(proxyRequest).then((response) => { - return isJSONString(response.body) ? JSON.parse(response.body) : safeLoad(response.body); - }); -}; diff --git a/frontend/packages/console-telemetry-plugin/src/listeners/const.ts b/frontend/packages/console-telemetry-plugin/src/listeners/const.ts index 63e95a738e6..7a4b2e71605 100644 --- a/frontend/packages/console-telemetry-plugin/src/listeners/const.ts +++ b/frontend/packages/console-telemetry-plugin/src/listeners/const.ts @@ -1,8 +1,3 @@ -export const SEGMENT_API_KEY = - window.SERVER_FLAGS?.telemetry?.DEVSANDBOX_SEGMENT_API_KEY || - window.SERVER_FLAGS?.telemetry?.SEGMENT_API_KEY || - ''; - export const TELEMETRY_DISABLED = window.SERVER_FLAGS?.telemetry?.DISABLED === 'true' || window.SERVER_FLAGS?.telemetry?.DEVSANDBOX_DISABLED === 'true'; diff --git a/frontend/packages/console-telemetry-plugin/src/listeners/segment.ts b/frontend/packages/console-telemetry-plugin/src/listeners/segment.ts index 3008a16f83f..137afc09d43 100644 --- a/frontend/packages/console-telemetry-plugin/src/listeners/segment.ts +++ b/frontend/packages/console-telemetry-plugin/src/listeners/segment.ts @@ -1,7 +1,36 @@ import { TelemetryEventListener } from '@console/dynamic-plugin-sdk/src'; -import { SEGMENT_API_KEY, TELEMETRY_DISABLED, TELEMETRY_DEBUG } from './const'; +import { TELEMETRY_DISABLED, TELEMETRY_DEBUG } from './const'; + +/** Segmnet API Key that looks like a hash */ +const apiKey = + window.SERVER_FLAGS?.telemetry?.DEVSANDBOX_SEGMENT_API_KEY || + window.SERVER_FLAGS?.telemetry?.SEGMENT_API_KEY || + ''; + +/** + * Segment `apiHost` parameter that should have the format like `api.segment.io/v1`. + * Is not defined here so that Segment can change it. + */ +const apiHost = window.SERVER_FLAGS?.telemetry?.SEGMENT_API_HOST || ''; + +/** Segment JS host. Default: `cdn.segment.com` */ +const jsHost = window.SERVER_FLAGS?.telemetry?.SEGMENT_JS_HOST || 'cdn.segment.com'; + +/** Full segment JS URL */ +const jsUrl = + window.SERVER_FLAGS?.telemetry?.SEGMENT_JS_URL || + `https://${jsHost}/analytics.js/v1/${encodeURIComponent(apiKey)}/analytics.min.js`; const initSegment = () => { + if (TELEMETRY_DEBUG) { + // eslint-disable-next-line no-console + console.debug('console-telemetry-plugin: initialize segment API with:', { + apiKey, + apiHost, + jsHost, + jsUrl, + }); + } // eslint-disable-next-line no-multi-assign const analytics = ((window as any).analytics = (window as any).analytics || []); if (analytics.initialize) { @@ -51,7 +80,7 @@ const initSegment = () => { const t = document.createElement('script'); t.type = 'text/javascript'; t.async = true; - t.src = `https://cdn.segment.com/analytics.js/v1/${encodeURIComponent(key)}/analytics.min.js`; + t.src = jsUrl; const n = document.getElementsByTagName('script')[0]; if (n.parentNode) { n.parentNode.insertBefore(t, n); @@ -60,10 +89,16 @@ const initSegment = () => { analytics._loadOptions = e; }; analytics.SNIPPET_VERSION = '4.13.1'; - analytics.load(SEGMENT_API_KEY); + const options: Record = {}; + if (apiHost) { + options.integrations = { 'Segment.io': { apiHost } }; + } + analytics.load(apiKey, options); }; -SEGMENT_API_KEY && !TELEMETRY_DISABLED && initSegment(); +if (!TELEMETRY_DISABLED && apiKey) { + initSegment(); +} const anonymousIP = { context: { @@ -80,7 +115,7 @@ export const eventListener: TelemetryEventListener = async ( console.debug('console-telemetry-plugin: received telemetry event:', eventType, properties); return; } - if (!SEGMENT_API_KEY || TELEMETRY_DISABLED) { + if (TELEMETRY_DISABLED || !apiKey) { return; } switch (eventType) { diff --git a/frontend/packages/dev-console/console-extensions.json b/frontend/packages/dev-console/console-extensions.json index f8659380a97..ba6252716dc 100644 --- a/frontend/packages/dev-console/console-extensions.json +++ b/frontend/packages/dev-console/console-extensions.json @@ -78,6 +78,17 @@ "flag": "OPENSHIFT_TEMPLATE" } }, + { + "type": "console.flag/model", + "properties": { + "model": { + "group": "tekton.dev", + "version": "v1", + "kind": "Pipeline" + }, + "flag": "OPENSHIFT_PIPELINE" + } + }, { "type": "console.cluster-configuration/item", @@ -334,6 +345,28 @@ "required": ["OPENSHIFT_BUILDCONFIG", "OPENSHIFT_IMAGESTREAM"] } }, + { + "type": "dev-console.add/action", + "properties": { + "id": "import-from-git", + "groupId": "git-repository", + "href": "/import/ns/:namespace", + "label": "%devconsole~Import from Git%", + "description": "%devconsole~Import code from your Git repository to be built and deployed%", + "icon": { "$codeRef": "icons.gitIconElement" }, + "accessReview": [ + { "group": "apps.openshift.io", "resource": "deploymentconfigs", "verb": "create" }, + { "group": "", "resource": "secrets", "verb": "create" }, + { "group": "route.openshift.io", "resource": "routes", "verb": "create" }, + { "group": "", "resource": "services", "verb": "create" }, + { "group": "tekton.dev", "resource": "pipelines", "verb": "create" } + ] + }, + "flags": { + "required": ["OPENSHIFT_PIPELINE"], + "disallowed": ["OPENSHIFT_BUILDCONFIG"] + } + }, { "type": "dev-console.add/action", "properties": { @@ -426,7 +459,7 @@ ] }, "flags": { - "required": ["JAVA_IMAGE_STREAM_ENABLED"] + "required": ["OPENSHIFT_BUILDCONFIG", "JAVA_IMAGE_STREAM_ENABLED"] } }, { @@ -492,7 +525,7 @@ "typeDescription": "%devconsole~**Builder Images** are container images that build source code for a particular language or framework.%" }, "flags": { - "required": ["OPENSHIFT_IMAGESTREAM"] + "required": ["OPENSHIFT_BUILDCONFIG", "OPENSHIFT_IMAGESTREAM"] } }, { @@ -504,7 +537,7 @@ "provider": { "$codeRef": "catalog.builderImageProvider" } }, "flags": { - "required": ["OPENSHIFT_IMAGESTREAM"] + "required": ["OPENSHIFT_BUILDCONFIG", "OPENSHIFT_IMAGESTREAM"] } }, { @@ -538,6 +571,9 @@ "title": "%devconsole~Devfiles%", "catalogDescription": "%devconsole~Browse for devfiles that support a particular language or framework. Cluster administrators can customize the content made available in the catalog.%", "typeDescription": "%devconsole~**Devfiles** are sets of objects for creating services, build configurations, and anything you have permission to create within a Project.%" + }, + "flags": { + "required": ["OPENSHIFT_BUILDCONFIG", "OPENSHIFT_IMAGESTREAM"] } }, { @@ -547,6 +583,9 @@ "type": "Devfile", "title": "%devconsole~Devfiles%", "provider": { "$codeRef": "catalog.devfileProvider" } + }, + "flags": { + "required": ["OPENSHIFT_BUILDCONFIG", "OPENSHIFT_IMAGESTREAM"] } }, { @@ -604,7 +643,7 @@ "provider": { "$codeRef": "catalog.builderImageSamplesProvider" } }, "flags": { - "required": ["OPENSHIFT_IMAGESTREAM"] + "required": ["OPENSHIFT_BUILDCONFIG", "OPENSHIFT_IMAGESTREAM"] } }, { @@ -614,6 +653,9 @@ "type": "Devfile", "title": "%devconsole~Devfile%", "provider": { "$codeRef": "catalog.devfileSamplesProvider" } + }, + "flags": { + "required": ["OPENSHIFT_BUILDCONFIG", "OPENSHIFT_IMAGESTREAM"] } }, { @@ -717,6 +759,9 @@ "data-quickstart-id": "qs-nav-builds", "data-test-id": "build-header" } + }, + "flags": { + "required": ["OPENSHIFT_BUILDCONFIG"] } }, { @@ -725,6 +770,9 @@ "exact": true, "path": ["/builds"], "component": { "$codeRef": "common.NamespaceRedirect" } + }, + "flags": { + "required": ["OPENSHIFT_BUILDCONFIG"] } }, { @@ -733,6 +781,9 @@ "exact": false, "path": ["/builds/all-namespaces", "/builds/ns/:ns"], "component": { "$codeRef": "builds.BuildsTabListPage" } + }, + "flags": { + "required": ["OPENSHIFT_BUILDCONFIG"] } }, { @@ -955,7 +1006,8 @@ "insertBefore": "devconsole.routingOptions" }, "flags": { - "disallowed": ["KNATIVE_SERVING_SERVICE"] + "disallowed": ["KNATIVE_SERVING_SERVICE"], + "required": ["OPENSHIFT_DEPLOYMENTCONFIG"] } }, { @@ -996,6 +1048,74 @@ "insertBefore": "devconsole.routingOptions" }, "flags": { + "required": ["KNATIVE_SERVING_SERVICE", "OPENSHIFT_DEPLOYMENTCONFIG"] + } + }, + { + "type": "console.user-preference/item", + "properties": { + "id": "devconsole.preferredResource", + "label": "%devconsole~Resource type%", + "groupId": "applications", + "description": "%devconsole~If resource type is not selected, the console default to the latest.%", + "field": { + "type": "dropdown", + "userSettingsKey": "devconsole.preferredResourceType", + "defaultValue": "kubernetes", + "description": "%devconsole~The defaults below will only apply to the Import from Git and Deploy Image forms when creating Deployments or Deployment Configs.%", + "options": [ + { + "value": "latest", + "label": "%devconsole~Last used%", + "description": "%devconsole~The last resource type used when adding a resource.%" + }, + { + "value": "kubernetes", + "label": "%devconsole~Deployment%", + "description": "%devconsole~A Deployment enables declarative updates for Pods and ReplicaSets.%" + } + ] + }, + "insertBefore": "devconsole.routingOptions" + }, + "flags": { + "disallowed": ["OPENSHIFT_DEPLOYMENTCONFIG", "KNATIVE_SERVING_SERVICE"] + } + }, + { + "type": "console.user-preference/item", + "properties": { + "id": "devconsole.preferredResource", + "label": "%devconsole~Resource type%", + "groupId": "applications", + "description": "%devconsole~If resource type is not selected, the console default to the latest.%", + "field": { + "type": "dropdown", + "userSettingsKey": "devconsole.preferredResourceType", + "defaultValue": "kubernetes", + "description": "%devconsole~The defaults below will only apply to the Import from Git and Deploy Image forms when creating Deployments or Deployment Configs.%", + "options": [ + { + "value": "latest", + "label": "%devconsole~Last used%", + "description": "%devconsole~The last resource type used when adding a resource.%" + }, + { + "value": "kubernetes", + "label": "%devconsole~Deployment%", + "description": "%devconsole~A Deployment enables declarative updates for Pods and ReplicaSets.%" + }, + { + "value": "knative", + "label": "%devconsole~Serverless Deployment%", + "description": "%devconsole~A type of deployment that enables Serverless scaling to 0 when idle.%" + } + ] + }, + "insertBefore": "devconsole.routingOptions" + }, + "flags": { + "disallowed": ["OPENSHIFT_DEPLOYMENTCONFIG"], "required": ["KNATIVE_SERVING_SERVICE"] } }, @@ -1127,6 +1247,17 @@ "required": ["OPENSHIFT_BUILDCONFIG", "OPENSHIFT_IMAGESTREAM"] } }, + { + "type": "console.page/route", + "properties": { + "exact": true, + "path": ["/import/all-namespaces", "/import/ns/:ns"], + "component": { "$codeRef": "import.ImportPage" } + }, + "flags": { + "required": ["OPENSHIFT_PIPELINE"] + } + }, { "type": "console.page/route", "properties": { diff --git a/frontend/packages/dev-console/integration-tests/features/deployment/deployment-dev-perspective.feature b/frontend/packages/dev-console/integration-tests/features/deployment/deployment-dev-perspective.feature index 551b290c8fd..6b87f4403c8 100644 --- a/frontend/packages/dev-console/integration-tests/features/deployment/deployment-dev-perspective.feature +++ b/frontend/packages/dev-console/integration-tests/features/deployment/deployment-dev-perspective.feature @@ -23,7 +23,7 @@ Feature: Deployment form view | test-depoyment1 | Rolling Update | image-registry.openshift-image-registry.svc:5000/openshift/httpd:latest | | test-depoyment2 | Recreate | image-registry.openshift-image-registry.svc:5000/openshift/httpd:latest | - @smoke + @smoke Scenario Outline: Create deployment using ImageStream in form view: D-01-TC02 Given user is at Deployments page When user clicks on Create Deployment @@ -36,3 +36,21 @@ Feature: Deployment form view | deployment_name | strategy_type | imagestream_id | | cli | Rolling Update | #cli-link | | dotnet | Recreate | #dotnet-link | + + @smoke + Scenario Outline: Create and edit deployment using form view: D-01-TC03 + Given user is at Deployments page + When user clicks on Create Deployment + And user selects ImageStream with id "" + And user clicks on Create button + And user clicks Edit Deployment from the action menu + And user select auto deploy checkbox + And user clicks on Save + And user clicks on the workload "" to open the sidebar + And user clicks Edit Deployment from the action menu + Then user sees auto deploy option is checked + + + Examples: + | deployment_name | imagestream_id | + | golang | #golang-link | diff --git a/frontend/packages/dev-console/integration-tests/support/pages/add-flow/git-page.ts b/frontend/packages/dev-console/integration-tests/support/pages/add-flow/git-page.ts index a41e4026a60..823f66ca8ba 100644 --- a/frontend/packages/dev-console/integration-tests/support/pages/add-flow/git-page.ts +++ b/frontend/packages/dev-console/integration-tests/support/pages/add-flow/git-page.ts @@ -45,6 +45,11 @@ export const gitPage = { statusCode: responses.devFileResources ? 200 : 404, body: responses.devFileResources, }).as('getDevfileResources'); + + cy.intercept('GET', `${apiBaseUrl}/contents//func.yaml`, { + statusCode: responses.funcJson ? 200 : 404, + body: responses.funcJson, + }).as('getFuncJson'); } cy.get(gitPO.gitRepoUrl).clear().type(gitUrl); diff --git a/frontend/packages/dev-console/integration-tests/support/step-definitions/deployment/deployment-dev-perspective.ts b/frontend/packages/dev-console/integration-tests/support/step-definitions/deployment/deployment-dev-perspective.ts index bd5630e98d4..7fafde43429 100644 --- a/frontend/packages/dev-console/integration-tests/support/step-definitions/deployment/deployment-dev-perspective.ts +++ b/frontend/packages/dev-console/integration-tests/support/step-definitions/deployment/deployment-dev-perspective.ts @@ -2,7 +2,7 @@ import { Given, When, Then } from 'cypress-cucumber-preprocessor/steps'; import { detailsPage } from '@console/cypress-integration-tests/views/details-page'; import { devNavigationMenu } from '../../constants'; import { deploymentStrategyFormP0, eventSourcePO, pagePO } from '../../pageObjects'; -import { createForm, navigateTo } from '../../pages'; +import { createForm, navigateTo, topologyPage, topologySidePane } from '../../pages'; Given('user is at Deployments page', () => { navigateTo(devNavigationMenu.Deployments); @@ -52,3 +52,26 @@ When('user selects ImageStream with id {string}', (imageStreamId: string) => { cy.get(deploymentStrategyFormP0.images.selectTagLatest).click(); cy.get(eventSourcePO.createPingSource.name).scrollIntoView(); }); + +When('user select auto deploy checkbox', () => { + cy.get('#form-checkbox-formData-triggers-image-field').should('not.be.checked'); + cy.get('#form-checkbox-formData-triggers-image-field').check(); +}); + +When('user clicks on Save', () => { + cy.byLegacyTestID('submit-button').click(); +}); + +Then('user sees auto deploy option is checked', () => { + cy.get('#form-checkbox-formData-triggers-image-field').should('be.checked'); +}); + +When('user clicks on the workload {string} to open the sidebar', (nodeName: string) => { + topologyPage.clickOnNode(nodeName); + topologySidePane.verify(); +}); + +When('user clicks Edit Deployment from the action menu', () => { + cy.byLegacyTestID('actions-menu-button').click(); + cy.byTestActionID('Edit Deployment').click(); +}); diff --git a/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-node-env/contents.json b/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-node-env/contents.json new file mode 100644 index 00000000000..02470b5c031 --- /dev/null +++ b/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-node-env/contents.json @@ -0,0 +1,114 @@ +[ + { + "name": ".gitignore", + "path": ".gitignore", + "sha": "3abd5fdff11e295a0d72346ac99fe7034da1c6cb", + "size": 126, + "url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/contents/.gitignore?ref=master", + "html_url": "https://github.com/vikram-raj/hello-func-node-env/blob/master/.gitignore", + "git_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/git/blobs/3abd5fdff11e295a0d72346ac99fe7034da1c6cb", + "download_url": "https://raw.githubusercontent.com/vikram-raj/hello-func-node-env/master/.gitignore", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/vikram-raj/hello-func-node-env/contents/.gitignore?ref=master", + "git": "https://api.github.com/repos/vikram-raj/hello-func-node-env/git/blobs/3abd5fdff11e295a0d72346ac99fe7034da1c6cb", + "html": "https://github.com/vikram-raj/hello-func-node-env/blob/master/.gitignore" + } + }, + { + "name": "README.md", + "path": "README.md", + "sha": "3ecedd17b45af0a59d4a52dd648eda8ba090047a", + "size": 2096, + "url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/contents/README.md?ref=master", + "html_url": "https://github.com/vikram-raj/hello-func-node-env/blob/master/README.md", + "git_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/git/blobs/3ecedd17b45af0a59d4a52dd648eda8ba090047a", + "download_url": "https://raw.githubusercontent.com/vikram-raj/hello-func-node-env/master/README.md", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/vikram-raj/hello-func-node-env/contents/README.md?ref=master", + "git": "https://api.github.com/repos/vikram-raj/hello-func-node-env/git/blobs/3ecedd17b45af0a59d4a52dd648eda8ba090047a", + "html": "https://github.com/vikram-raj/hello-func-node-env/blob/master/README.md" + } + }, + { + "name": "func.yaml", + "path": "func.yaml", + "sha": "0ee31c9ad466045e40efa70433806dd40f1ca3c7", + "size": 721, + "url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/contents/func.yaml?ref=master", + "html_url": "https://github.com/vikram-raj/hello-func-node-env/blob/master/func.yaml", + "git_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/git/blobs/0ee31c9ad466045e40efa70433806dd40f1ca3c7", + "download_url": "https://raw.githubusercontent.com/vikram-raj/hello-func-node-env/master/func.yaml", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/vikram-raj/hello-func-node-env/contents/func.yaml?ref=master", + "git": "https://api.github.com/repos/vikram-raj/hello-func-node-env/git/blobs/0ee31c9ad466045e40efa70433806dd40f1ca3c7", + "html": "https://github.com/vikram-raj/hello-func-node-env/blob/master/func.yaml" + } + }, + { + "name": "index.js", + "path": "index.js", + "sha": "04c81441d0b3e3e39bee13343c4058956f4ee0e4", + "size": 1463, + "url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/contents/index.js?ref=master", + "html_url": "https://github.com/vikram-raj/hello-func-node-env/blob/master/index.js", + "git_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/git/blobs/04c81441d0b3e3e39bee13343c4058956f4ee0e4", + "download_url": "https://raw.githubusercontent.com/vikram-raj/hello-func-node-env/master/index.js", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/vikram-raj/hello-func-node-env/contents/index.js?ref=master", + "git": "https://api.github.com/repos/vikram-raj/hello-func-node-env/git/blobs/04c81441d0b3e3e39bee13343c4058956f4ee0e4", + "html": "https://github.com/vikram-raj/hello-func-node-env/blob/master/index.js" + } + }, + { + "name": "package-lock.json", + "path": "package-lock.json", + "sha": "14391b4480f8919aa6cedfe7e92585eb55259d8a", + "size": 151831, + "url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/contents/package-lock.json?ref=master", + "html_url": "https://github.com/vikram-raj/hello-func-node-env/blob/master/package-lock.json", + "git_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/git/blobs/14391b4480f8919aa6cedfe7e92585eb55259d8a", + "download_url": "https://raw.githubusercontent.com/vikram-raj/hello-func-node-env/master/package-lock.json", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/vikram-raj/hello-func-node-env/contents/package-lock.json?ref=master", + "git": "https://api.github.com/repos/vikram-raj/hello-func-node-env/git/blobs/14391b4480f8919aa6cedfe7e92585eb55259d8a", + "html": "https://github.com/vikram-raj/hello-func-node-env/blob/master/package-lock.json" + } + }, + { + "name": "package.json", + "path": "package.json", + "sha": "2806b7a4ce0a7caf6f1e3d9c0a833c81db97f7eb", + "size": 565, + "url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/contents/package.json?ref=master", + "html_url": "https://github.com/vikram-raj/hello-func-node-env/blob/master/package.json", + "git_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/git/blobs/2806b7a4ce0a7caf6f1e3d9c0a833c81db97f7eb", + "download_url": "https://raw.githubusercontent.com/vikram-raj/hello-func-node-env/master/package.json", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/vikram-raj/hello-func-node-env/contents/package.json?ref=master", + "git": "https://api.github.com/repos/vikram-raj/hello-func-node-env/git/blobs/2806b7a4ce0a7caf6f1e3d9c0a833c81db97f7eb", + "html": "https://github.com/vikram-raj/hello-func-node-env/blob/master/package.json" + } + }, + { + "name": "test", + "path": "test", + "sha": "9cea2aab5cb7d30f925060499367172b566d0842", + "size": 0, + "url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/contents/test?ref=master", + "html_url": "https://github.com/vikram-raj/hello-func-node-env/tree/master/test", + "git_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/git/trees/9cea2aab5cb7d30f925060499367172b566d0842", + "download_url": null, + "type": "dir", + "_links": { + "self": "https://api.github.com/repos/vikram-raj/hello-func-node-env/contents/test?ref=master", + "git": "https://api.github.com/repos/vikram-raj/hello-func-node-env/git/trees/9cea2aab5cb7d30f925060499367172b566d0842", + "html": "https://github.com/vikram-raj/hello-func-node-env/tree/master/test" + } + } + ] diff --git a/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-node-env/func.json b/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-node-env/func.json new file mode 100644 index 00000000000..f0d1e760d81 --- /dev/null +++ b/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-node-env/func.json @@ -0,0 +1,18 @@ +{ + "name": "func.yaml", + "path": "func.yaml", + "sha": "0ee31c9ad466045e40efa70433806dd40f1ca3c7", + "size": 721, + "url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/contents/func.yaml?ref=master", + "html_url": "https://github.com/vikram-raj/hello-func-node-env/blob/master/func.yaml", + "git_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/git/blobs/0ee31c9ad466045e40efa70433806dd40f1ca3c7", + "download_url": "https://raw.githubusercontent.com/vikram-raj/hello-func-node-env/master/func.yaml", + "type": "file", + "content": "c3BlY1ZlcnNpb246IDAuMzQuMApuYW1lOiBoZWxsby1mdW5jLW5vZGUtZW52\nCnJ1bnRpbWU6IG5vZGUKcmVnaXN0cnk6IHF1YXkuaW8vdmlyYWpfMQppbWFn\nZTogaW1hZ2UtcmVnaXN0cnkub3BlbnNoaWZ0LWltYWdlLXJlZ2lzdHJ5LnN2\nYzo1MDAwL2RlZmF1bHQvaGVsbG8tZnVuYy1ub2RlLWVudjpsYXRlc3QKaW1h\nZ2VEaWdlc3Q6IHNoYTI1NjoyZjNmMzY2ODM3NDllZDM0YWNiNzVjNjYxMjhl\nYmM3MmVjNmYxZWVlN2RjMTUwNjAyOTMxMTEyMzkzZWI3OGE5CmNyZWF0ZWQ6\nIDIwMjItMTItMjdUMDc6NTQ6NDguMzU2NTY5MjY1KzA1OjMwCmludm9jYXRp\nb246CiAgZm9ybWF0OiBodHRwCmJ1aWxkOgogIGJ1aWxkcGFja3M6IFtdCiAg\nYnVpbGRlcjogczJpCiAgYnVpbGRFbnZzOgogIC0gbmFtZTogYnVpbGRFbnYK\nICAgIHZhbHVlOiBidWlsZEVudlZhbAogIC0gbmFtZTogYnVpbGRFbnYxCiAg\nICB2YWx1ZTogYnVpbGRFbnZWYWwxCnJ1bjoKICB2b2x1bWVzOiBbXQogIGVu\ndnM6CiAgLSBuYW1lOiBoZWxsbwogICAgdmFsdWU6IHdvcmxkCiAgLSBuYW1l\nOiBlbnYKICAgIHZhbHVlOiBlbnZWYWx1ZQpkZXBsb3k6CiAgbmFtZXNwYWNl\nOiB2aXJhagogIGFubm90YXRpb25zOiB7fQogIG9wdGlvbnM6IHt9CiAgbGFi\nZWxzOiBbXQogIGhlYWx0aEVuZHBvaW50czoKICAgIGxpdmVuZXNzOiAvaGVh\nbHRoL2xpdmVuZXNzCiAgICByZWFkaW5lc3M6IC9oZWFsdGgvcmVhZGluZXNz\nCg==\n", + "encoding": "base64", + "_links": { + "self": "https://api.github.com/repos/vikram-raj/hello-func-node-env/contents/func.yaml?ref=master", + "git": "https://api.github.com/repos/vikram-raj/hello-func-node-env/git/blobs/0ee31c9ad466045e40efa70433806dd40f1ca3c7", + "html": "https://github.com/vikram-raj/hello-func-node-env/blob/master/func.yaml" + } +} \ No newline at end of file diff --git a/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-node-env/package.json b/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-node-env/package.json new file mode 100644 index 00000000000..9b34d155c95 --- /dev/null +++ b/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-node-env/package.json @@ -0,0 +1,18 @@ +{ + "name": "package.json", + "path": "package.json", + "sha": "2806b7a4ce0a7caf6f1e3d9c0a833c81db97f7eb", + "size": 565, + "url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/contents/package.json?ref=master", + "html_url": "https://github.com/vikram-raj/hello-func-node-env/blob/master/package.json", + "git_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/git/blobs/2806b7a4ce0a7caf6f1e3d9c0a833c81db97f7eb", + "download_url": "https://raw.githubusercontent.com/vikram-raj/hello-func-node-env/master/package.json", + "type": "file", + "content": "ewogICJuYW1lIjogImh0dHAtaGFuZGxlciIsCiAgInZlcnNpb24iOiAiMC4x\nLjAiLAogICJkZXNjcmlwdGlvbiI6ICJBIGZ1bmN0aW9uIHdoaWNoIHJlc3Bv\nbmRzIHRvIEhUVFAgcmVxdWVzdHMiLAogICJtYWluIjogImluZGV4LmpzIiwK\nICAic2NyaXB0cyI6IHsKICAgICJ0ZXN0IjogIm5vZGUgdGVzdC91bml0Lmpz\nICYmIG5vZGUgdGVzdC9pbnRlZ3JhdGlvbi5qcyIsCiAgICAic3RhcnQiOiAi\nZmFhcy1qcy1ydW50aW1lIC4vaW5kZXguanMiLAogICAgImRlYnVnIjogIm5v\nZGVtb24gLS1pbnNwZWN0IC4vbm9kZV9tb2R1bGVzL2ZhYXMtanMtcnVudGlt\nZS9iaW4vY2xpLmpzIC4vaW5kZXguanMiCiAgfSwKICAia2V5d29yZHMiOiBb\nXSwKICAiYXV0aG9yIjogIiIsCiAgImxpY2Vuc2UiOiAiQXBhY2hlLTIuMCIs\nCiAgImRlcGVuZGVuY2llcyI6IHsKICAgICJmYWFzLWpzLXJ1bnRpbWUiOiAi\nXjAuOS4wIgogIH0sCiAgImRldkRlcGVuZGVuY2llcyI6IHsKICAgICJub2Rl\nbW9uIjogIl4yLjAuNCIsCiAgICAic3VwZXJ0ZXN0IjogIl40LjAuMiIsCiAg\nICAidGFwZSI6ICJeNS4wLjEiCiAgfQp9Cg==\n", + "encoding": "base64", + "_links": { + "self": "https://api.github.com/repos/vikram-raj/hello-func-node-env/contents/package.json?ref=master", + "git": "https://api.github.com/repos/vikram-raj/hello-func-node-env/git/blobs/2806b7a4ce0a7caf6f1e3d9c0a833c81db97f7eb", + "html": "https://github.com/vikram-raj/hello-func-node-env/blob/master/package.json" + } +} diff --git a/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-node-env/repo.json b/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-node-env/repo.json new file mode 100644 index 00000000000..7e1b3ef25b2 --- /dev/null +++ b/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-node-env/repo.json @@ -0,0 +1,105 @@ +{ + "id": 582727984, + "node_id": "R_kgDOIru5MA", + "name": "hello-func-node-env", + "full_name": "vikram-raj/hello-func-node-env", + "private": false, + "owner": { + "login": "vikram-raj", + "id": 2561818, + "node_id": "MDQ6VXNlcjI1NjE4MTg=", + "avatar_url": "https://avatars.githubusercontent.com/u/2561818?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/vikram-raj", + "html_url": "https://github.com/vikram-raj", + "followers_url": "https://api.github.com/users/vikram-raj/followers", + "following_url": "https://api.github.com/users/vikram-raj/following{/other_user}", + "gists_url": "https://api.github.com/users/vikram-raj/gists{/gist_id}", + "starred_url": "https://api.github.com/users/vikram-raj/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/vikram-raj/subscriptions", + "organizations_url": "https://api.github.com/users/vikram-raj/orgs", + "repos_url": "https://api.github.com/users/vikram-raj/repos", + "events_url": "https://api.github.com/users/vikram-raj/events{/privacy}", + "received_events_url": "https://api.github.com/users/vikram-raj/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/vikram-raj/hello-func-node-env", + "description": null, + "fork": false, + "url": "https://api.github.com/repos/vikram-raj/hello-func-node-env", + "forks_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/forks", + "keys_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/teams", + "hooks_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/hooks", + "issue_events_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/issues/events{/number}", + "events_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/events", + "assignees_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/assignees{/user}", + "branches_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/branches{/branch}", + "tags_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/tags", + "blobs_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/statuses/{sha}", + "languages_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/languages", + "stargazers_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/stargazers", + "contributors_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/contributors", + "subscribers_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/subscribers", + "subscription_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/subscription", + "commits_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/contents/{+path}", + "compare_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/merges", + "archive_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/downloads", + "issues_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/issues{/number}", + "pulls_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/pulls{/number}", + "milestones_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/milestones{/number}", + "notifications_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/labels{/name}", + "releases_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/releases{/id}", + "deployments_url": "https://api.github.com/repos/vikram-raj/hello-func-node-env/deployments", + "created_at": "2022-12-27T17:45:59Z", + "updated_at": "2022-12-27T17:47:32Z", + "pushed_at": "2022-12-27T17:47:28Z", + "git_url": "git://github.com/vikram-raj/hello-func-node-env.git", + "ssh_url": "git@github.com:vikram-raj/hello-func-node-env.git", + "clone_url": "https://github.com/vikram-raj/hello-func-node-env.git", + "svn_url": "https://github.com/vikram-raj/hello-func-node-env", + "homepage": null, + "size": 45, + "stargazers_count": 0, + "watchers_count": 0, + "language": "JavaScript", + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "has_discussions": false, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 0, + "license": null, + "allow_forking": true, + "is_template": false, + "web_commit_signoff_required": false, + "topics": [ + + ], + "visibility": "public", + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "temp_clone_token": null, + "network_count": 0, + "subscribers_count": 1 + } diff --git a/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-node/contents.json b/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-node/contents.json new file mode 100644 index 00000000000..50d4a3e0666 --- /dev/null +++ b/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-node/contents.json @@ -0,0 +1,114 @@ +[ + { + "name": ".gitignore", + "path": ".gitignore", + "sha": "3abd5fdff11e295a0d72346ac99fe7034da1c6cb", + "size": 126, + "url": "https://api.github.com/repos/vikram-raj/hello-func-node/contents/.gitignore?ref=master", + "html_url": "https://github.com/vikram-raj/hello-func-node/blob/master/.gitignore", + "git_url": "https://api.github.com/repos/vikram-raj/hello-func-node/git/blobs/3abd5fdff11e295a0d72346ac99fe7034da1c6cb", + "download_url": "https://raw.githubusercontent.com/vikram-raj/hello-func-node/master/.gitignore", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/vikram-raj/hello-func-node/contents/.gitignore?ref=master", + "git": "https://api.github.com/repos/vikram-raj/hello-func-node/git/blobs/3abd5fdff11e295a0d72346ac99fe7034da1c6cb", + "html": "https://github.com/vikram-raj/hello-func-node/blob/master/.gitignore" + } + }, + { + "name": "README.md", + "path": "README.md", + "sha": "3ecedd17b45af0a59d4a52dd648eda8ba090047a", + "size": 2096, + "url": "https://api.github.com/repos/vikram-raj/hello-func-node/contents/README.md?ref=master", + "html_url": "https://github.com/vikram-raj/hello-func-node/blob/master/README.md", + "git_url": "https://api.github.com/repos/vikram-raj/hello-func-node/git/blobs/3ecedd17b45af0a59d4a52dd648eda8ba090047a", + "download_url": "https://raw.githubusercontent.com/vikram-raj/hello-func-node/master/README.md", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/vikram-raj/hello-func-node/contents/README.md?ref=master", + "git": "https://api.github.com/repos/vikram-raj/hello-func-node/git/blobs/3ecedd17b45af0a59d4a52dd648eda8ba090047a", + "html": "https://github.com/vikram-raj/hello-func-node/blob/master/README.md" + } + }, + { + "name": "func.yaml", + "path": "func.yaml", + "sha": "296531d91cee272407f90494bca2a20736b4ff30", + "size": 364, + "url": "https://api.github.com/repos/vikram-raj/hello-func-node/contents/func.yaml?ref=master", + "html_url": "https://github.com/vikram-raj/hello-func-node/blob/master/func.yaml", + "git_url": "https://api.github.com/repos/vikram-raj/hello-func-node/git/blobs/296531d91cee272407f90494bca2a20736b4ff30", + "download_url": "https://raw.githubusercontent.com/vikram-raj/hello-func-node/master/func.yaml", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/vikram-raj/hello-func-node/contents/func.yaml?ref=master", + "git": "https://api.github.com/repos/vikram-raj/hello-func-node/git/blobs/296531d91cee272407f90494bca2a20736b4ff30", + "html": "https://github.com/vikram-raj/hello-func-node/blob/master/func.yaml" + } + }, + { + "name": "index.js", + "path": "index.js", + "sha": "04c81441d0b3e3e39bee13343c4058956f4ee0e4", + "size": 1463, + "url": "https://api.github.com/repos/vikram-raj/hello-func-node/contents/index.js?ref=master", + "html_url": "https://github.com/vikram-raj/hello-func-node/blob/master/index.js", + "git_url": "https://api.github.com/repos/vikram-raj/hello-func-node/git/blobs/04c81441d0b3e3e39bee13343c4058956f4ee0e4", + "download_url": "https://raw.githubusercontent.com/vikram-raj/hello-func-node/master/index.js", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/vikram-raj/hello-func-node/contents/index.js?ref=master", + "git": "https://api.github.com/repos/vikram-raj/hello-func-node/git/blobs/04c81441d0b3e3e39bee13343c4058956f4ee0e4", + "html": "https://github.com/vikram-raj/hello-func-node/blob/master/index.js" + } + }, + { + "name": "package-lock.json", + "path": "package-lock.json", + "sha": "14391b4480f8919aa6cedfe7e92585eb55259d8a", + "size": 151831, + "url": "https://api.github.com/repos/vikram-raj/hello-func-node/contents/package-lock.json?ref=master", + "html_url": "https://github.com/vikram-raj/hello-func-node/blob/master/package-lock.json", + "git_url": "https://api.github.com/repos/vikram-raj/hello-func-node/git/blobs/14391b4480f8919aa6cedfe7e92585eb55259d8a", + "download_url": "https://raw.githubusercontent.com/vikram-raj/hello-func-node/master/package-lock.json", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/vikram-raj/hello-func-node/contents/package-lock.json?ref=master", + "git": "https://api.github.com/repos/vikram-raj/hello-func-node/git/blobs/14391b4480f8919aa6cedfe7e92585eb55259d8a", + "html": "https://github.com/vikram-raj/hello-func-node/blob/master/package-lock.json" + } + }, + { + "name": "package.json", + "path": "package.json", + "sha": "2806b7a4ce0a7caf6f1e3d9c0a833c81db97f7eb", + "size": 565, + "url": "https://api.github.com/repos/vikram-raj/hello-func-node/contents/package.json?ref=master", + "html_url": "https://github.com/vikram-raj/hello-func-node/blob/master/package.json", + "git_url": "https://api.github.com/repos/vikram-raj/hello-func-node/git/blobs/2806b7a4ce0a7caf6f1e3d9c0a833c81db97f7eb", + "download_url": "https://raw.githubusercontent.com/vikram-raj/hello-func-node/master/package.json", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/vikram-raj/hello-func-node/contents/package.json?ref=master", + "git": "https://api.github.com/repos/vikram-raj/hello-func-node/git/blobs/2806b7a4ce0a7caf6f1e3d9c0a833c81db97f7eb", + "html": "https://github.com/vikram-raj/hello-func-node/blob/master/package.json" + } + }, + { + "name": "test", + "path": "test", + "sha": "9cea2aab5cb7d30f925060499367172b566d0842", + "size": 0, + "url": "https://api.github.com/repos/vikram-raj/hello-func-node/contents/test?ref=master", + "html_url": "https://github.com/vikram-raj/hello-func-node/tree/master/test", + "git_url": "https://api.github.com/repos/vikram-raj/hello-func-node/git/trees/9cea2aab5cb7d30f925060499367172b566d0842", + "download_url": null, + "type": "dir", + "_links": { + "self": "https://api.github.com/repos/vikram-raj/hello-func-node/contents/test?ref=master", + "git": "https://api.github.com/repos/vikram-raj/hello-func-node/git/trees/9cea2aab5cb7d30f925060499367172b566d0842", + "html": "https://github.com/vikram-raj/hello-func-node/tree/master/test" + } + } + ] \ No newline at end of file diff --git a/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-node/func.json b/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-node/func.json new file mode 100644 index 00000000000..65a3a91ca74 --- /dev/null +++ b/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-node/func.json @@ -0,0 +1,18 @@ +{ + "name": "func.yaml", + "path": "func.yaml", + "sha": "296531d91cee272407f90494bca2a20736b4ff30", + "size": 364, + "url": "https://api.github.com/repos/vikram-raj/hello-func-node/contents/func.yaml?ref=master", + "html_url": "https://github.com/vikram-raj/hello-func-node/blob/master/func.yaml", + "git_url": "https://api.github.com/repos/vikram-raj/hello-func-node/git/blobs/296531d91cee272407f90494bca2a20736b4ff30", + "download_url": "https://raw.githubusercontent.com/vikram-raj/hello-func-node/master/func.yaml", + "type": "file", + "content": "c3BlY1ZlcnNpb246IDAuMzQuMApuYW1lOiBoZWxsby1mdW5jLW5vZGUKcnVu\ndGltZTogbm9kZQpyZWdpc3RyeTogJycKaW1hZ2U6ICcnCmludm9jYXRpb246\nCiAgZm9ybWF0OiBodHRwCmJ1aWxkOgogIGJ1aWxkcGFja3M6IFtdCiAgYnVp\nbGRlcjogczJpCiAgYnVpbGRFbnZzOiBbXQpydW46CiAgdm9sdW1lczogW10K\nICBlbnZzOgogIC0gbmFtZTogTVlfQVBJX0tFWQogICAgdmFsdWU6ICcxMjM0\nNScKZGVwbG95OgogIGFubm90YXRpb25zOiB7fQogIG9wdGlvbnM6IHt9CiAg\nbGFiZWxzOiBbXQogIGhlYWx0aEVuZHBvaW50czoKICAgIGxpdmVuZXNzOiAv\naGVhbHRoL2xpdmVuZXNzCiAgICByZWFkaW5lc3M6IC9oZWFsdGgvcmVhZGlu\nZXNzCg==\n", + "encoding": "base64", + "_links": { + "self": "https://api.github.com/repos/vikram-raj/hello-func-node/contents/func.yaml?ref=master", + "git": "https://api.github.com/repos/vikram-raj/hello-func-node/git/blobs/296531d91cee272407f90494bca2a20736b4ff30", + "html": "https://github.com/vikram-raj/hello-func-node/blob/master/func.yaml" + } +} \ No newline at end of file diff --git a/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-node/package.json b/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-node/package.json new file mode 100644 index 00000000000..f5054cefcd1 --- /dev/null +++ b/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-node/package.json @@ -0,0 +1,18 @@ +{ + "name": "package.json", + "path": "package.json", + "sha": "2806b7a4ce0a7caf6f1e3d9c0a833c81db97f7eb", + "size": 565, + "url": "https://api.github.com/repos/vikram-raj/hello-func-node/contents/package.json?ref=master", + "html_url": "https://github.com/vikram-raj/hello-func-node/blob/master/package.json", + "git_url": "https://api.github.com/repos/vikram-raj/hello-func-node/git/blobs/2806b7a4ce0a7caf6f1e3d9c0a833c81db97f7eb", + "download_url": "https://raw.githubusercontent.com/vikram-raj/hello-func-node/master/package.json", + "type": "file", + "content": "ewogICJuYW1lIjogImh0dHAtaGFuZGxlciIsCiAgInZlcnNpb24iOiAiMC4x\nLjAiLAogICJkZXNjcmlwdGlvbiI6ICJBIGZ1bmN0aW9uIHdoaWNoIHJlc3Bv\nbmRzIHRvIEhUVFAgcmVxdWVzdHMiLAogICJtYWluIjogImluZGV4LmpzIiwK\nICAic2NyaXB0cyI6IHsKICAgICJ0ZXN0IjogIm5vZGUgdGVzdC91bml0Lmpz\nICYmIG5vZGUgdGVzdC9pbnRlZ3JhdGlvbi5qcyIsCiAgICAic3RhcnQiOiAi\nZmFhcy1qcy1ydW50aW1lIC4vaW5kZXguanMiLAogICAgImRlYnVnIjogIm5v\nZGVtb24gLS1pbnNwZWN0IC4vbm9kZV9tb2R1bGVzL2ZhYXMtanMtcnVudGlt\nZS9iaW4vY2xpLmpzIC4vaW5kZXguanMiCiAgfSwKICAia2V5d29yZHMiOiBb\nXSwKICAiYXV0aG9yIjogIiIsCiAgImxpY2Vuc2UiOiAiQXBhY2hlLTIuMCIs\nCiAgImRlcGVuZGVuY2llcyI6IHsKICAgICJmYWFzLWpzLXJ1bnRpbWUiOiAi\nXjAuOS4wIgogIH0sCiAgImRldkRlcGVuZGVuY2llcyI6IHsKICAgICJub2Rl\nbW9uIjogIl4yLjAuNCIsCiAgICAic3VwZXJ0ZXN0IjogIl40LjAuMiIsCiAg\nICAidGFwZSI6ICJeNS4wLjEiCiAgfQp9Cg==\n", + "encoding": "base64", + "_links": { + "self": "https://api.github.com/repos/vikram-raj/hello-func-node/contents/package.json?ref=master", + "git": "https://api.github.com/repos/vikram-raj/hello-func-node/git/blobs/2806b7a4ce0a7caf6f1e3d9c0a833c81db97f7eb", + "html": "https://github.com/vikram-raj/hello-func-node/blob/master/package.json" + } +} diff --git a/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-node/repo.json b/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-node/repo.json new file mode 100644 index 00000000000..fa3a76df3c0 --- /dev/null +++ b/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-node/repo.json @@ -0,0 +1,105 @@ +{ + "id": 561716556, + "node_id": "R_kgDOIXsdTA", + "name": "hello-func-node", + "full_name": "vikram-raj/hello-func-node", + "private": false, + "owner": { + "login": "vikram-raj", + "id": 2561818, + "node_id": "MDQ6VXNlcjI1NjE4MTg=", + "avatar_url": "https://avatars.githubusercontent.com/u/2561818?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/vikram-raj", + "html_url": "https://github.com/vikram-raj", + "followers_url": "https://api.github.com/users/vikram-raj/followers", + "following_url": "https://api.github.com/users/vikram-raj/following{/other_user}", + "gists_url": "https://api.github.com/users/vikram-raj/gists{/gist_id}", + "starred_url": "https://api.github.com/users/vikram-raj/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/vikram-raj/subscriptions", + "organizations_url": "https://api.github.com/users/vikram-raj/orgs", + "repos_url": "https://api.github.com/users/vikram-raj/repos", + "events_url": "https://api.github.com/users/vikram-raj/events{/privacy}", + "received_events_url": "https://api.github.com/users/vikram-raj/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/vikram-raj/hello-func-node", + "description": null, + "fork": false, + "url": "https://api.github.com/repos/vikram-raj/hello-func-node", + "forks_url": "https://api.github.com/repos/vikram-raj/hello-func-node/forks", + "keys_url": "https://api.github.com/repos/vikram-raj/hello-func-node/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/vikram-raj/hello-func-node/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/vikram-raj/hello-func-node/teams", + "hooks_url": "https://api.github.com/repos/vikram-raj/hello-func-node/hooks", + "issue_events_url": "https://api.github.com/repos/vikram-raj/hello-func-node/issues/events{/number}", + "events_url": "https://api.github.com/repos/vikram-raj/hello-func-node/events", + "assignees_url": "https://api.github.com/repos/vikram-raj/hello-func-node/assignees{/user}", + "branches_url": "https://api.github.com/repos/vikram-raj/hello-func-node/branches{/branch}", + "tags_url": "https://api.github.com/repos/vikram-raj/hello-func-node/tags", + "blobs_url": "https://api.github.com/repos/vikram-raj/hello-func-node/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/vikram-raj/hello-func-node/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/vikram-raj/hello-func-node/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/vikram-raj/hello-func-node/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/vikram-raj/hello-func-node/statuses/{sha}", + "languages_url": "https://api.github.com/repos/vikram-raj/hello-func-node/languages", + "stargazers_url": "https://api.github.com/repos/vikram-raj/hello-func-node/stargazers", + "contributors_url": "https://api.github.com/repos/vikram-raj/hello-func-node/contributors", + "subscribers_url": "https://api.github.com/repos/vikram-raj/hello-func-node/subscribers", + "subscription_url": "https://api.github.com/repos/vikram-raj/hello-func-node/subscription", + "commits_url": "https://api.github.com/repos/vikram-raj/hello-func-node/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/vikram-raj/hello-func-node/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/vikram-raj/hello-func-node/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/vikram-raj/hello-func-node/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/vikram-raj/hello-func-node/contents/{+path}", + "compare_url": "https://api.github.com/repos/vikram-raj/hello-func-node/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/vikram-raj/hello-func-node/merges", + "archive_url": "https://api.github.com/repos/vikram-raj/hello-func-node/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/vikram-raj/hello-func-node/downloads", + "issues_url": "https://api.github.com/repos/vikram-raj/hello-func-node/issues{/number}", + "pulls_url": "https://api.github.com/repos/vikram-raj/hello-func-node/pulls{/number}", + "milestones_url": "https://api.github.com/repos/vikram-raj/hello-func-node/milestones{/number}", + "notifications_url": "https://api.github.com/repos/vikram-raj/hello-func-node/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/vikram-raj/hello-func-node/labels{/name}", + "releases_url": "https://api.github.com/repos/vikram-raj/hello-func-node/releases{/id}", + "deployments_url": "https://api.github.com/repos/vikram-raj/hello-func-node/deployments", + "created_at": "2022-11-04T10:27:10Z", + "updated_at": "2022-11-17T08:20:02Z", + "pushed_at": "2023-02-13T17:49:55Z", + "git_url": "git://github.com/vikram-raj/hello-func-node.git", + "ssh_url": "git@github.com:vikram-raj/hello-func-node.git", + "clone_url": "https://github.com/vikram-raj/hello-func-node.git", + "svn_url": "https://github.com/vikram-raj/hello-func-node", + "homepage": null, + "size": 49, + "stargazers_count": 0, + "watchers_count": 0, + "language": "JavaScript", + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "has_discussions": false, + "forks_count": 1, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 0, + "license": null, + "allow_forking": true, + "is_template": false, + "web_commit_signoff_required": false, + "topics": [ + + ], + "visibility": "public", + "forks": 1, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "temp_clone_token": null, + "network_count": 1, + "subscribers_count": 1 + } diff --git a/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-quarkus/contents.json b/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-quarkus/contents.json new file mode 100644 index 00000000000..d71d13f969c --- /dev/null +++ b/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-quarkus/contents.json @@ -0,0 +1,130 @@ +[ + { + "name": ".gitignore", + "path": ".gitignore", + "sha": "3abd5fdff11e295a0d72346ac99fe7034da1c6cb", + "size": 126, + "url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/contents/.gitignore?ref=master", + "html_url": "https://github.com/vikram-raj/hello-func-quarkus/blob/master/.gitignore", + "git_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/git/blobs/3abd5fdff11e295a0d72346ac99fe7034da1c6cb", + "download_url": "https://raw.githubusercontent.com/vikram-raj/hello-func-quarkus/master/.gitignore", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/contents/.gitignore?ref=master", + "git": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/git/blobs/3abd5fdff11e295a0d72346ac99fe7034da1c6cb", + "html": "https://github.com/vikram-raj/hello-func-quarkus/blob/master/.gitignore" + } + }, + { + "name": ".mvn", + "path": ".mvn", + "sha": "321dbd85deb16a24bcc0a29b974aa7a9bab5f85c", + "size": 0, + "url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/contents/.mvn?ref=master", + "html_url": "https://github.com/vikram-raj/hello-func-quarkus/tree/master/.mvn", + "git_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/git/trees/321dbd85deb16a24bcc0a29b974aa7a9bab5f85c", + "download_url": null, + "type": "dir", + "_links": { + "self": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/contents/.mvn?ref=master", + "git": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/git/trees/321dbd85deb16a24bcc0a29b974aa7a9bab5f85c", + "html": "https://github.com/vikram-raj/hello-func-quarkus/tree/master/.mvn" + } + }, + { + "name": "README.md", + "path": "README.md", + "sha": "db59866a5a0668c68245b07c538f0dd5ba145bbe", + "size": 2276, + "url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/contents/README.md?ref=master", + "html_url": "https://github.com/vikram-raj/hello-func-quarkus/blob/master/README.md", + "git_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/git/blobs/db59866a5a0668c68245b07c538f0dd5ba145bbe", + "download_url": "https://raw.githubusercontent.com/vikram-raj/hello-func-quarkus/master/README.md", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/contents/README.md?ref=master", + "git": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/git/blobs/db59866a5a0668c68245b07c538f0dd5ba145bbe", + "html": "https://github.com/vikram-raj/hello-func-quarkus/blob/master/README.md" + } + }, + { + "name": "func.yaml", + "path": "func.yaml", + "sha": "afa4aa0b8b1cce8265bd358ece2aab321d38ad77", + "size": 1014, + "url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/contents/func.yaml?ref=master", + "html_url": "https://github.com/vikram-raj/hello-func-quarkus/blob/master/func.yaml", + "git_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/git/blobs/afa4aa0b8b1cce8265bd358ece2aab321d38ad77", + "download_url": "https://raw.githubusercontent.com/vikram-raj/hello-func-quarkus/master/func.yaml", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/contents/func.yaml?ref=master", + "git": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/git/blobs/afa4aa0b8b1cce8265bd358ece2aab321d38ad77", + "html": "https://github.com/vikram-raj/hello-func-quarkus/blob/master/func.yaml" + } + }, + { + "name": "mvnw", + "path": "mvnw", + "sha": "41c0f0c23db5dca836d3db1a17a7a28444d17d74", + "size": 10069, + "url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/contents/mvnw?ref=master", + "html_url": "https://github.com/vikram-raj/hello-func-quarkus/blob/master/mvnw", + "git_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/git/blobs/41c0f0c23db5dca836d3db1a17a7a28444d17d74", + "download_url": "https://raw.githubusercontent.com/vikram-raj/hello-func-quarkus/master/mvnw", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/contents/mvnw?ref=master", + "git": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/git/blobs/41c0f0c23db5dca836d3db1a17a7a28444d17d74", + "html": "https://github.com/vikram-raj/hello-func-quarkus/blob/master/mvnw" + } + }, + { + "name": "mvnw.cmd", + "path": "mvnw.cmd", + "sha": "86115719e5383e94597f918bf18c5ecbb7cfe492", + "size": 6607, + "url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/contents/mvnw.cmd?ref=master", + "html_url": "https://github.com/vikram-raj/hello-func-quarkus/blob/master/mvnw.cmd", + "git_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/git/blobs/86115719e5383e94597f918bf18c5ecbb7cfe492", + "download_url": "https://raw.githubusercontent.com/vikram-raj/hello-func-quarkus/master/mvnw.cmd", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/contents/mvnw.cmd?ref=master", + "git": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/git/blobs/86115719e5383e94597f918bf18c5ecbb7cfe492", + "html": "https://github.com/vikram-raj/hello-func-quarkus/blob/master/mvnw.cmd" + } + }, + { + "name": "pom.xml", + "path": "pom.xml", + "sha": "d20f62895f702f3dd5fcdc215ae40150d4b754c4", + "size": 4351, + "url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/contents/pom.xml?ref=master", + "html_url": "https://github.com/vikram-raj/hello-func-quarkus/blob/master/pom.xml", + "git_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/git/blobs/d20f62895f702f3dd5fcdc215ae40150d4b754c4", + "download_url": "https://raw.githubusercontent.com/vikram-raj/hello-func-quarkus/master/pom.xml", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/contents/pom.xml?ref=master", + "git": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/git/blobs/d20f62895f702f3dd5fcdc215ae40150d4b754c4", + "html": "https://github.com/vikram-raj/hello-func-quarkus/blob/master/pom.xml" + } + }, + { + "name": "src", + "path": "src", + "sha": "8f2034073149a21b1daf2a2f1e60a30fa3f4ccb5", + "size": 0, + "url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/contents/src?ref=master", + "html_url": "https://github.com/vikram-raj/hello-func-quarkus/tree/master/src", + "git_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/git/trees/8f2034073149a21b1daf2a2f1e60a30fa3f4ccb5", + "download_url": null, + "type": "dir", + "_links": { + "self": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/contents/src?ref=master", + "git": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/git/trees/8f2034073149a21b1daf2a2f1e60a30fa3f4ccb5", + "html": "https://github.com/vikram-raj/hello-func-quarkus/tree/master/src" + } + } + ] diff --git a/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-quarkus/func.json b/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-quarkus/func.json new file mode 100644 index 00000000000..c3e9c1e43fa --- /dev/null +++ b/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-quarkus/func.json @@ -0,0 +1,18 @@ +{ + "name": "func.yaml", + "path": "func.yaml", + "sha": "afa4aa0b8b1cce8265bd358ece2aab321d38ad77", + "size": 1014, + "url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/contents/func.yaml?ref=master", + "html_url": "https://github.com/vikram-raj/hello-func-quarkus/blob/master/func.yaml", + "git_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/git/blobs/afa4aa0b8b1cce8265bd358ece2aab321d38ad77", + "download_url": "https://raw.githubusercontent.com/vikram-raj/hello-func-quarkus/master/func.yaml", + "type": "file", + "content": "c3BlY1ZlcnNpb246IDAuMzQuMApuYW1lOiBoZWxsby1mdW5jLXF1YXJrdXMK\ncnVudGltZTogcXVhcmt1cwpyZWdpc3RyeTogcXVheS5pby92aXJhal8xCmlt\nYWdlOiBxdWF5LmlvL3ZpcmFqXzEvaGVsbG8tZnVuYy1xdWFya3VzOmxhdGVz\ndAppbWFnZURpZ2VzdDogc2hhMjU2OmFkNjMyZDNkNGQ0N2Q0NjM3YjA2Yjk3\nZjU1M2ZiMWRlODIxYTg2Mjk0ODU0MzE1Njc2YjViZDM2ZTllZWRiMzAKY3Jl\nYXRlZDogMjAyMi0xMS0xNlQxNjoxNjoxNC4wNTgzNDg2MDIrMDU6MzAKaW52\nb2NhdGlvbjoKICBmb3JtYXQ6IGh0dHAKYnVpbGQ6CiAgYnVpbGRwYWNrczog\nW10KICBidWlsZGVyOiBzMmkKICBidWlsZEVudnM6CiAgLSBuYW1lOiBCUF9O\nQVRJVkVfSU1BR0UKICAgIHZhbHVlOiAiZmFsc2UiCiAgLSBuYW1lOiBCUF9N\nQVZFTl9CVUlMVF9BUlRJRkFDVAogICAgdmFsdWU6IGZ1bmMueWFtbCB0YXJn\nZXQvcXVhcmt1cy1hcHAvbGliLyB0YXJnZXQvcXVhcmt1cy1hcHAvKi5qYXIg\ndGFyZ2V0L3F1YXJrdXMtYXBwL2FwcC8KICAgICAgdGFyZ2V0L3F1YXJrdXMt\nYXBwL3F1YXJrdXMvCiAgLSBuYW1lOiBCUF9NQVZFTl9CVUlMRF9BUkdVTUVO\nVFMKICAgIHZhbHVlOiBwYWNrYWdlIC1Ec2tpcFRlc3RzPXRydWUgLURtYXZl\nbi5qYXZhZG9jLnNraXA9dHJ1ZSAtRHF1YXJrdXMucGFja2FnZS50eXBlPWZh\nc3QtamFyCiAgLSBuYW1lOiBNQVZFTl9TMklfQVJUSUZBQ1RfRElSUwogICAg\ndmFsdWU6IHRhcmdldC9xdWFya3VzLWFwcAogIC0gbmFtZTogUzJJX1NPVVJD\nRV9ERVBMT1lNRU5UU19GSUxURVIKICAgIHZhbHVlOiBsaWIgcXVhcmt1cy1y\ndW4uamFyIGFwcCBxdWFya3VzCnJ1bjoKICB2b2x1bWVzOiBbXQogIGVudnM6\nIFtdCmRlcGxveToKICBuYW1lc3BhY2U6IHZpcmFqCiAgYW5ub3RhdGlvbnM6\nIHt9CiAgb3B0aW9uczoge30KICBsYWJlbHM6IFtdCiAgaGVhbHRoRW5kcG9p\nbnRzOgogICAgbGl2ZW5lc3M6IC9oZWFsdGgvbGl2ZW5lc3MKICAgIHJlYWRp\nbmVzczogL2hlYWx0aC9yZWFkaW5lc3MK\n", + "encoding": "base64", + "_links": { + "self": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/contents/func.yaml?ref=master", + "git": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/git/blobs/afa4aa0b8b1cce8265bd358ece2aab321d38ad77", + "html": "https://github.com/vikram-raj/hello-func-quarkus/blob/master/func.yaml" + } +} \ No newline at end of file diff --git a/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-quarkus/repo.json b/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-quarkus/repo.json new file mode 100644 index 00000000000..29d4f956e4e --- /dev/null +++ b/frontend/packages/dev-console/integration-tests/testData/git-import/hello-func-quarkus/repo.json @@ -0,0 +1,105 @@ +{ + "id": 567175013, + "node_id": "R_kgDOIc5nZQ", + "name": "hello-func-quarkus", + "full_name": "vikram-raj/hello-func-quarkus", + "private": false, + "owner": { + "login": "vikram-raj", + "id": 2561818, + "node_id": "MDQ6VXNlcjI1NjE4MTg=", + "avatar_url": "https://avatars.githubusercontent.com/u/2561818?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/vikram-raj", + "html_url": "https://github.com/vikram-raj", + "followers_url": "https://api.github.com/users/vikram-raj/followers", + "following_url": "https://api.github.com/users/vikram-raj/following{/other_user}", + "gists_url": "https://api.github.com/users/vikram-raj/gists{/gist_id}", + "starred_url": "https://api.github.com/users/vikram-raj/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/vikram-raj/subscriptions", + "organizations_url": "https://api.github.com/users/vikram-raj/orgs", + "repos_url": "https://api.github.com/users/vikram-raj/repos", + "events_url": "https://api.github.com/users/vikram-raj/events{/privacy}", + "received_events_url": "https://api.github.com/users/vikram-raj/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/vikram-raj/hello-func-quarkus", + "description": null, + "fork": false, + "url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus", + "forks_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/forks", + "keys_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/teams", + "hooks_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/hooks", + "issue_events_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/issues/events{/number}", + "events_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/events", + "assignees_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/assignees{/user}", + "branches_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/branches{/branch}", + "tags_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/tags", + "blobs_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/statuses/{sha}", + "languages_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/languages", + "stargazers_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/stargazers", + "contributors_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/contributors", + "subscribers_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/subscribers", + "subscription_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/subscription", + "commits_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/contents/{+path}", + "compare_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/merges", + "archive_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/downloads", + "issues_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/issues{/number}", + "pulls_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/pulls{/number}", + "milestones_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/milestones{/number}", + "notifications_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/labels{/name}", + "releases_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/releases{/id}", + "deployments_url": "https://api.github.com/repos/vikram-raj/hello-func-quarkus/deployments", + "created_at": "2022-11-17T08:26:21Z", + "updated_at": "2022-11-17T08:27:02Z", + "pushed_at": "2023-01-17T01:31:12Z", + "git_url": "git://github.com/vikram-raj/hello-func-quarkus.git", + "ssh_url": "git@github.com:vikram-raj/hello-func-quarkus.git", + "clone_url": "https://github.com/vikram-raj/hello-func-quarkus.git", + "svn_url": "https://github.com/vikram-raj/hello-func-quarkus", + "homepage": null, + "size": 13, + "stargazers_count": 0, + "watchers_count": 0, + "language": "Java", + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "has_discussions": false, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 0, + "license": null, + "allow_forking": true, + "is_template": false, + "web_commit_signoff_required": false, + "topics": [ + + ], + "visibility": "public", + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "temp_clone_token": null, + "network_count": 0, + "subscribers_count": 1 + } diff --git a/frontend/packages/dev-console/integration-tests/testData/git-import/kn-func-typescript-cloudevents/contents.json b/frontend/packages/dev-console/integration-tests/testData/git-import/kn-func-typescript-cloudevents/contents.json new file mode 100644 index 00000000000..c23b1a08cef --- /dev/null +++ b/frontend/packages/dev-console/integration-tests/testData/git-import/kn-func-typescript-cloudevents/contents.json @@ -0,0 +1,162 @@ +[ + { + "name": ".eslintrc", + "path": ".eslintrc", + "sha": "03c44bafb5e59cc0a5931256afc71df24937f44d", + "size": 457, + "url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/contents/.eslintrc?ref=main", + "html_url": "https://github.com/openshift-dev-console/kn-func-typescript-cloudevents/blob/main/.eslintrc", + "git_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/git/blobs/03c44bafb5e59cc0a5931256afc71df24937f44d", + "download_url": "https://raw.githubusercontent.com/openshift-dev-console/kn-func-typescript-cloudevents/main/.eslintrc", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/contents/.eslintrc?ref=main", + "git": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/git/blobs/03c44bafb5e59cc0a5931256afc71df24937f44d", + "html": "https://github.com/openshift-dev-console/kn-func-typescript-cloudevents/blob/main/.eslintrc" + } + }, + { + "name": ".gitignore", + "path": ".gitignore", + "sha": "3abd5fdff11e295a0d72346ac99fe7034da1c6cb", + "size": 126, + "url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/contents/.gitignore?ref=main", + "html_url": "https://github.com/openshift-dev-console/kn-func-typescript-cloudevents/blob/main/.gitignore", + "git_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/git/blobs/3abd5fdff11e295a0d72346ac99fe7034da1c6cb", + "download_url": "https://raw.githubusercontent.com/openshift-dev-console/kn-func-typescript-cloudevents/main/.gitignore", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/contents/.gitignore?ref=main", + "git": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/git/blobs/3abd5fdff11e295a0d72346ac99fe7034da1c6cb", + "html": "https://github.com/openshift-dev-console/kn-func-typescript-cloudevents/blob/main/.gitignore" + } + }, + { + "name": ".prettierrc", + "path": ".prettierrc", + "sha": "d2b80b80b387b3c5159b7f20413844850ed51748", + "size": 90, + "url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/contents/.prettierrc?ref=main", + "html_url": "https://github.com/openshift-dev-console/kn-func-typescript-cloudevents/blob/main/.prettierrc", + "git_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/git/blobs/d2b80b80b387b3c5159b7f20413844850ed51748", + "download_url": "https://raw.githubusercontent.com/openshift-dev-console/kn-func-typescript-cloudevents/main/.prettierrc", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/contents/.prettierrc?ref=main", + "git": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/git/blobs/d2b80b80b387b3c5159b7f20413844850ed51748", + "html": "https://github.com/openshift-dev-console/kn-func-typescript-cloudevents/blob/main/.prettierrc" + } + }, + { + "name": "README.md", + "path": "README.md", + "sha": "956f08860c7ccbdfd25c35b4e645155fc085e070", + "size": 1656, + "url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/contents/README.md?ref=main", + "html_url": "https://github.com/openshift-dev-console/kn-func-typescript-cloudevents/blob/main/README.md", + "git_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/git/blobs/956f08860c7ccbdfd25c35b4e645155fc085e070", + "download_url": "https://raw.githubusercontent.com/openshift-dev-console/kn-func-typescript-cloudevents/main/README.md", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/contents/README.md?ref=main", + "git": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/git/blobs/956f08860c7ccbdfd25c35b4e645155fc085e070", + "html": "https://github.com/openshift-dev-console/kn-func-typescript-cloudevents/blob/main/README.md" + } + }, + { + "name": "func.yaml", + "path": "func.yaml", + "sha": "a9a73b03d5b80a12919f514f070cbe0fc6316a80", + "size": 432, + "url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/contents/func.yaml?ref=main", + "html_url": "https://github.com/openshift-dev-console/kn-func-typescript-cloudevents/blob/main/func.yaml", + "git_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/git/blobs/a9a73b03d5b80a12919f514f070cbe0fc6316a80", + "download_url": "https://raw.githubusercontent.com/openshift-dev-console/kn-func-typescript-cloudevents/main/func.yaml", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/contents/func.yaml?ref=main", + "git": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/git/blobs/a9a73b03d5b80a12919f514f070cbe0fc6316a80", + "html": "https://github.com/openshift-dev-console/kn-func-typescript-cloudevents/blob/main/func.yaml" + } + }, + { + "name": "package-lock.json", + "path": "package-lock.json", + "sha": "7d701ae401bf3fb62ea82bcca2a30ceb8bd0a251", + "size": 297047, + "url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/contents/package-lock.json?ref=main", + "html_url": "https://github.com/openshift-dev-console/kn-func-typescript-cloudevents/blob/main/package-lock.json", + "git_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/git/blobs/7d701ae401bf3fb62ea82bcca2a30ceb8bd0a251", + "download_url": "https://raw.githubusercontent.com/openshift-dev-console/kn-func-typescript-cloudevents/main/package-lock.json", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/contents/package-lock.json?ref=main", + "git": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/git/blobs/7d701ae401bf3fb62ea82bcca2a30ceb8bd0a251", + "html": "https://github.com/openshift-dev-console/kn-func-typescript-cloudevents/blob/main/package-lock.json" + } + }, + { + "name": "package.json", + "path": "package.json", + "sha": "f4dc806d2f050d1a96cb9e47301015d48a723c87", + "size": 1336, + "url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/contents/package.json?ref=main", + "html_url": "https://github.com/openshift-dev-console/kn-func-typescript-cloudevents/blob/main/package.json", + "git_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/git/blobs/f4dc806d2f050d1a96cb9e47301015d48a723c87", + "download_url": "https://raw.githubusercontent.com/openshift-dev-console/kn-func-typescript-cloudevents/main/package.json", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/contents/package.json?ref=main", + "git": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/git/blobs/f4dc806d2f050d1a96cb9e47301015d48a723c87", + "html": "https://github.com/openshift-dev-console/kn-func-typescript-cloudevents/blob/main/package.json" + } + }, + { + "name": "src", + "path": "src", + "sha": "93a375a5a8bf422e82e2cb600f40e50fc487f0af", + "size": 0, + "url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/contents/src?ref=main", + "html_url": "https://github.com/openshift-dev-console/kn-func-typescript-cloudevents/tree/main/src", + "git_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/git/trees/93a375a5a8bf422e82e2cb600f40e50fc487f0af", + "download_url": null, + "type": "dir", + "_links": { + "self": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/contents/src?ref=main", + "git": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/git/trees/93a375a5a8bf422e82e2cb600f40e50fc487f0af", + "html": "https://github.com/openshift-dev-console/kn-func-typescript-cloudevents/tree/main/src" + } + }, + { + "name": "test", + "path": "test", + "sha": "c4e8f3e9a0104139eacf2869a3250615d68de213", + "size": 0, + "url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/contents/test?ref=main", + "html_url": "https://github.com/openshift-dev-console/kn-func-typescript-cloudevents/tree/main/test", + "git_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/git/trees/c4e8f3e9a0104139eacf2869a3250615d68de213", + "download_url": null, + "type": "dir", + "_links": { + "self": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/contents/test?ref=main", + "git": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/git/trees/c4e8f3e9a0104139eacf2869a3250615d68de213", + "html": "https://github.com/openshift-dev-console/kn-func-typescript-cloudevents/tree/main/test" + } + }, + { + "name": "tsconfig.json", + "path": "tsconfig.json", + "sha": "bbe4487b41fd333aea843fd133e3d250160fb703", + "size": 1830, + "url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/contents/tsconfig.json?ref=main", + "html_url": "https://github.com/openshift-dev-console/kn-func-typescript-cloudevents/blob/main/tsconfig.json", + "git_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/git/blobs/bbe4487b41fd333aea843fd133e3d250160fb703", + "download_url": "https://raw.githubusercontent.com/openshift-dev-console/kn-func-typescript-cloudevents/main/tsconfig.json", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/contents/tsconfig.json?ref=main", + "git": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/git/blobs/bbe4487b41fd333aea843fd133e3d250160fb703", + "html": "https://github.com/openshift-dev-console/kn-func-typescript-cloudevents/blob/main/tsconfig.json" + } + } + ] diff --git a/frontend/packages/dev-console/integration-tests/testData/git-import/kn-func-typescript-cloudevents/func.json b/frontend/packages/dev-console/integration-tests/testData/git-import/kn-func-typescript-cloudevents/func.json new file mode 100644 index 00000000000..c26bf735e61 --- /dev/null +++ b/frontend/packages/dev-console/integration-tests/testData/git-import/kn-func-typescript-cloudevents/func.json @@ -0,0 +1,18 @@ +{ + "name": "func.yaml", + "path": "func.yaml", + "sha": "a9a73b03d5b80a12919f514f070cbe0fc6316a80", + "size": 432, + "url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/contents/func.yaml?ref=main", + "html_url": "https://github.com/openshift-dev-console/kn-func-typescript-cloudevents/blob/main/func.yaml", + "git_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/git/blobs/a9a73b03d5b80a12919f514f070cbe0fc6316a80", + "download_url": "https://raw.githubusercontent.com/openshift-dev-console/kn-func-typescript-cloudevents/main/func.yaml", + "type": "file", + "content": "c3BlY1ZlcnNpb246IDAuMzUuMApuYW1lOiBrbi1mdW5jLXR5cGVzY3JpcHQt\nY2xvdWRldmVudHMKcnVudGltZTogdHlwZXNjcmlwdApyZWdpc3RyeTogIiIK\naW1hZ2U6ICIiCmltYWdlRGlnZXN0OiAiIgppbnZva2U6IGNsb3VkZXZlbnQK\nYnVpbGQ6CiAgYnVpbGRwYWNrczogW10KICBidWlsZGVyOiBzMmkKICBidWls\nZEVudnM6CiAgLSBuYW1lOiBCUF9OT0RFX1JVTl9TQ1JJUFRTCiAgICB2YWx1\nZTogYnVpbGQKcnVuOgogIHZvbHVtZXM6IFtdCiAgZW52czogW10KZGVwbG95\nOgogIG5hbWVzcGFjZTogIiIKICByZW1vdGU6IGZhbHNlCiAgYW5ub3RhdGlv\nbnM6IHt9CiAgb3B0aW9uczoge30KICBsYWJlbHM6IFtdCiAgaGVhbHRoRW5k\ncG9pbnRzOgogICAgbGl2ZW5lc3M6IC9oZWFsdGgvbGl2ZW5lc3MKICAgIHJl\nYWRpbmVzczogL2hlYWx0aC9yZWFkaW5lc3MK\n", + "encoding": "base64", + "_links": { + "self": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/contents/func.yaml?ref=main", + "git": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/git/blobs/a9a73b03d5b80a12919f514f070cbe0fc6316a80", + "html": "https://github.com/openshift-dev-console/kn-func-typescript-cloudevents/blob/main/func.yaml" + } +} \ No newline at end of file diff --git a/frontend/packages/dev-console/integration-tests/testData/git-import/kn-func-typescript-cloudevents/package.json b/frontend/packages/dev-console/integration-tests/testData/git-import/kn-func-typescript-cloudevents/package.json new file mode 100644 index 00000000000..27ba4525fc2 --- /dev/null +++ b/frontend/packages/dev-console/integration-tests/testData/git-import/kn-func-typescript-cloudevents/package.json @@ -0,0 +1,18 @@ +{ + "name": "package.json", + "path": "package.json", + "sha": "f4dc806d2f050d1a96cb9e47301015d48a723c87", + "size": 1336, + "url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/contents/package.json?ref=main", + "html_url": "https://github.com/openshift-dev-console/kn-func-typescript-cloudevents/blob/main/package.json", + "git_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/git/blobs/f4dc806d2f050d1a96cb9e47301015d48a723c87", + "download_url": "https://raw.githubusercontent.com/openshift-dev-console/kn-func-typescript-cloudevents/main/package.json", + "type": "file", + "content": "ewogICJuYW1lIjogImV2ZW50LWhhbmRsZXIiLAogICJ2ZXJzaW9uIjogIjAu\nMS4wIiwKICAiZGVzY3JpcHRpb24iOiAiVHlwZVNjcmlwdCBDbG91ZEV2ZW50\nIEhhbmRsZXIiLAogICJsaWNlbnNlIjogIkFwYWNoZS0yLjAiLAogICJyZXBv\nc2l0b3J5IjogewogICAgInR5cGUiOiAiZ2l0IiwKICAgICJ1cmwiOiAiIgog\nIH0sCiAgInNjcmlwdHMiOiB7CiAgICAiYnVpbGQiOiAibnB4IC1wIHR5cGVz\nY3JpcHQgdHNjIiwKICAgICJwcmV0ZXN0IjogIm5wbSBydW4gbGludCAmJiBu\ncG0gcnVuIGJ1aWxkIiwKICAgICJ0ZXN0OnVuaXQiOiAidHMtbm9kZSBub2Rl\nX21vZHVsZXMvdGFwZS9iaW4vdGFwZSB0ZXN0L3VuaXQudHMiLAogICAgInRl\nc3Q6aW50ZWdyYXRpb24iOiAidHMtbm9kZSBub2RlX21vZHVsZXMvdGFwZS9i\naW4vdGFwZSB0ZXN0L2ludGVncmF0aW9uLnRzIiwKICAgICJ0ZXN0IjogIm5w\nbSBydW4gdGVzdDp1bml0ICYmIG5wbSBydW4gdGVzdDppbnRlZ3JhdGlvbiIs\nCiAgICAic3RhcnQiOiAiRlVOQ19MT0dfTEVWRUw9aW5mbyBmYWFzLWpzLXJ1\nbnRpbWUgLi9idWlsZC9pbmRleC5qcyIsCiAgICAibGludCI6ICJlc2xpbnQg\nXCJzcmMvKiovKi57anMsdHMsdHN4fVwiIFwidGVzdC8qKi8qLntqcyx0cyx0\nc3h9XCIgLS1xdWlldCIsCiAgICAiZGVidWciOiAibm9kZW1vbiAtLWluc3Bl\nY3QgLi9ub2RlX21vZHVsZXMvZmFhcy1qcy1ydW50aW1lL2Jpbi9jbGkuanMg\nLi9idWlsZC9pbmRleC5qcyIKICB9LAogICJkZXZEZXBlbmRlbmNpZXMiOiB7\nCiAgICAiQHR5cGVzL3RhcGUiOiAiXjQuMTMuMCIsCiAgICAiQHR5cGVzY3Jp\ncHQtZXNsaW50L2VzbGludC1wbHVnaW4iOiAiXjQuMjQuMCIsCiAgICAiQHR5\ncGVzY3JpcHQtZXNsaW50L3BhcnNlciI6ICJeNC4yNC4wIiwKICAgICJlc2xp\nbnQiOiAiXjcuMjYuMCIsCiAgICAiZXNsaW50LWNvbmZpZy1wcmV0dGllciI6\nICJeOC4zLjAiLAogICAgImVzbGludC1wbHVnaW4tcHJldHRpZXIiOiAiXjMu\nNC4wIiwKICAgICJub2RlbW9uIjogIl4yLjAuNCIsCiAgICAicHJldHRpZXIi\nOiAiXjIuMy4wIiwKICAgICJzdXBlcnRlc3QiOiAiXjYuMy4xIiwKICAgICJ0\nYXBlIjogIl40LjEzLjAiLAogICAgInRzLW5vZGUiOiAiXjkuMS4xIiwKICAg\nICJ0c2QiOiAiXjAuMjQuMSIsCiAgICAidHNsaW50LWNvbmZpZy1wcmV0dGll\nciI6ICJeMS4xOC4wIiwKICAgICJ0eXBlc2NyaXB0IjogIl40LjIuNCIKICB9\nLAogICJkZXBlbmRlbmNpZXMiOiB7CiAgICAiQHR5cGVzL25vZGUiOiAiXjE2\nLjExLjEyIiwKICAgICJjbG91ZGV2ZW50cyI6ICJeNi4wLjMiLAogICAgImZh\nYXMtanMtcnVudGltZSI6ICJeMC45LjciCiAgfQp9Cg==\n", + "encoding": "base64", + "_links": { + "self": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/contents/package.json?ref=main", + "git": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/git/blobs/f4dc806d2f050d1a96cb9e47301015d48a723c87", + "html": "https://github.com/openshift-dev-console/kn-func-typescript-cloudevents/blob/main/package.json" + } +} \ No newline at end of file diff --git a/frontend/packages/dev-console/integration-tests/testData/git-import/kn-func-typescript-cloudevents/repo.json b/frontend/packages/dev-console/integration-tests/testData/git-import/kn-func-typescript-cloudevents/repo.json new file mode 100644 index 00000000000..e4cf8b0478c --- /dev/null +++ b/frontend/packages/dev-console/integration-tests/testData/git-import/kn-func-typescript-cloudevents/repo.json @@ -0,0 +1,125 @@ +{ + "id": 621518463, + "node_id": "R_kgDOJQuefw", + "name": "kn-func-typescript-cloudevents", + "full_name": "openshift-dev-console/kn-func-typescript-cloudevents", + "private": false, + "owner": { + "login": "openshift-dev-console", + "id": 123080219, + "node_id": "O_kgDOB1YOGw", + "avatar_url": "https://avatars.githubusercontent.com/u/123080219?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/openshift-dev-console", + "html_url": "https://github.com/openshift-dev-console", + "followers_url": "https://api.github.com/users/openshift-dev-console/followers", + "following_url": "https://api.github.com/users/openshift-dev-console/following{/other_user}", + "gists_url": "https://api.github.com/users/openshift-dev-console/gists{/gist_id}", + "starred_url": "https://api.github.com/users/openshift-dev-console/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/openshift-dev-console/subscriptions", + "organizations_url": "https://api.github.com/users/openshift-dev-console/orgs", + "repos_url": "https://api.github.com/users/openshift-dev-console/repos", + "events_url": "https://api.github.com/users/openshift-dev-console/events{/privacy}", + "received_events_url": "https://api.github.com/users/openshift-dev-console/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/openshift-dev-console/kn-func-typescript-cloudevents", + "description": "Knative func example created with [kn] func create -l typescript -t cloudevents", + "fork": false, + "url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents", + "forks_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/forks", + "keys_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/teams", + "hooks_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/hooks", + "issue_events_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/issues/events{/number}", + "events_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/events", + "assignees_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/assignees{/user}", + "branches_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/branches{/branch}", + "tags_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/tags", + "blobs_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/statuses/{sha}", + "languages_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/languages", + "stargazers_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/stargazers", + "contributors_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/contributors", + "subscribers_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/subscribers", + "subscription_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/subscription", + "commits_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/contents/{+path}", + "compare_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/merges", + "archive_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/downloads", + "issues_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/issues{/number}", + "pulls_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/pulls{/number}", + "milestones_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/milestones{/number}", + "notifications_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/labels{/name}", + "releases_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/releases{/id}", + "deployments_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-cloudevents/deployments", + "created_at": "2023-03-30T20:34:38Z", + "updated_at": "2023-03-30T21:00:31Z", + "pushed_at": "2023-03-30T20:53:09Z", + "git_url": "git://github.com/openshift-dev-console/kn-func-typescript-cloudevents.git", + "ssh_url": "git@github.com:openshift-dev-console/kn-func-typescript-cloudevents.git", + "clone_url": "https://github.com/openshift-dev-console/kn-func-typescript-cloudevents.git", + "svn_url": "https://github.com/openshift-dev-console/kn-func-typescript-cloudevents", + "homepage": "", + "size": 84, + "stargazers_count": 0, + "watchers_count": 0, + "language": "TypeScript", + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "has_discussions": false, + "forks_count": 1, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 0, + "license": null, + "allow_forking": true, + "is_template": false, + "web_commit_signoff_required": false, + "topics": [ + + ], + "visibility": "public", + "forks": 1, + "open_issues": 0, + "watchers": 0, + "default_branch": "main", + "temp_clone_token": null, + "organization": { + "login": "openshift-dev-console", + "id": 123080219, + "node_id": "O_kgDOB1YOGw", + "avatar_url": "https://avatars.githubusercontent.com/u/123080219?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/openshift-dev-console", + "html_url": "https://github.com/openshift-dev-console", + "followers_url": "https://api.github.com/users/openshift-dev-console/followers", + "following_url": "https://api.github.com/users/openshift-dev-console/following{/other_user}", + "gists_url": "https://api.github.com/users/openshift-dev-console/gists{/gist_id}", + "starred_url": "https://api.github.com/users/openshift-dev-console/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/openshift-dev-console/subscriptions", + "organizations_url": "https://api.github.com/users/openshift-dev-console/orgs", + "repos_url": "https://api.github.com/users/openshift-dev-console/repos", + "events_url": "https://api.github.com/users/openshift-dev-console/events{/privacy}", + "received_events_url": "https://api.github.com/users/openshift-dev-console/received_events", + "type": "Organization", + "site_admin": false + }, + "network_count": 1, + "subscribers_count": 1 + } diff --git a/frontend/packages/dev-console/integration-tests/testData/git-import/kn-func-typescript-http/contents.json b/frontend/packages/dev-console/integration-tests/testData/git-import/kn-func-typescript-http/contents.json new file mode 100644 index 00000000000..b187270652f --- /dev/null +++ b/frontend/packages/dev-console/integration-tests/testData/git-import/kn-func-typescript-http/contents.json @@ -0,0 +1,162 @@ +[ + { + "name": ".eslintrc", + "path": ".eslintrc", + "sha": "f9d619ca7dbe59d7fa9ca8fd7c5a9122b254d03d", + "size": 458, + "url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/contents/.eslintrc?ref=main", + "html_url": "https://github.com/openshift-dev-console/kn-func-typescript-http/blob/main/.eslintrc", + "git_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/git/blobs/f9d619ca7dbe59d7fa9ca8fd7c5a9122b254d03d", + "download_url": "https://raw.githubusercontent.com/openshift-dev-console/kn-func-typescript-http/main/.eslintrc", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/contents/.eslintrc?ref=main", + "git": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/git/blobs/f9d619ca7dbe59d7fa9ca8fd7c5a9122b254d03d", + "html": "https://github.com/openshift-dev-console/kn-func-typescript-http/blob/main/.eslintrc" + } + }, + { + "name": ".gitignore", + "path": ".gitignore", + "sha": "3abd5fdff11e295a0d72346ac99fe7034da1c6cb", + "size": 126, + "url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/contents/.gitignore?ref=main", + "html_url": "https://github.com/openshift-dev-console/kn-func-typescript-http/blob/main/.gitignore", + "git_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/git/blobs/3abd5fdff11e295a0d72346ac99fe7034da1c6cb", + "download_url": "https://raw.githubusercontent.com/openshift-dev-console/kn-func-typescript-http/main/.gitignore", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/contents/.gitignore?ref=main", + "git": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/git/blobs/3abd5fdff11e295a0d72346ac99fe7034da1c6cb", + "html": "https://github.com/openshift-dev-console/kn-func-typescript-http/blob/main/.gitignore" + } + }, + { + "name": ".prettierrc", + "path": ".prettierrc", + "sha": "0706fd1552751fdb3b3e69defb7135b426b18616", + "size": 90, + "url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/contents/.prettierrc?ref=main", + "html_url": "https://github.com/openshift-dev-console/kn-func-typescript-http/blob/main/.prettierrc", + "git_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/git/blobs/0706fd1552751fdb3b3e69defb7135b426b18616", + "download_url": "https://raw.githubusercontent.com/openshift-dev-console/kn-func-typescript-http/main/.prettierrc", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/contents/.prettierrc?ref=main", + "git": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/git/blobs/0706fd1552751fdb3b3e69defb7135b426b18616", + "html": "https://github.com/openshift-dev-console/kn-func-typescript-http/blob/main/.prettierrc" + } + }, + { + "name": "README.md", + "path": "README.md", + "sha": "d6cc12f6d20a25cf0bf414ca0099e0dd1270d8c9", + "size": 1889, + "url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/contents/README.md?ref=main", + "html_url": "https://github.com/openshift-dev-console/kn-func-typescript-http/blob/main/README.md", + "git_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/git/blobs/d6cc12f6d20a25cf0bf414ca0099e0dd1270d8c9", + "download_url": "https://raw.githubusercontent.com/openshift-dev-console/kn-func-typescript-http/main/README.md", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/contents/README.md?ref=main", + "git": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/git/blobs/d6cc12f6d20a25cf0bf414ca0099e0dd1270d8c9", + "html": "https://github.com/openshift-dev-console/kn-func-typescript-http/blob/main/README.md" + } + }, + { + "name": "func.yaml", + "path": "func.yaml", + "sha": "91767c9c02cee2efe4fe901e10ca94a2fbda3f54", + "size": 406, + "url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/contents/func.yaml?ref=main", + "html_url": "https://github.com/openshift-dev-console/kn-func-typescript-http/blob/main/func.yaml", + "git_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/git/blobs/91767c9c02cee2efe4fe901e10ca94a2fbda3f54", + "download_url": "https://raw.githubusercontent.com/openshift-dev-console/kn-func-typescript-http/main/func.yaml", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/contents/func.yaml?ref=main", + "git": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/git/blobs/91767c9c02cee2efe4fe901e10ca94a2fbda3f54", + "html": "https://github.com/openshift-dev-console/kn-func-typescript-http/blob/main/func.yaml" + } + }, + { + "name": "package-lock.json", + "path": "package-lock.json", + "sha": "ac311a535ab217264cfd5d8510112a40f4f07f9b", + "size": 296890, + "url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/contents/package-lock.json?ref=main", + "html_url": "https://github.com/openshift-dev-console/kn-func-typescript-http/blob/main/package-lock.json", + "git_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/git/blobs/ac311a535ab217264cfd5d8510112a40f4f07f9b", + "download_url": "https://raw.githubusercontent.com/openshift-dev-console/kn-func-typescript-http/main/package-lock.json", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/contents/package-lock.json?ref=main", + "git": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/git/blobs/ac311a535ab217264cfd5d8510112a40f4f07f9b", + "html": "https://github.com/openshift-dev-console/kn-func-typescript-http/blob/main/package-lock.json" + } + }, + { + "name": "package.json", + "path": "package.json", + "sha": "3736c3a890ae67db07e77ec5b12fd22680d50faf", + "size": 1301, + "url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/contents/package.json?ref=main", + "html_url": "https://github.com/openshift-dev-console/kn-func-typescript-http/blob/main/package.json", + "git_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/git/blobs/3736c3a890ae67db07e77ec5b12fd22680d50faf", + "download_url": "https://raw.githubusercontent.com/openshift-dev-console/kn-func-typescript-http/main/package.json", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/contents/package.json?ref=main", + "git": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/git/blobs/3736c3a890ae67db07e77ec5b12fd22680d50faf", + "html": "https://github.com/openshift-dev-console/kn-func-typescript-http/blob/main/package.json" + } + }, + { + "name": "src", + "path": "src", + "sha": "80c0fadc3c8404384d87c8bd0cfadada12556314", + "size": 0, + "url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/contents/src?ref=main", + "html_url": "https://github.com/openshift-dev-console/kn-func-typescript-http/tree/main/src", + "git_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/git/trees/80c0fadc3c8404384d87c8bd0cfadada12556314", + "download_url": null, + "type": "dir", + "_links": { + "self": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/contents/src?ref=main", + "git": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/git/trees/80c0fadc3c8404384d87c8bd0cfadada12556314", + "html": "https://github.com/openshift-dev-console/kn-func-typescript-http/tree/main/src" + } + }, + { + "name": "test", + "path": "test", + "sha": "9c3fe3b01ca33d4caefa4599074dbd3634f9d809", + "size": 0, + "url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/contents/test?ref=main", + "html_url": "https://github.com/openshift-dev-console/kn-func-typescript-http/tree/main/test", + "git_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/git/trees/9c3fe3b01ca33d4caefa4599074dbd3634f9d809", + "download_url": null, + "type": "dir", + "_links": { + "self": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/contents/test?ref=main", + "git": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/git/trees/9c3fe3b01ca33d4caefa4599074dbd3634f9d809", + "html": "https://github.com/openshift-dev-console/kn-func-typescript-http/tree/main/test" + } + }, + { + "name": "tsconfig.json", + "path": "tsconfig.json", + "sha": "bbe4487b41fd333aea843fd133e3d250160fb703", + "size": 1830, + "url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/contents/tsconfig.json?ref=main", + "html_url": "https://github.com/openshift-dev-console/kn-func-typescript-http/blob/main/tsconfig.json", + "git_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/git/blobs/bbe4487b41fd333aea843fd133e3d250160fb703", + "download_url": "https://raw.githubusercontent.com/openshift-dev-console/kn-func-typescript-http/main/tsconfig.json", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/contents/tsconfig.json?ref=main", + "git": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/git/blobs/bbe4487b41fd333aea843fd133e3d250160fb703", + "html": "https://github.com/openshift-dev-console/kn-func-typescript-http/blob/main/tsconfig.json" + } + } + ] diff --git a/frontend/packages/dev-console/integration-tests/testData/git-import/kn-func-typescript-http/func.json b/frontend/packages/dev-console/integration-tests/testData/git-import/kn-func-typescript-http/func.json new file mode 100644 index 00000000000..4e34374ced3 --- /dev/null +++ b/frontend/packages/dev-console/integration-tests/testData/git-import/kn-func-typescript-http/func.json @@ -0,0 +1,18 @@ +{ + "name": "func.yaml", + "path": "func.yaml", + "sha": "91767c9c02cee2efe4fe901e10ca94a2fbda3f54", + "size": 406, + "url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/contents/func.yaml?ref=main", + "html_url": "https://github.com/openshift-dev-console/kn-func-typescript-http/blob/main/func.yaml", + "git_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/git/blobs/91767c9c02cee2efe4fe901e10ca94a2fbda3f54", + "download_url": "https://raw.githubusercontent.com/openshift-dev-console/kn-func-typescript-http/main/func.yaml", + "type": "file", + "content": "c3BlY1ZlcnNpb246IDAuMzUuMApuYW1lOiBrbi1mdW5jLXR5cGVzY3JpcHQt\naHR0cApydW50aW1lOiB0eXBlc2NyaXB0CnJlZ2lzdHJ5OiAiIgppbWFnZTog\nIiIKaW1hZ2VEaWdlc3Q6ICIiCmJ1aWxkOgogIGJ1aWxkcGFja3M6IFtdCiAg\nYnVpbGRlcjogczJpCiAgYnVpbGRFbnZzOgogIC0gbmFtZTogQlBfTk9ERV9S\nVU5fU0NSSVBUUwogICAgdmFsdWU6IGJ1aWxkCnJ1bjoKICB2b2x1bWVzOiBb\nXQogIGVudnM6IFtdCmRlcGxveToKICBuYW1lc3BhY2U6ICIiCiAgcmVtb3Rl\nOiBmYWxzZQogIGFubm90YXRpb25zOiB7fQogIG9wdGlvbnM6IHt9CiAgbGFi\nZWxzOiBbXQogIGhlYWx0aEVuZHBvaW50czoKICAgIGxpdmVuZXNzOiAvaGVh\nbHRoL2xpdmVuZXNzCiAgICByZWFkaW5lc3M6IC9oZWFsdGgvcmVhZGluZXNz\nCg==\n", + "encoding": "base64", + "_links": { + "self": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/contents/func.yaml?ref=main", + "git": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/git/blobs/91767c9c02cee2efe4fe901e10ca94a2fbda3f54", + "html": "https://github.com/openshift-dev-console/kn-func-typescript-http/blob/main/func.yaml" + } +} diff --git a/frontend/packages/dev-console/integration-tests/testData/git-import/kn-func-typescript-http/package.json b/frontend/packages/dev-console/integration-tests/testData/git-import/kn-func-typescript-http/package.json new file mode 100644 index 00000000000..0a527ac511f --- /dev/null +++ b/frontend/packages/dev-console/integration-tests/testData/git-import/kn-func-typescript-http/package.json @@ -0,0 +1,18 @@ +{ + "name": "package.json", + "path": "package.json", + "sha": "3736c3a890ae67db07e77ec5b12fd22680d50faf", + "size": 1301, + "url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/contents/package.json?ref=main", + "html_url": "https://github.com/openshift-dev-console/kn-func-typescript-http/blob/main/package.json", + "git_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/git/blobs/3736c3a890ae67db07e77ec5b12fd22680d50faf", + "download_url": "https://raw.githubusercontent.com/openshift-dev-console/kn-func-typescript-http/main/package.json", + "type": "file", + "content": "ewogICJuYW1lIjogImV2ZW50LWhhbmRsZXIiLAogICJ2ZXJzaW9uIjogIjAu\nMS4wIiwKICAiZGVzY3JpcHRpb24iOiAiVHlwZVNjcmlwdCBIVFRQIEhhbmRs\nZXIiLAogICJsaWNlbnNlIjogIkFwYWNoZS0yLjAiLAogICJyZXBvc2l0b3J5\nIjogewogICAgInR5cGUiOiAiZ2l0IiwKICAgICJ1cmwiOiAiIgogIH0sCiAg\nInNjcmlwdHMiOiB7CiAgICAiYnVpbGQiOiAibnB4IC1wIHR5cGVzY3JpcHQg\ndHNjIiwKICAgICJwcmV0ZXN0IjogIm5wbSBydW4gbGludCAmJiBucG0gcnVu\nIGJ1aWxkIiwKICAgICJ0ZXN0OnVuaXQiOiAidHMtbm9kZSBub2RlX21vZHVs\nZXMvdGFwZS9iaW4vdGFwZSB0ZXN0L3VuaXQudHMiLAogICAgInRlc3Q6aW50\nZWdyYXRpb24iOiAidHMtbm9kZSBub2RlX21vZHVsZXMvdGFwZS9iaW4vdGFw\nZSB0ZXN0L2ludGVncmF0aW9uLnRzIiwKICAgICJ0ZXN0IjogIm5wbSBydW4g\ndGVzdDp1bml0ICYmIG5wbSBydW4gdGVzdDppbnRlZ3JhdGlvbiIsCiAgICAi\nc3RhcnQiOiAiRlVOQ19MT0dfTEVWRUw9aW5mbyBmYWFzLWpzLXJ1bnRpbWUg\nLi9idWlsZC9pbmRleC5qcyIsCiAgICAibGludCI6ICJlc2xpbnQgXCJzcmMv\nKiovKi57anMsdHMsdHN4fVwiIFwidGVzdC8qKi8qLntqcyx0cyx0c3h9XCIg\nLS1xdWlldCIsCiAgICAiZGVidWciOiAibm9kZW1vbiAtLWluc3BlY3QgLi9u\nb2RlX21vZHVsZXMvZmFhcy1qcy1ydW50aW1lL2Jpbi9jbGkuanMgLi9idWls\nZC9pbmRleC5qcyIKICB9LAogICJkZXZEZXBlbmRlbmNpZXMiOiB7CiAgICAi\nQHR5cGVzL3RhcGUiOiAiXjQuMTMuMCIsCiAgICAiQHR5cGVzY3JpcHQtZXNs\naW50L2VzbGludC1wbHVnaW4iOiAiXjQuMjQuMCIsCiAgICAiQHR5cGVzY3Jp\ncHQtZXNsaW50L3BhcnNlciI6ICJeNC4yNC4wIiwKICAgICJlc2xpbnQiOiAi\nXjcuMjYuMCIsCiAgICAiZXNsaW50LWNvbmZpZy1wcmV0dGllciI6ICJeOC4z\nLjAiLAogICAgImVzbGludC1wbHVnaW4tcHJldHRpZXIiOiAiXjMuNC4wIiwK\nICAgICJub2RlbW9uIjogIl4yLjAuNCIsCiAgICAicHJldHRpZXIiOiAiXjIu\nMy4wIiwKICAgICJzdXBlcnRlc3QiOiAiXjYuMy4xIiwKICAgICJ0YXBlIjog\nIl40LjEzLjAiLAogICAgInRzLW5vZGUiOiAiXjkuMS4xIiwKICAgICJ0c2Qi\nOiAiXjAuMjQuMSIsCiAgICAidHNsaW50LWNvbmZpZy1wcmV0dGllciI6ICJe\nMS4xOC4wIiwKICAgICJ0eXBlc2NyaXB0IjogIl40LjIuNCIKICB9LAogICJk\nZXBlbmRlbmNpZXMiOiB7CiAgICAiQHR5cGVzL25vZGUiOiAiXjE2LjExLjEy\nIiwKICAgICJmYWFzLWpzLXJ1bnRpbWUiOiAiXjAuOS43IgogIH0KfQo=\n", + "encoding": "base64", + "_links": { + "self": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/contents/package.json?ref=main", + "git": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/git/blobs/3736c3a890ae67db07e77ec5b12fd22680d50faf", + "html": "https://github.com/openshift-dev-console/kn-func-typescript-http/blob/main/package.json" + } +} \ No newline at end of file diff --git a/frontend/packages/dev-console/integration-tests/testData/git-import/kn-func-typescript-http/repo.json b/frontend/packages/dev-console/integration-tests/testData/git-import/kn-func-typescript-http/repo.json new file mode 100644 index 00000000000..16457827efb --- /dev/null +++ b/frontend/packages/dev-console/integration-tests/testData/git-import/kn-func-typescript-http/repo.json @@ -0,0 +1,125 @@ +{ + "id": 621518477, + "node_id": "R_kgDOJQuejQ", + "name": "kn-func-typescript-http", + "full_name": "openshift-dev-console/kn-func-typescript-http", + "private": false, + "owner": { + "login": "openshift-dev-console", + "id": 123080219, + "node_id": "O_kgDOB1YOGw", + "avatar_url": "https://avatars.githubusercontent.com/u/123080219?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/openshift-dev-console", + "html_url": "https://github.com/openshift-dev-console", + "followers_url": "https://api.github.com/users/openshift-dev-console/followers", + "following_url": "https://api.github.com/users/openshift-dev-console/following{/other_user}", + "gists_url": "https://api.github.com/users/openshift-dev-console/gists{/gist_id}", + "starred_url": "https://api.github.com/users/openshift-dev-console/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/openshift-dev-console/subscriptions", + "organizations_url": "https://api.github.com/users/openshift-dev-console/orgs", + "repos_url": "https://api.github.com/users/openshift-dev-console/repos", + "events_url": "https://api.github.com/users/openshift-dev-console/events{/privacy}", + "received_events_url": "https://api.github.com/users/openshift-dev-console/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/openshift-dev-console/kn-func-typescript-http", + "description": "Knative func example created with [kn] func create -l typescript -t http", + "fork": false, + "url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http", + "forks_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/forks", + "keys_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/teams", + "hooks_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/hooks", + "issue_events_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/issues/events{/number}", + "events_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/events", + "assignees_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/assignees{/user}", + "branches_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/branches{/branch}", + "tags_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/tags", + "blobs_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/statuses/{sha}", + "languages_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/languages", + "stargazers_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/stargazers", + "contributors_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/contributors", + "subscribers_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/subscribers", + "subscription_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/subscription", + "commits_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/contents/{+path}", + "compare_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/merges", + "archive_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/downloads", + "issues_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/issues{/number}", + "pulls_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/pulls{/number}", + "milestones_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/milestones{/number}", + "notifications_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/labels{/name}", + "releases_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/releases{/id}", + "deployments_url": "https://api.github.com/repos/openshift-dev-console/kn-func-typescript-http/deployments", + "created_at": "2023-03-30T20:34:40Z", + "updated_at": "2023-03-30T21:00:39Z", + "pushed_at": "2023-03-30T20:53:11Z", + "git_url": "git://github.com/openshift-dev-console/kn-func-typescript-http.git", + "ssh_url": "git@github.com:openshift-dev-console/kn-func-typescript-http.git", + "clone_url": "https://github.com/openshift-dev-console/kn-func-typescript-http.git", + "svn_url": "https://github.com/openshift-dev-console/kn-func-typescript-http", + "homepage": "", + "size": 84, + "stargazers_count": 1, + "watchers_count": 1, + "language": "TypeScript", + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "has_discussions": false, + "forks_count": 1, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 0, + "license": null, + "allow_forking": true, + "is_template": false, + "web_commit_signoff_required": false, + "topics": [ + + ], + "visibility": "public", + "forks": 1, + "open_issues": 0, + "watchers": 1, + "default_branch": "main", + "temp_clone_token": null, + "organization": { + "login": "openshift-dev-console", + "id": 123080219, + "node_id": "O_kgDOB1YOGw", + "avatar_url": "https://avatars.githubusercontent.com/u/123080219?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/openshift-dev-console", + "html_url": "https://github.com/openshift-dev-console", + "followers_url": "https://api.github.com/users/openshift-dev-console/followers", + "following_url": "https://api.github.com/users/openshift-dev-console/following{/other_user}", + "gists_url": "https://api.github.com/users/openshift-dev-console/gists{/gist_id}", + "starred_url": "https://api.github.com/users/openshift-dev-console/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/openshift-dev-console/subscriptions", + "organizations_url": "https://api.github.com/users/openshift-dev-console/orgs", + "repos_url": "https://api.github.com/users/openshift-dev-console/repos", + "events_url": "https://api.github.com/users/openshift-dev-console/events{/privacy}", + "received_events_url": "https://api.github.com/users/openshift-dev-console/received_events", + "type": "Organization", + "site_admin": false + }, + "network_count": 1, + "subscribers_count": 1 + } diff --git a/frontend/packages/dev-console/integration-tests/testData/git-import/repos.ts b/frontend/packages/dev-console/integration-tests/testData/git-import/repos.ts index 3e216322342..7866bae2701 100644 --- a/frontend/packages/dev-console/integration-tests/testData/git-import/repos.ts +++ b/frontend/packages/dev-console/integration-tests/testData/git-import/repos.ts @@ -14,6 +14,30 @@ export const gitImportRepos: GithubRepo[] = [ url: 'https://github.com/rohitkrai03/flask-dockerfile-example', folder: 'flask-dockerfile-example', }, + { + url: 'https://github.com/Lucifergene/serverless-func-repo', + folder: 'serverless-func-repo', + }, + { + url: 'https://github.com/vikram-raj/hello-func-node', + folder: 'hello-func-node', + }, + { + url: 'https://github.com/vikram-raj/hello-func-node-env', + folder: 'hello-func-node-env', + }, + { + url: 'https://github.com/vikram-raj/hello-func-quarkus', + folder: 'hello-func-quarkus', + }, + { + url: 'https://github.com/openshift-dev-console/kn-func-typescript-http', + folder: 'kn-func-typescript-http', + }, + { + url: 'https://github.com/openshift-dev-console/kn-func-typescript-cloudevents', + folder: 'kn-func-typescript-cloudevents', + }, ]; interface GithubRepo { @@ -27,6 +51,7 @@ export function getResponseMocks(repo: GithubRepo) { let packageJson = null; let devFileResources = null; + let funcJson = null; try { packageJson = require(`./${repo.folder}/package.json`); } catch (err) { @@ -37,10 +62,16 @@ export function getResponseMocks(repo: GithubRepo) { } catch (err) { // nothing, the file does not exist } + try { + funcJson = require(`./${repo.folder}/func.json`); + } catch (err) { + // nothing, the file does not exist + } return { repoResponse: repoJson, contentsResponse: contentsJson, packageResponse: packageJson, devFileResources, + funcJson, }; } diff --git a/frontend/packages/dev-console/integration-tests/testData/git-import/serverless-func-repo/contents.json b/frontend/packages/dev-console/integration-tests/testData/git-import/serverless-func-repo/contents.json new file mode 100644 index 00000000000..a7e646eb866 --- /dev/null +++ b/frontend/packages/dev-console/integration-tests/testData/git-import/serverless-func-repo/contents.json @@ -0,0 +1,114 @@ +[ + { + "name": ".gitignore", + "path": ".gitignore", + "sha": "3abd5fdff11e295a0d72346ac99fe7034da1c6cb", + "size": 126, + "url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/contents/.gitignore?ref=master", + "html_url": "https://github.com/Lucifergene/serverless-func-repo/blob/master/.gitignore", + "git_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/git/blobs/3abd5fdff11e295a0d72346ac99fe7034da1c6cb", + "download_url": "https://raw.githubusercontent.com/Lucifergene/serverless-func-repo/master/.gitignore", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/Lucifergene/serverless-func-repo/contents/.gitignore?ref=master", + "git": "https://api.github.com/repos/Lucifergene/serverless-func-repo/git/blobs/3abd5fdff11e295a0d72346ac99fe7034da1c6cb", + "html": "https://github.com/Lucifergene/serverless-func-repo/blob/master/.gitignore" + } + }, + { + "name": "README.md", + "path": "README.md", + "sha": "3ecedd17b45af0a59d4a52dd648eda8ba090047a", + "size": 2096, + "url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/contents/README.md?ref=master", + "html_url": "https://github.com/Lucifergene/serverless-func-repo/blob/master/README.md", + "git_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/git/blobs/3ecedd17b45af0a59d4a52dd648eda8ba090047a", + "download_url": "https://raw.githubusercontent.com/Lucifergene/serverless-func-repo/master/README.md", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/Lucifergene/serverless-func-repo/contents/README.md?ref=master", + "git": "https://api.github.com/repos/Lucifergene/serverless-func-repo/git/blobs/3ecedd17b45af0a59d4a52dd648eda8ba090047a", + "html": "https://github.com/Lucifergene/serverless-func-repo/blob/master/README.md" + } + }, + { + "name": "func.yaml", + "path": "func.yaml", + "sha": "9336f0c8f3b0f5c37a55dc7e4e063fa99bfa4a29", + "size": 612, + "url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/contents/func.yaml?ref=master", + "html_url": "https://github.com/Lucifergene/serverless-func-repo/blob/master/func.yaml", + "git_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/git/blobs/9336f0c8f3b0f5c37a55dc7e4e063fa99bfa4a29", + "download_url": "https://raw.githubusercontent.com/Lucifergene/serverless-func-repo/master/func.yaml", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/Lucifergene/serverless-func-repo/contents/func.yaml?ref=master", + "git": "https://api.github.com/repos/Lucifergene/serverless-func-repo/git/blobs/9336f0c8f3b0f5c37a55dc7e4e063fa99bfa4a29", + "html": "https://github.com/Lucifergene/serverless-func-repo/blob/master/func.yaml" + } + }, + { + "name": "index.js", + "path": "index.js", + "sha": "04c81441d0b3e3e39bee13343c4058956f4ee0e4", + "size": 1463, + "url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/contents/index.js?ref=master", + "html_url": "https://github.com/Lucifergene/serverless-func-repo/blob/master/index.js", + "git_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/git/blobs/04c81441d0b3e3e39bee13343c4058956f4ee0e4", + "download_url": "https://raw.githubusercontent.com/Lucifergene/serverless-func-repo/master/index.js", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/Lucifergene/serverless-func-repo/contents/index.js?ref=master", + "git": "https://api.github.com/repos/Lucifergene/serverless-func-repo/git/blobs/04c81441d0b3e3e39bee13343c4058956f4ee0e4", + "html": "https://github.com/Lucifergene/serverless-func-repo/blob/master/index.js" + } + }, + { + "name": "package-lock.json", + "path": "package-lock.json", + "sha": "14391b4480f8919aa6cedfe7e92585eb55259d8a", + "size": 151831, + "url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/contents/package-lock.json?ref=master", + "html_url": "https://github.com/Lucifergene/serverless-func-repo/blob/master/package-lock.json", + "git_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/git/blobs/14391b4480f8919aa6cedfe7e92585eb55259d8a", + "download_url": "https://raw.githubusercontent.com/Lucifergene/serverless-func-repo/master/package-lock.json", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/Lucifergene/serverless-func-repo/contents/package-lock.json?ref=master", + "git": "https://api.github.com/repos/Lucifergene/serverless-func-repo/git/blobs/14391b4480f8919aa6cedfe7e92585eb55259d8a", + "html": "https://github.com/Lucifergene/serverless-func-repo/blob/master/package-lock.json" + } + }, + { + "name": "package.json", + "path": "package.json", + "sha": "2806b7a4ce0a7caf6f1e3d9c0a833c81db97f7eb", + "size": 565, + "url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/contents/package.json?ref=master", + "html_url": "https://github.com/Lucifergene/serverless-func-repo/blob/master/package.json", + "git_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/git/blobs/2806b7a4ce0a7caf6f1e3d9c0a833c81db97f7eb", + "download_url": "https://raw.githubusercontent.com/Lucifergene/serverless-func-repo/master/package.json", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/Lucifergene/serverless-func-repo/contents/package.json?ref=master", + "git": "https://api.github.com/repos/Lucifergene/serverless-func-repo/git/blobs/2806b7a4ce0a7caf6f1e3d9c0a833c81db97f7eb", + "html": "https://github.com/Lucifergene/serverless-func-repo/blob/master/package.json" + } + }, + { + "name": "test", + "path": "test", + "sha": "9cea2aab5cb7d30f925060499367172b566d0842", + "size": 0, + "url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/contents/test?ref=master", + "html_url": "https://github.com/Lucifergene/serverless-func-repo/tree/master/test", + "git_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/git/trees/9cea2aab5cb7d30f925060499367172b566d0842", + "download_url": null, + "type": "dir", + "_links": { + "self": "https://api.github.com/repos/Lucifergene/serverless-func-repo/contents/test?ref=master", + "git": "https://api.github.com/repos/Lucifergene/serverless-func-repo/git/trees/9cea2aab5cb7d30f925060499367172b566d0842", + "html": "https://github.com/Lucifergene/serverless-func-repo/tree/master/test" + } + } + ] diff --git a/frontend/packages/dev-console/integration-tests/testData/git-import/serverless-func-repo/func.json b/frontend/packages/dev-console/integration-tests/testData/git-import/serverless-func-repo/func.json new file mode 100644 index 00000000000..c1c22112d99 --- /dev/null +++ b/frontend/packages/dev-console/integration-tests/testData/git-import/serverless-func-repo/func.json @@ -0,0 +1,18 @@ +{ + "name": "func.yaml", + "path": "func.yaml", + "sha": "9336f0c8f3b0f5c37a55dc7e4e063fa99bfa4a29", + "size": 612, + "url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/contents/func.yaml?ref=master", + "html_url": "https://github.com/Lucifergene/serverless-func-repo/blob/master/func.yaml", + "git_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/git/blobs/9336f0c8f3b0f5c37a55dc7e4e063fa99bfa4a29", + "download_url": "https://raw.githubusercontent.com/Lucifergene/serverless-func-repo/master/func.yaml", + "type": "file", + "content": "c3BlY1ZlcnNpb246IDAuMzQuMApuYW1lOiBoZWxsby1mdW5jLW5vZGUKcnVu\ndGltZTogbm9kZQpyZWdpc3RyeTogcXVheS5pby92aXJhal8xCmltYWdlOiBx\ndWF5LmlvL3ZpcmFqXzEvaGVsbG8tZnVuYy1ub2RlOmxhdGVzdAppbWFnZURp\nZ2VzdDogc2hhMjU2OmNiOTFjNmY4YThhODk3YTE0MjMxMjg4YjkwYWYzNWVm\nYTAwZmRhMjRjMzk5ZWQ5M2FmZGRlNThiYmNkOWI0Y2EKY3JlYXRlZDogMjAy\nMi0xMS0xNlQxNTozODozOC41MDIyNzUyNDQrMDU6MzAKaW52b2NhdGlvbjoK\nICBmb3JtYXQ6IGh0dHAKYnVpbGQ6CiAgYnVpbGRwYWNrczogW10KICBidWls\nZGVyOiBzMmkKICBidWlsZEVudnM6IAogIC0gbmFtZTogTVlfQlVJTERfS0VZ\nCiAgICB2YWx1ZTogdGVzdHMKcnVuOgogIHZvbHVtZXM6IFtdCiAgZW52czoK\nICAtIG5hbWU6IE1ZX0FQSV9LRVkKICAgIHZhbHVlOiAne3sgZW52OkFQSV9L\nRVkgfX0nCmRlcGxveToKICBuYW1lc3BhY2U6IHZpcmFqCiAgYW5ub3RhdGlv\nbnM6IHt9CiAgb3B0aW9uczoge30KICBsYWJlbHM6IFtdCiAgaGVhbHRoRW5k\ncG9pbnRzOgogICAgbGl2ZW5lc3M6IC9oZWFsdGgvbGl2ZW5lc3MKICAgIHJl\nYWRpbmVzczogL2hlYWx0aC9yZWFkaW5lc3MK\n", + "encoding": "base64", + "_links": { + "self": "https://api.github.com/repos/Lucifergene/serverless-func-repo/contents/func.yaml?ref=master", + "git": "https://api.github.com/repos/Lucifergene/serverless-func-repo/git/blobs/9336f0c8f3b0f5c37a55dc7e4e063fa99bfa4a29", + "html": "https://github.com/Lucifergene/serverless-func-repo/blob/master/func.yaml" + } +} \ No newline at end of file diff --git a/frontend/packages/dev-console/integration-tests/testData/git-import/serverless-func-repo/package.json b/frontend/packages/dev-console/integration-tests/testData/git-import/serverless-func-repo/package.json new file mode 100644 index 00000000000..0361fd3fedf --- /dev/null +++ b/frontend/packages/dev-console/integration-tests/testData/git-import/serverless-func-repo/package.json @@ -0,0 +1,18 @@ +{ + "name": "package.json", + "path": "package.json", + "sha": "2806b7a4ce0a7caf6f1e3d9c0a833c81db97f7eb", + "size": 565, + "url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/contents/package.json?ref=master", + "html_url": "https://github.com/Lucifergene/serverless-func-repo/blob/master/package.json", + "git_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/git/blobs/2806b7a4ce0a7caf6f1e3d9c0a833c81db97f7eb", + "download_url": "https://raw.githubusercontent.com/Lucifergene/serverless-func-repo/master/package.json", + "type": "file", + "content": "ewogICJuYW1lIjogImh0dHAtaGFuZGxlciIsCiAgInZlcnNpb24iOiAiMC4x\nLjAiLAogICJkZXNjcmlwdGlvbiI6ICJBIGZ1bmN0aW9uIHdoaWNoIHJlc3Bv\nbmRzIHRvIEhUVFAgcmVxdWVzdHMiLAogICJtYWluIjogImluZGV4LmpzIiwK\nICAic2NyaXB0cyI6IHsKICAgICJ0ZXN0IjogIm5vZGUgdGVzdC91bml0Lmpz\nICYmIG5vZGUgdGVzdC9pbnRlZ3JhdGlvbi5qcyIsCiAgICAic3RhcnQiOiAi\nZmFhcy1qcy1ydW50aW1lIC4vaW5kZXguanMiLAogICAgImRlYnVnIjogIm5v\nZGVtb24gLS1pbnNwZWN0IC4vbm9kZV9tb2R1bGVzL2ZhYXMtanMtcnVudGlt\nZS9iaW4vY2xpLmpzIC4vaW5kZXguanMiCiAgfSwKICAia2V5d29yZHMiOiBb\nXSwKICAiYXV0aG9yIjogIiIsCiAgImxpY2Vuc2UiOiAiQXBhY2hlLTIuMCIs\nCiAgImRlcGVuZGVuY2llcyI6IHsKICAgICJmYWFzLWpzLXJ1bnRpbWUiOiAi\nXjAuOS4wIgogIH0sCiAgImRldkRlcGVuZGVuY2llcyI6IHsKICAgICJub2Rl\nbW9uIjogIl4yLjAuNCIsCiAgICAic3VwZXJ0ZXN0IjogIl40LjAuMiIsCiAg\nICAidGFwZSI6ICJeNS4wLjEiCiAgfQp9Cg==\n", + "encoding": "base64", + "_links": { + "self": "https://api.github.com/repos/Lucifergene/serverless-func-repo/contents/package.json?ref=master", + "git": "https://api.github.com/repos/Lucifergene/serverless-func-repo/git/blobs/2806b7a4ce0a7caf6f1e3d9c0a833c81db97f7eb", + "html": "https://github.com/Lucifergene/serverless-func-repo/blob/master/package.json" + } +} \ No newline at end of file diff --git a/frontend/packages/dev-console/integration-tests/testData/git-import/serverless-func-repo/repo.json b/frontend/packages/dev-console/integration-tests/testData/git-import/serverless-func-repo/repo.json new file mode 100644 index 00000000000..728449104a5 --- /dev/null +++ b/frontend/packages/dev-console/integration-tests/testData/git-import/serverless-func-repo/repo.json @@ -0,0 +1,105 @@ +{ + "id": 590432017, + "node_id": "R_kgDOIzFHEQ", + "name": "serverless-func-repo", + "full_name": "Lucifergene/serverless-func-repo", + "private": false, + "owner": { + "login": "Lucifergene", + "id": 47265560, + "node_id": "MDQ6VXNlcjQ3MjY1NTYw", + "avatar_url": "https://avatars.githubusercontent.com/u/47265560?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Lucifergene", + "html_url": "https://github.com/Lucifergene", + "followers_url": "https://api.github.com/users/Lucifergene/followers", + "following_url": "https://api.github.com/users/Lucifergene/following{/other_user}", + "gists_url": "https://api.github.com/users/Lucifergene/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Lucifergene/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Lucifergene/subscriptions", + "organizations_url": "https://api.github.com/users/Lucifergene/orgs", + "repos_url": "https://api.github.com/users/Lucifergene/repos", + "events_url": "https://api.github.com/users/Lucifergene/events{/privacy}", + "received_events_url": "https://api.github.com/users/Lucifergene/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/Lucifergene/serverless-func-repo", + "description": null, + "fork": false, + "url": "https://api.github.com/repos/Lucifergene/serverless-func-repo", + "forks_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/forks", + "keys_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/teams", + "hooks_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/hooks", + "issue_events_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/issues/events{/number}", + "events_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/events", + "assignees_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/assignees{/user}", + "branches_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/branches{/branch}", + "tags_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/tags", + "blobs_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/statuses/{sha}", + "languages_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/languages", + "stargazers_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/stargazers", + "contributors_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/contributors", + "subscribers_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/subscribers", + "subscription_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/subscription", + "commits_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/contents/{+path}", + "compare_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/merges", + "archive_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/downloads", + "issues_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/issues{/number}", + "pulls_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/pulls{/number}", + "milestones_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/milestones{/number}", + "notifications_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/labels{/name}", + "releases_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/releases{/id}", + "deployments_url": "https://api.github.com/repos/Lucifergene/serverless-func-repo/deployments", + "created_at": "2023-01-18T12:03:32Z", + "updated_at": "2023-01-18T12:03:55Z", + "pushed_at": "2023-01-18T12:03:46Z", + "git_url": "git://github.com/Lucifergene/serverless-func-repo.git", + "ssh_url": "git@github.com:Lucifergene/serverless-func-repo.git", + "clone_url": "https://github.com/Lucifergene/serverless-func-repo.git", + "svn_url": "https://github.com/Lucifergene/serverless-func-repo", + "homepage": null, + "size": 70, + "stargazers_count": 0, + "watchers_count": 0, + "language": "JavaScript", + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "has_discussions": false, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 0, + "license": null, + "allow_forking": true, + "is_template": false, + "web_commit_signoff_required": false, + "topics": [ + + ], + "visibility": "public", + "forks": 0, + "open_issues": 0, + "watchers": 0, + "default_branch": "master", + "temp_clone_token": null, + "network_count": 0, + "subscribers_count": 2 + } diff --git a/frontend/packages/dev-console/locales/en/devconsole.json b/frontend/packages/dev-console/locales/en/devconsole.json index cf2ae408e23..b7577bb2b3b 100644 --- a/frontend/packages/dev-console/locales/en/devconsole.json +++ b/frontend/packages/dev-console/locales/en/devconsole.json @@ -198,6 +198,7 @@ "A <1>DeploymentConfig to rollout new revisions when the Image changes.": "A <1>DeploymentConfig to rollout new revisions when the Image changes.", "A <1>Service to expose your workload inside the Cluster.": "A <1>Service to expose your workload inside the Cluster.", "An optional <1>Route to expose your workload outside the Cluster.": "An optional <1>Route to expose your workload outside the Cluster.", + "Create Builder Image Sample": "Create Builder Image Sample", "Create Devfile Sample": "Create Devfile Sample", "Instantiate Template": "Instantiate Template", "Pause rollouts": "Pause rollouts", @@ -641,7 +642,8 @@ "Average Container bandwidth by Pod: transmitted": "Average Container bandwidth by Pod: transmitted", "Successfully updated the project access.": "Successfully updated the project access.", "Project access": "Project access", - "Project access allows you to add or remove a user's access to the project. More advanced management of role-based access control appear in <1>Roles and <4>Role Bindings. For more information, see the <7>role-based access control documentation.": "Project access allows you to add or remove a user's access to the project. More advanced management of role-based access control appear in <1>Roles and <4>Role Bindings. For more information, see the <7>role-based access control documentation.", + "Project access allows you to add or remove a user's access to the project. More advanced management of role-based access control appear in <1>Roles and <4>Role Bindings.": "Project access allows you to add or remove a user's access to the project. More advanced management of role-based access control appear in <1>Roles and <4>Role Bindings.", + " For more information, see the <3>role-based access control documentation.": " For more information, see the <3>role-based access control documentation.", "Select a type": "Select a type", "Add access": "Add access", "Subject": "Subject", diff --git a/frontend/packages/dev-console/src/components/add/SampleGettingStartedCard.tsx b/frontend/packages/dev-console/src/components/add/SampleGettingStartedCard.tsx index 58732fb57d3..8aad516e423 100644 --- a/frontend/packages/dev-console/src/components/add/SampleGettingStartedCard.tsx +++ b/frontend/packages/dev-console/src/components/add/SampleGettingStartedCard.tsx @@ -66,6 +66,10 @@ export const SampleGettingStartedCard: React.FC = const orderedCatalogItems = orderCatalogItems(service.items || [], featured); const slicedCatalogItems = orderedCatalogItems.slice(0, 2); + if (service.loaded && slicedCatalogItems.length === 0) { + return null; + } + const links: GettingStartedLink[] = service.loaded ? slicedCatalogItems.map((item) => { return { diff --git a/frontend/packages/dev-console/src/components/builds/BuildsTabListPage.tsx b/frontend/packages/dev-console/src/components/builds/BuildsTabListPage.tsx index 7b0d4816477..81f5681b296 100644 --- a/frontend/packages/dev-console/src/components/builds/BuildsTabListPage.tsx +++ b/frontend/packages/dev-console/src/components/builds/BuildsTabListPage.tsx @@ -62,8 +62,13 @@ const BuildsTabListPage: React.FC = ({ match }) => { // Shipwright Builds from @console/shipwright-plugin // We resolve this plugin (list) page by string, so that we don't have // a bi-directional or circular dependency to the shipwright-plugin. - const shipwrightKind = 'shipwright.io~v1alpha1~Build'; - const shipwrightBuildEnabled = useFlag('SHIPWRIGHT_BUILD'); + const SHIPWRIGHT_BUILD = useFlag('SHIPWRIGHT_BUILD'); + const SHIPWRIGHT_BUILD_V1ALPHA1 = useFlag('SHIPWRIGHT_BUILD_V1ALPHA1'); + + const shipwrightBuildEnabled = SHIPWRIGHT_BUILD || SHIPWRIGHT_BUILD_V1ALPHA1; + const shipwrightKind = SHIPWRIGHT_BUILD + ? 'shipwright.io~v1beta1~Build' + : 'shipwright.io~v1alpha1~Build'; const shipwrightBuildLoader = resourceListPages.get(shipwrightKind); const [shipwrightBuildModel] = useK8sModel(shipwrightKind); const shipwrightBuildComponent = React.useMemo(() => { diff --git a/frontend/packages/dev-console/src/components/catalog/providers/__tests__/useDevfileSamples.data.ts b/frontend/packages/dev-console/src/components/catalog/providers/__tests__/useDevfileSamples.data.ts index 1b0b9951c4b..1e2853bba8f 100644 --- a/frontend/packages/dev-console/src/components/catalog/providers/__tests__/useDevfileSamples.data.ts +++ b/frontend/packages/dev-console/src/components/catalog/providers/__tests__/useDevfileSamples.data.ts @@ -66,6 +66,7 @@ export const expectedCatalogItems: CatalogItem[] = [ type: 'Devfile', name: 'Basic Node.js', provider: undefined, + secondaryLabel: 'Samples', description: 'A simple Hello World Node.js application', tags: ['NodeJS', 'Express'], cta: { @@ -80,6 +81,7 @@ export const expectedCatalogItems: CatalogItem[] = [ type: 'Devfile', name: 'Basic Quarkus', provider: undefined, + secondaryLabel: 'Samples', description: 'A simple Hello World Java application using Quarkus', tags: ['Java', 'Quarkus'], cta: { @@ -94,6 +96,7 @@ export const expectedCatalogItems: CatalogItem[] = [ type: 'Devfile', name: 'Basic Spring Boot', provider: undefined, + secondaryLabel: 'Samples', description: 'A simple Hello World Java Spring Boot application using Maven', tags: ['Java', 'Spring'], cta: { @@ -108,6 +111,7 @@ export const expectedCatalogItems: CatalogItem[] = [ type: 'Devfile', name: 'Basic Python', provider: undefined, + secondaryLabel: 'Samples', description: 'A simple Hello World application using Python', tags: ['Python'], cta: { diff --git a/frontend/packages/dev-console/src/components/catalog/providers/useBuilderImageSamples.ts b/frontend/packages/dev-console/src/components/catalog/providers/useBuilderImageSamples.ts index 2de915c2bfe..a2cebeb60be 100644 --- a/frontend/packages/dev-console/src/components/catalog/providers/useBuilderImageSamples.ts +++ b/frontend/packages/dev-console/src/components/catalog/providers/useBuilderImageSamples.ts @@ -30,13 +30,14 @@ const normalizeBuilderImages = ( const provider = annotations?.[ANNOTATIONS.providerDisplayName] ?? ''; const creationTimestamp = imageStream.metadata?.creationTimestamp; const href = `/samples/ns/${activeNamespace}/${name}/${imageStreamNS}`; - const createLabel = t('devconsole~Create'); + const createLabel = t('devconsole~Create Builder Image Sample'); const type = 'BuilderImage'; const item: CatalogItem = { uid: `${type}-${uid}`, type, name: title, + secondaryLabel: 'Samples', provider, description, creationTimestamp, diff --git a/frontend/packages/dev-console/src/components/catalog/providers/useDevfileSamples.tsx b/frontend/packages/dev-console/src/components/catalog/providers/useDevfileSamples.tsx index 0df3d0ef8dd..ba3d6a7bef4 100644 --- a/frontend/packages/dev-console/src/components/catalog/providers/useDevfileSamples.tsx +++ b/frontend/packages/dev-console/src/components/catalog/providers/useDevfileSamples.tsx @@ -29,6 +29,7 @@ const normalizeDevfileSamples = ( uid, type: 'Devfile', name: displayName, + secondaryLabel: 'Samples', description, tags, provider, diff --git a/frontend/packages/dev-console/src/components/deployments/DeploymentForm.tsx b/frontend/packages/dev-console/src/components/deployments/DeploymentForm.tsx index 5e43aa01b1b..aee270b6479 100644 --- a/frontend/packages/dev-console/src/components/deployments/DeploymentForm.tsx +++ b/frontend/packages/dev-console/src/components/deployments/DeploymentForm.tsx @@ -53,8 +53,7 @@ const EditDeploymentForm: React.FC< const yamlEditor = ( <> - -
+ {resource.kind === DeploymentConfigModel.kind && } = ({ const [showYAMLAlert, setShowYAMLAlert] = React.useState(true); return ( <> - + {resourceObj.kind === DeploymentConfigModel.kind && } {showYAMLAlert && setShowYAMLAlert(false)} />} diff --git a/frontend/packages/dev-console/src/components/deployments/utils/deployment-utils.ts b/frontend/packages/dev-console/src/components/deployments/utils/deployment-utils.ts index cd82c8e4cbd..21f2028b5c6 100644 --- a/frontend/packages/dev-console/src/components/deployments/utils/deployment-utils.ts +++ b/frontend/packages/dev-console/src/components/deployments/utils/deployment-utils.ts @@ -358,7 +358,7 @@ export const getTriggersAndImageStreamValues = ( return { ...data, triggers: { - image: imageTrigger?.pause === 'false', + image: imageTrigger?.paused === 'false', }, fromImageStreamTag: !!imageTrigger, imageStream: { @@ -595,7 +595,12 @@ export const convertEditFormToDeployment = ( containers: getUpdatedContainers(containers, fromImageStreamTag, isi, imageName, envs), imagePullSecrets: [ ...(deployment.spec.template.spec.imagePullSecrets ?? []), - ...(imagePullSecret ? [{ name: imagePullSecret }] : []), + ...(imagePullSecret && + !(deployment.spec.template.spec.imagePullSecrets ?? []).some( + (secret) => secret.name === imagePullSecret, + ) + ? [{ name: imagePullSecret }] + : []), ], }, }, diff --git a/frontend/packages/dev-console/src/components/edit-application/edit-application-utils.ts b/frontend/packages/dev-console/src/components/edit-application/edit-application-utils.ts index c5d01740947..cddc30dd1ca 100644 --- a/frontend/packages/dev-console/src/components/edit-application/edit-application-utils.ts +++ b/frontend/packages/dev-console/src/components/edit-application/edit-application-utils.ts @@ -348,7 +348,7 @@ export const getDeploymentData = (resource: K8sResourceKind) => { return { env, triggers: { - image: imageTrigger?.pause === 'false', + image: imageTrigger?.paused === 'false', }, replicas: resource.spec?.replicas ?? 1, }; diff --git a/frontend/packages/dev-console/src/components/health-checks/AddHealthChecks.tsx b/frontend/packages/dev-console/src/components/health-checks/AddHealthChecks.tsx index fad32426315..512616d110b 100644 --- a/frontend/packages/dev-console/src/components/health-checks/AddHealthChecks.tsx +++ b/frontend/packages/dev-console/src/components/health-checks/AddHealthChecks.tsx @@ -10,6 +10,7 @@ import { documentationURLs, getDocumentationURL, history, + isManaged, PageHeading, ResourceLink, } from '@console/internal/components/utils'; @@ -84,9 +85,11 @@ const AddHealthChecks: React.FC & AddHealthChecksProps title={ <> {pageTitle} - + {!isManaged() && ( + + )} } /> diff --git a/frontend/packages/dev-console/src/components/hpa/HPADetailsForm.tsx b/frontend/packages/dev-console/src/components/hpa/HPADetailsForm.tsx index 4f147785022..f2c7bd54074 100644 --- a/frontend/packages/dev-console/src/components/hpa/HPADetailsForm.tsx +++ b/frontend/packages/dev-console/src/components/hpa/HPADetailsForm.tsx @@ -73,10 +73,12 @@ const HPADetailsForm: React.FC = () => { & GitImportFormProps> = ({ @@ -51,6 +53,11 @@ const GitImportForm: React.FC & GitImportFormProps> = const showExtensionCards = values.import.selectedStrategy.type === ImportStrategy.SERVERLESS_FUNCTION && isSample; + const showSecureRouteSectionForDevfile = + (importType === ImportTypes.devfile || + values.import.selectedStrategy.type === ImportStrategy.DEVFILE) && + values?.devfile?.devfileSuggestedResources?.route?.spec?.tls; + return (
@@ -86,6 +93,11 @@ const GitImportForm: React.FC & GitImportFormProps> = )} + {showSecureRouteSectionForDevfile && ( +
+ +
+ )} )} diff --git a/frontend/packages/dev-console/src/components/import/__tests__/import-submit-utils-data.ts b/frontend/packages/dev-console/src/components/import/__tests__/import-submit-utils-data.ts index 7ef5ce453d8..3e41ad246aa 100644 --- a/frontend/packages/dev-console/src/components/import/__tests__/import-submit-utils-data.ts +++ b/frontend/packages/dev-console/src/components/import/__tests__/import-submit-utils-data.ts @@ -895,13 +895,13 @@ export const sampleClusterTriggerBinding = { metadata: { annotations: { 'kubectl.kubernetes.io/last-applied-configuration': - '{"apiVersion":"triggers.tekton.dev/v1alpha1","kind":"ClusterTriggerBinding","metadata":{"name":"github-push","namespace":"openshift-pipelines","ownerReferences":[{"apiVersion":"operator.tekton.dev/v1alpha1","blockOwnerDeletion":true,"controller":true,"kind":"TektonInstallerSet","name":"addon-triggers-k74bh","uid":"fe29d5cd-2580-48fa-b6de-a236c518e2e8"}]},"spec":{"params":[{"name":"git-revision","value":"$(body.head_commit.id)"},{"name":"git-commit-message","value":"$(body.head_commit.message)"},{"name":"git-repo-url","value":"$(body.repository.url)"},{"name":"git-repo-name","value":"$(body.repository.name)"},{"name":"content-type","value":"$(header.Content-Type)"},{"name":"pusher-name","value":"$(body.pusher.name)"}]}}\n', + '{"apiVersion":"triggers.tekton.dev/v1beta1","kind":"ClusterTriggerBinding","metadata":{"name":"github-push","namespace":"openshift-pipelines","ownerReferences":[{"apiVersion":"operator.tekton.dev/v1alpha1","blockOwnerDeletion":true,"controller":true,"kind":"TektonInstallerSet","name":"addon-triggers-k74bh","uid":"fe29d5cd-2580-48fa-b6de-a236c518e2e8"}]},"spec":{"params":[{"name":"git-revision","value":"$(body.head_commit.id)"},{"name":"git-commit-message","value":"$(body.head_commit.message)"},{"name":"git-repo-url","value":"$(body.repository.url)"},{"name":"git-repo-name","value":"$(body.repository.name)"},{"name":"content-type","value":"$(header.Content-Type)"},{"name":"pusher-name","value":"$(body.pusher.name)"}]}}\n', }, creationTimestamp: '2021-12-15T04:29:42Z', generation: 1, managedFields: [ { - apiVersion: 'triggers.tekton.dev/v1alpha1', + apiVersion: 'triggers.tekton.dev/v1beta1', fieldsType: 'FieldsV1', fieldsV1: { 'f:metadata': { diff --git a/frontend/packages/dev-console/src/components/import/__tests__/import-submit-utils.spec.ts b/frontend/packages/dev-console/src/components/import/__tests__/import-submit-utils.spec.ts index 7a061e4f927..c12595b4a98 100644 --- a/frontend/packages/dev-console/src/components/import/__tests__/import-submit-utils.spec.ts +++ b/frontend/packages/dev-console/src/components/import/__tests__/import-submit-utils.spec.ts @@ -53,7 +53,7 @@ describe('Import Submit Utils', () => { namespace: 'gijohn', }, fieldPath: 'spec.template.spec.containers[?(@.name=="nodejs-ex-git")].image', - pause: 'false', + paused: 'false', }, ]); done(); @@ -426,7 +426,7 @@ describe('Import Submit Utils', () => { 'app.openshift.io/vcs-ref': 'master', 'app.openshift.io/vcs-uri': 'https://github.com/redhat-developer/devfile-sample', 'image.openshift.io/triggers': - '[{"from":{"kind":"ImageStreamTag","name":"devfile-sample:latest","namespace":"gijohn"},"fieldPath":"spec.template.spec.containers[?(@.name==\\"devfile-sample\\")].image","pause":"false"}]', + '[{"from":{"kind":"ImageStreamTag","name":"devfile-sample:latest","namespace":"gijohn"},"fieldPath":"spec.template.spec.containers[?(@.name==\\"devfile-sample\\")].image","paused":"false"}]', isFromDevfile: 'true', 'openshift.io/generated-by': 'OpenShiftWebConsole', 'app.openshift.io/route-disabled': 'false', diff --git a/frontend/packages/dev-console/src/components/import/__tests__/upload-jar-submit-utils.spec.ts b/frontend/packages/dev-console/src/components/import/__tests__/upload-jar-submit-utils.spec.ts index 891137f4f00..a5a1a3b294e 100644 --- a/frontend/packages/dev-console/src/components/import/__tests__/upload-jar-submit-utils.spec.ts +++ b/frontend/packages/dev-console/src/components/import/__tests__/upload-jar-submit-utils.spec.ts @@ -44,7 +44,7 @@ describe('Upload Jar Submit Utils', () => { namespace: 'my-app', }, fieldPath: 'spec.template.spec.containers[?(@.name=="java-ex-git")].image', - pause: 'false', + paused: 'false', }, ]); done(); diff --git a/frontend/packages/dev-console/src/components/import/advanced/ScalingSection.tsx b/frontend/packages/dev-console/src/components/import/advanced/ScalingSection.tsx index 14a4d350704..c0bfe7ba93d 100644 --- a/frontend/packages/dev-console/src/components/import/advanced/ScalingSection.tsx +++ b/frontend/packages/dev-console/src/components/import/advanced/ScalingSection.tsx @@ -15,6 +15,7 @@ const ScalingSection: React.FC<{ name: string }> = ({ name }) => { name={name} label={t('devconsole~Replicas')} helpText={t('devconsole~The number of instances of your Image.')} + setOutputAsIntegerFlag /> ); diff --git a/frontend/packages/dev-console/src/components/import/import-types.ts b/frontend/packages/dev-console/src/components/import/import-types.ts index 53b7dbce959..22b252f468c 100644 --- a/frontend/packages/dev-console/src/components/import/import-types.ts +++ b/frontend/packages/dev-console/src/components/import/import-types.ts @@ -387,3 +387,10 @@ export interface HealthChecksFormData { livenessProbe: HealthCheckFormProbe; startupProbe?: HealthCheckFormProbe; } + +export enum BuildOptions { + BUILDS = 'BUILDS', + SHIPWRIGHT_BUILD = 'SHIPWRIGHT_BUILD', + PIPELINES = 'PIPELINES', + DISABLED = 'DISABLED', +} diff --git a/frontend/packages/dev-console/src/components/import/import-validation-utils.ts b/frontend/packages/dev-console/src/components/import/import-validation-utils.ts index bd7737003e5..99a6a2829a5 100644 --- a/frontend/packages/dev-console/src/components/import/import-validation-utils.ts +++ b/frontend/packages/dev-console/src/components/import/import-validation-utils.ts @@ -19,6 +19,7 @@ import { gitUrlRegex, resourcesValidationSchema, devfileValidationSchema, + importFlowPipelineTemplateValidationSchema, } from './validation-schema'; export const validationSchema = (t: TFunction) => @@ -38,6 +39,7 @@ export const validationSchema = (t: TFunction) => resources: resourcesValidationSchema, healthChecks: healthChecksProbesValidationSchema(t), pac: importFlowRepositoryValidationSchema(t), + pipeline: importFlowPipelineTemplateValidationSchema, }); const hasDomain = (url: string, domain: string): boolean => { diff --git a/frontend/packages/dev-console/src/components/import/section/ResourceSection.tsx b/frontend/packages/dev-console/src/components/import/section/ResourceSection.tsx index 8ea4716d909..085c9ab51e6 100644 --- a/frontend/packages/dev-console/src/components/import/section/ResourceSection.tsx +++ b/frontend/packages/dev-console/src/components/import/section/ResourceSection.tsx @@ -4,6 +4,7 @@ import { FormikValues, useField, useFormikContext } from 'formik'; import * as _ from 'lodash'; import { Trans, useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom'; +import { FLAG_OPENSHIFT_DEPLOYMENTCONFIG } from '@console/dev-console/src/const'; import { ImportStrategy } from '@console/git-service/src'; import { getActiveNamespace } from '@console/internal/actions/ui'; import { useAccessReview } from '@console/internal/components/utils'; @@ -46,6 +47,9 @@ const ResourceSection: React.FC = ({ flags }) => { flags[FLAG_KNATIVE_SERVING_SERVICE] && knativeServiceAccess; + const canIncludeDeploymentConfig = + !invalidTypes.includes(Resources.OpenShift) && flags[FLAG_OPENSHIFT_DEPLOYMENTCONFIG]; + const [, setResourceType] = useResourceType(); const onChange = React.useCallback( @@ -69,7 +73,7 @@ const ResourceSection: React.FC = ({ flags }) => { ), }); } - if (!invalidTypes.includes(Resources.OpenShift)) { + if (canIncludeDeploymentConfig) { options.push({ label: t(ReadableResourcesNames[Resources.OpenShift]), value: Resources.OpenShift, @@ -90,7 +94,7 @@ const ResourceSection: React.FC = ({ flags }) => { }); } return options; - }, [invalidTypes, canIncludeKnative, t]); + }, [invalidTypes, canIncludeDeploymentConfig, canIncludeKnative, t]); if ( !['edit', 'knatify'].includes(values.formType) && @@ -122,4 +126,7 @@ const ResourceSection: React.FC = ({ flags }) => { return null; }; -export default connectToFlags(FLAG_KNATIVE_SERVING_SERVICE)(ResourceSection); +export default connectToFlags( + FLAG_KNATIVE_SERVING_SERVICE, + FLAG_OPENSHIFT_DEPLOYMENTCONFIG, +)(ResourceSection); diff --git a/frontend/packages/dev-console/src/components/import/validation-schema.ts b/frontend/packages/dev-console/src/components/import/validation-schema.ts index 284ffab3c96..396be0a3620 100644 --- a/frontend/packages/dev-console/src/components/import/validation-schema.ts +++ b/frontend/packages/dev-console/src/components/import/validation-schema.ts @@ -2,9 +2,10 @@ import { TFunction } from 'i18next'; import * as _ from 'lodash'; import * as yup from 'yup'; import { convertToBaseValue } from '@console/internal/components/utils'; +import { PipelineType } from '@console/pipelines-plugin/src/components/import/import-types'; import { CREATE_APPLICATION_KEY } from '@console/topology/src/const'; import { isInteger } from '../../utils/yup-validation-util'; -import { Resources } from './import-types'; +import { BuildOptions, Resources } from './import-types'; import { removeKsvcInfoFromDomainMapping } from './serverless/serverless-utils'; const hostnameRegex = /^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$/; @@ -346,3 +347,14 @@ export const isiValidationSchema = (t: TFunction) => tag: yup.string(), status: yup.string().required(t('devconsole~Required')), }); + +export const importFlowPipelineTemplateValidationSchema = yup + .object() + .when(['enabled', 'type', 'build.option', 'pac.pipelineEnabled'], { + is: (isPipelineEnabled, pipelineType, buildOption, isPACPipelineEnabled) => + (isPipelineEnabled || buildOption === BuildOptions.PIPELINES || isPACPipelineEnabled) && + pipelineType !== PipelineType.PAC, + then: yup.object().shape({ + templateSelected: yup.string().required(), + }), + }); diff --git a/frontend/packages/dev-console/src/components/monitoring/alerts/MonitoringAlerts.tsx b/frontend/packages/dev-console/src/components/monitoring/alerts/MonitoringAlerts.tsx index 671e8126eb7..81949378951 100644 --- a/frontend/packages/dev-console/src/components/monitoring/alerts/MonitoringAlerts.tsx +++ b/frontend/packages/dev-console/src/components/monitoring/alerts/MonitoringAlerts.tsx @@ -13,6 +13,8 @@ import { useResolvedExtensions, AlertingRulesSourceExtension, isAlertingRulesSource, + AlertStates, + RuleStates, } from '@console/dynamic-plugin-sdk'; import { sortList } from '@console/internal/actions/ui'; import { getFilteredRows } from '@console/internal/components/factory/table-data-hook'; @@ -95,9 +97,26 @@ export const MonitoringAlerts: React.FC = ({ match, rules, alerts, filter }, [filters, listSorts, columnIndex, rules, listOrderBy, monitoringAlertColumn, sortOrder]); React.useEffect(() => { - const tableRows = monitoringAlertRows(filteredRules, collapsedRowsIds, namespace); + // TODO: This works around a bug where the rule's state is never set to silenced in Redux + const newRules = _.cloneDeep(filteredRules); + if (newRules) { + const alertRules = alerts?.data?.map((alert) => alert.rule) ?? []; + const silencedAlertRules = alertRules.filter((rule) => rule.state === RuleStates.Silenced); + silencedAlertRules.forEach((alertRule) => { + newRules.forEach((rule) => { + if (rule.id === alertRule.id) { + rule.state = RuleStates.Silenced; + rule.alerts.forEach((alert) => { + alert.state = AlertStates.Silenced; + }); + } + }); + }); + } + + const tableRows = monitoringAlertRows(newRules, collapsedRowsIds, namespace); setRows(tableRows); - }, [collapsedRowsIds, filteredRules, namespace]); + }, [alerts?.data, collapsedRowsIds, filteredRules, namespace]); const onCollapse = React.useCallback( (event: React.MouseEvent, rowKey: number, isOpen: boolean) => { @@ -132,7 +151,7 @@ export const MonitoringAlerts: React.FC = ({ match, rules, alerts, filter if (!alerts?.loaded && !alerts?.loadError) { return ; } - if (rules.length === 0) { + if (rules?.length === 0) { return ; } return ( diff --git a/frontend/packages/dev-console/src/components/monitoring/alerts/SilenceAlert.tsx b/frontend/packages/dev-console/src/components/monitoring/alerts/SilenceAlert.tsx index da58c20d778..99b6e44db1d 100644 --- a/frontend/packages/dev-console/src/components/monitoring/alerts/SilenceAlert.tsx +++ b/frontend/packages/dev-console/src/components/monitoring/alerts/SilenceAlert.tsx @@ -24,16 +24,11 @@ type SilenceAlertProps = { namespace: string; }; -const SilenceUntil = ({ rule }) => { - if (!_.isEmpty(rule.silencedBy)) { - return ( -
e.preventDefault()} role="presentation"> - -
- ); - } - return null; -}; +const SilenceUntil = ({ rule }) => ( +
e.preventDefault()} role="presentation"> + +
+); const SilenceAlert: React.FC = ({ rule, namespace }) => { const [isChecked, setIsChecked] = React.useState(true); @@ -41,10 +36,8 @@ const SilenceAlert: React.FC = ({ rule, namespace }) => { const dispatch = useDispatch(); React.useEffect(() => { - if (rule.state === RuleStates.Silenced) { - setIsChecked(false); - } - }, [rule]); + setIsChecked(rule.state !== RuleStates.Silenced); + }, [rule.state]); const handleChange = (checked: boolean) => { setIsChecked(checked); diff --git a/frontend/packages/dev-console/src/components/monitoring/alerts/useRuleAlertsPoller.tsx b/frontend/packages/dev-console/src/components/monitoring/alerts/useRuleAlertsPoller.tsx index d085be0fc39..a813b96cbb0 100644 --- a/frontend/packages/dev-console/src/components/monitoring/alerts/useRuleAlertsPoller.tsx +++ b/frontend/packages/dev-console/src/components/monitoring/alerts/useRuleAlertsPoller.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import * as _ from 'lodash'; import { Dispatch } from 'redux'; -import { PrometheusRulesResponse } from '@console/dynamic-plugin-sdk'; +import { PrometheusRulesResponse, useActivePerspective } from '@console/dynamic-plugin-sdk'; import { alertingErrored, alertingLoaded, @@ -26,6 +26,7 @@ export const useRulesAlertsPoller = ( getAlertingRules: (namespace?: string) => Promise; }[], ) => { + const [perspective] = useActivePerspective(); React.useEffect(() => { const url = getPrometheusURL({ endpoint: PrometheusEndpoint.RULES, @@ -36,7 +37,10 @@ export const useRulesAlertsPoller = ( const poller = (): void => { fetchAlerts(url, alertsSource, namespace) .then(({ data }) => { - const { alerts: fetchedAlerts, rules: fetchedRules } = getAlertsAndRules(data); + const { alerts: fetchedAlerts, rules: fetchedRules } = getAlertsAndRules( + data, + perspective, + ); const sortThanosRules = _.sortBy(fetchedRules, alertingRuleStateOrder); dispatch(alertingSetRules('devRules', sortThanosRules, 'dev')); dispatch(alertingLoaded('devAlerts', fetchedAlerts, 'dev')); @@ -57,5 +61,5 @@ export const useRulesAlertsPoller = ( clearTimeout(pollerTimeout); } }; - }, [namespace, alertsSource, dispatch]); + }, [namespace, alertsSource, dispatch, perspective]); }; diff --git a/frontend/packages/dev-console/src/components/project-access/ProjectAccess.tsx b/frontend/packages/dev-console/src/components/project-access/ProjectAccess.tsx index 6c5098e1e9d..ee523a278c4 100644 --- a/frontend/packages/dev-console/src/components/project-access/ProjectAccess.tsx +++ b/frontend/packages/dev-console/src/components/project-access/ProjectAccess.tsx @@ -9,6 +9,7 @@ import { ExternalLink, getDocumentationURL, history, + isManaged, LoadingBox, PageHeading, StatusBox, @@ -123,10 +124,15 @@ const ProjectAccess: React.FC = ({ "Project access allows you to add or remove a user's access to the project. More advanced management of role-based access control appear in " } Roles and{' '} - Role Bindings. For - more information, see the{' '} - role-based access control documentation. + Role Bindings. + {!isManaged() && ( + + {' '} + For more information, see the{' '} + role-based access control documentation. + + )} {roleBindings.loadError ? ( diff --git a/frontend/packages/dev-console/src/components/project-access/project-access-form-submit-utils.ts b/frontend/packages/dev-console/src/components/project-access/project-access-form-submit-utils.ts index f95b8d1d930..8b3e36d2f65 100644 --- a/frontend/packages/dev-console/src/components/project-access/project-access-form-submit-utils.ts +++ b/frontend/packages/dev-console/src/components/project-access/project-access-form-submit-utils.ts @@ -147,7 +147,9 @@ export const sendRoleBindingRequest = ( ? [ { ...(user.subject.kind === 'ServiceAccount' - ? { namespace: user.subject.namespace } + ? user.subject.namespace + ? { namespace: user.subject.namespace } + : {} : { apiGroup: 'rbac.authorization.k8s.io' }), kind: user.subject.kind, name: user.subject.name, diff --git a/frontend/packages/dev-console/src/components/project-access/project-access-form-validation-utils.ts b/frontend/packages/dev-console/src/components/project-access/project-access-form-validation-utils.ts index 46c7e15c48f..bbe3e2af47b 100644 --- a/frontend/packages/dev-console/src/components/project-access/project-access-form-validation-utils.ts +++ b/frontend/packages/dev-console/src/components/project-access/project-access-form-validation-utils.ts @@ -7,10 +7,6 @@ export const validationSchema = yup.object().shape({ subject: yup.object().shape({ name: yup.string().required(i18n.t('devconsole~Required')), kind: yup.string().required(i18n.t('devconsole~Required')), - namespace: yup.string().when('kind', { - is: 'ServiceAccount', - then: yup.string().required(i18n.t('devconsole~Required')), - }), }), role: yup.string().required(i18n.t('devconsole~Required')), }), diff --git a/frontend/packages/dev-console/src/const.ts b/frontend/packages/dev-console/src/const.ts index 61dfbb1d917..73a23fb5388 100644 --- a/frontend/packages/dev-console/src/const.ts +++ b/frontend/packages/dev-console/src/const.ts @@ -37,3 +37,5 @@ export const ADD_TO_PROJECT = 'add-to-project'; export const FLAG_JAVA_IMAGE_STREAM_ENABLED = 'JAVA_IMAGE_STREAM_ENABLED'; export const IMAGESTREAM_NAMESPACE = 'openshift'; export const JAVA_IMAGESTREAM_NAME = 'java'; + +export const FLAG_OPENSHIFT_DEPLOYMENTCONFIG = 'OPENSHIFT_DEPLOYMENTCONFIG'; diff --git a/frontend/packages/dev-console/src/utils/__tests__/resource-label-utils-data.ts b/frontend/packages/dev-console/src/utils/__tests__/resource-label-utils-data.ts index 8d2bae30b05..ef1cbcd1265 100644 --- a/frontend/packages/dev-console/src/utils/__tests__/resource-label-utils-data.ts +++ b/frontend/packages/dev-console/src/utils/__tests__/resource-label-utils-data.ts @@ -7,7 +7,7 @@ export const originalDeployment = { 'app.openshift.io/vcs-ref': 'master', 'app.openshift.io/vcs-uri': 'https://github.com/divyanshiGupta/nationalparks-py', 'image.openshift.io/triggers': - '[{"from":{"kind":"ImageStreamTag","name":"nationalparks-py:latest","namespace":"div"},"fieldPath":"spec.template.spec.containers[?(@.name==\\"nationalparks-py\\")].image","pause":"false"}]', + '[{"from":{"kind":"ImageStreamTag","name":"nationalparks-py:latest","namespace":"div"},"fieldPath":"spec.template.spec.containers[?(@.name==\\"nationalparks-py\\")].image","paused":"false"}]', 'openshift.io/generated-by': 'OpenShiftWebConsole', 'app.openshift.io/connects-to': 'database', 'deployment.kubernetes.io/revision': '4', @@ -104,7 +104,7 @@ export const newDeployment = { 'app.openshift.io/vcs-ref': 'master', 'app.openshift.io/vcs-uri': 'https://github.com/divyanshiGupta/nationalparks-py', 'image.openshift.io/triggers': - '[{"from":{"kind":"ImageStreamTag","name":"nationalparks-py:latest","namespace":"div"},"fieldPath":"spec.template.spec.containers[?(@.name==\\"nationalparks-py\\")].image","pause":"true"}]', + '[{"from":{"kind":"ImageStreamTag","name":"nationalparks-py:latest","namespace":"div"},"fieldPath":"spec.template.spec.containers[?(@.name==\\"nationalparks-py\\")].image","paused":"true"}]', 'openshift.io/generated-by': 'OpenShiftWebConsole', }, name: 'nationalparks-py', @@ -169,7 +169,7 @@ export const devfileDeployment = { 'app.openshift.io/vcs-ref': 'master', 'app.openshift.io/vcs-uri': 'https://github.com/divyanshiGupta/nationalparks-py', 'image.openshift.io/triggers': - '[{"from":{"kind":"ImageStreamTag","name":"nationalparks-py:latest","namespace":"div"},"fieldPath":"spec.template.spec.containers[?(@.name==\\"nationalparks-py\\")].image","pause":"true"}]', + '[{"from":{"kind":"ImageStreamTag","name":"nationalparks-py:latest","namespace":"div"},"fieldPath":"spec.template.spec.containers[?(@.name==\\"nationalparks-py\\")].image","paused":"true"}]', 'openshift.io/generated-by': 'OpenShiftWebConsole', isFromDevfile: 'true', }, diff --git a/frontend/packages/dev-console/src/utils/__tests__/resource-label-utils.spec.tsx b/frontend/packages/dev-console/src/utils/__tests__/resource-label-utils.spec.tsx index 80ccb3e853d..4fb8f5266b6 100644 --- a/frontend/packages/dev-console/src/utils/__tests__/resource-label-utils.spec.tsx +++ b/frontend/packages/dev-console/src/utils/__tests__/resource-label-utils.spec.tsx @@ -36,7 +36,7 @@ describe('resource-label-utils', () => { 'app.openshift.io/vcs-ref': 'master', 'app.openshift.io/vcs-uri': 'https://github.com/divyanshiGupta/nationalparks-py', 'image.openshift.io/triggers': - '[{"from":{"kind":"ImageStreamTag","name":"nationalparks-py:latest","namespace":"div"},"fieldPath":"spec.template.spec.containers[?(@.name==\\"nationalparks-py\\")].image","pause":"true"}]', + '[{"from":{"kind":"ImageStreamTag","name":"nationalparks-py:latest","namespace":"div"},"fieldPath":"spec.template.spec.containers[?(@.name==\\"nationalparks-py\\")].image","paused":"true"}]', 'openshift.io/generated-by': 'OpenShiftWebConsole', 'app.openshift.io/connects-to': 'database', 'deployment.kubernetes.io/revision': '4', @@ -125,12 +125,12 @@ describe('resource-label-utils', () => { let annotation = getTriggerAnnotation('test', 'python', 'div', true); expect(annotation).toEqual({ 'image.openshift.io/triggers': - '[{"from":{"kind":"ImageStreamTag","name":"python:latest","namespace":"div"},"fieldPath":"spec.template.spec.containers[?(@.name==\\"test\\")].image","pause":"false"}]', + '[{"from":{"kind":"ImageStreamTag","name":"python:latest","namespace":"div"},"fieldPath":"spec.template.spec.containers[?(@.name==\\"test\\")].image","paused":"false"}]', }); annotation = getTriggerAnnotation('test', 'test', 'div', false); expect(annotation).toEqual({ 'image.openshift.io/triggers': - '[{"from":{"kind":"ImageStreamTag","name":"test:latest","namespace":"div"},"fieldPath":"spec.template.spec.containers[?(@.name==\\"test\\")].image","pause":"true"}]', + '[{"from":{"kind":"ImageStreamTag","name":"test:latest","namespace":"div"},"fieldPath":"spec.template.spec.containers[?(@.name==\\"test\\")].image","paused":"true"}]', }); }); }); diff --git a/frontend/packages/dev-console/src/utils/resource-label-utils.ts b/frontend/packages/dev-console/src/utils/resource-label-utils.ts index 80f1739b7bd..04b7416ad20 100644 --- a/frontend/packages/dev-console/src/utils/resource-label-utils.ts +++ b/frontend/packages/dev-console/src/utils/resource-label-utils.ts @@ -75,7 +75,7 @@ export const getTriggerAnnotation = ( { from: { kind: 'ImageStreamTag', name: `${imageName}:${imageTag}`, namespace: imageNamespace }, fieldPath: `spec.template.spec.containers[?(@.name=="${containerName}")].image`, - pause: `${!imageTrigger}`, + paused: `${!imageTrigger}`, }, ]), }); diff --git a/frontend/packages/git-service/src/services/bitbucket-service.ts b/frontend/packages/git-service/src/services/bitbucket-service.ts index 34ee54dcf9c..2527c655374 100644 --- a/frontend/packages/git-service/src/services/bitbucket-service.ts +++ b/frontend/packages/git-service/src/services/bitbucket-service.ts @@ -2,12 +2,7 @@ import { Base64 } from 'js-base64'; import * as ParseBitbucketUrl from 'parse-bitbucket-url'; import 'whatwg-fetch'; import { consoleFetchJSON } from '@console/dynamic-plugin-sdk/src/lib-core'; -import { - API_PROXY_URL, - ProxyResponse, - consoleProxyFetchJSON, - convertHeaders, -} from '@console/shared/src/utils/proxy'; +import { DevConsoleEndpointResponse } from '@console/shared/src'; import { GitSource, SecretType, @@ -19,6 +14,24 @@ import { } from '../types'; import { BaseService } from './base-service'; +type BBWebhookBody = { + url: string; + events: string[]; + skip_cert_verification: boolean; + active: boolean; +}; + +type BitbucketWebhookRequest = { + headers: Headers; + isServer: boolean; + baseURL: string; + owner: string; + repoName: string; + body: BBWebhookBody; +}; + +export const BITBUCKET_WEBHOOK_BACKEND_URL = '/api/dev-console/webhooks/bitbucket'; + export class BitbucketService extends BaseService { private readonly metadata: RepoMetadata; @@ -61,15 +74,6 @@ export class BitbucketService extends BaseService { ...headers, }; - if (this.isServer) { - return consoleProxyFetchJSON({ - url, - method: requestMethod || 'GET', - headers: convertHeaders(requestHeaders), - ...(body && { body: JSON.stringify(body) }), - }); - } - const response = await fetch(url, { method: requestMethod || 'GET', headers: requestHeaders, @@ -183,28 +187,33 @@ export class BitbucketService extends BaseService { webhookURL: string, sslVerification: boolean, ): Promise => { - const headers = { - 'Content-Type': ['application/json'], - Authorization: [`Basic ${token}`], - }; - const body = { + const headers = new Headers({ + 'Content-Type': 'application/json', + Authorization: `Basic ${token}`, + }); + const body: BBWebhookBody = { url: webhookURL, events: ['repo:push', 'pullrequest:created', 'pullrequest:updated'], skip_cert_verification: !sslVerification, active: true, }; - const url = this.isServer - ? `${this.baseURL}/projects/${this.metadata.owner}/repos/${this.metadata.repoName}/hooks` - : `${this.baseURL}/repositories/${this.metadata.owner}/${this.metadata.repoName}/hooks`; - /* Using DevConsole Proxy to create webhook as Bitbucket is giving CORS error */ - const webhookResponse: ProxyResponse = await consoleFetchJSON.post(API_PROXY_URL, { - url, - method: 'POST', + const webhookRequestBody: BitbucketWebhookRequest = { headers, - body: JSON.stringify(body), - }); + isServer: this.isServer, + baseURL: this.baseURL, + owner: this.metadata.owner, + repoName: this.metadata.repoName, + body, + }; + const webhookResponse: DevConsoleEndpointResponse = await consoleFetchJSON.post( + BITBUCKET_WEBHOOK_BACKEND_URL, + webhookRequestBody, + ); + if (!webhookResponse.statusCode) { + throw new Error('Unexpected proxy response: Status code is missing!'); + } return webhookResponse.statusCode === 201; }; diff --git a/frontend/packages/git-service/src/services/github-service.ts b/frontend/packages/git-service/src/services/github-service.ts index 14336579f44..d59deb61f91 100644 --- a/frontend/packages/git-service/src/services/github-service.ts +++ b/frontend/packages/git-service/src/services/github-service.ts @@ -2,7 +2,7 @@ import * as Octokit from '@octokit/rest'; import * as GitUrlParse from 'git-url-parse'; import { Base64 } from 'js-base64'; import { consoleFetchJSON } from '@console/dynamic-plugin-sdk/src/lib-core'; -import { API_PROXY_URL, ProxyResponse } from '@console/shared/src/utils/proxy'; +import { DevConsoleEndpointResponse } from '@console/shared/src'; import { GitSource, SecretType, @@ -14,6 +14,27 @@ import { } from '../types'; import { BaseService } from './base-service'; +type GHWebhookBody = { + name: string; + active: boolean; + config: { + url: string; + content_type: string; + insecure_ssl: string; + secret: string; + }; + events: string[]; +}; + +type GithubWebhookRequest = { + headers: Headers; + hostName: string; + owner: string; + repoName: string; + body: GHWebhookBody; +}; + +export const GITHUB_WEBHOOK_BACKEND_URL = '/api/dev-console/webhooks/github'; export class GithubService extends BaseService { private readonly client: Octokit; @@ -138,12 +159,12 @@ export class GithubService extends BaseService { sslVerification: boolean, webhookSecret: string, ): Promise => { - const headers = { - Accept: ['application/vnd.github+json'], - Authorization: [`Bearer ${token}`], - 'X-GitHub-Api-Version': ['2022-11-28'], - }; - const body = { + const headers = new Headers({ + Accept: 'application/vnd.github+json', + Authorization: `Bearer ${token}`, + 'X-GitHub-Api-Version': '2022-11-28', + }); + const body: GHWebhookBody = { name: 'web', active: true, config: { @@ -158,13 +179,22 @@ export class GithubService extends BaseService { this.metadata.host === 'github.com' ? `https://api.github.com` : `https://${this.metadata.host}/api/v3`; - /* Using DevConsole Proxy to create webhook as Octokit is giving CORS error */ - const webhookResponse: ProxyResponse = await consoleFetchJSON.post(API_PROXY_URL, { - url: `${AddWebhookBaseURL}/repos/${this.metadata.owner}/${this.metadata.repoName}/hooks`, - method: 'POST', + + const webhookRequestBody: GithubWebhookRequest = { headers, - body: JSON.stringify(body), - }); + hostName: AddWebhookBaseURL, + owner: this.metadata.owner, + repoName: this.metadata.repoName, + body, + }; + + const webhookResponse: DevConsoleEndpointResponse = await consoleFetchJSON.post( + GITHUB_WEBHOOK_BACKEND_URL, + webhookRequestBody, + ); + if (!webhookResponse.statusCode) { + throw new Error('Unexpected proxy response: Status code is missing!'); + } return webhookResponse.statusCode === 201; }; diff --git a/frontend/packages/git-service/src/services/gitlab-service.ts b/frontend/packages/git-service/src/services/gitlab-service.ts index 557540b2f78..26acf44c63c 100644 --- a/frontend/packages/git-service/src/services/gitlab-service.ts +++ b/frontend/packages/git-service/src/services/gitlab-service.ts @@ -3,7 +3,7 @@ import { Gitlab } from 'gitlab'; import i18n from 'i18next'; import { Base64 } from 'js-base64'; import { consoleFetchJSON } from '@console/dynamic-plugin-sdk/src/lib-core'; -import { API_PROXY_URL, ProxyResponse } from '@console/shared/src/utils/proxy'; +import { DevConsoleEndpointResponse } from '@console/shared/src'; import { GitSource, SecretType, @@ -20,6 +20,23 @@ type GitlabRepo = { path_with_namespace: string; }; +type GLWebhookBody = { + url: string; + push_events: boolean; + merge_requests_events: boolean; + enable_ssl_verification: boolean; + token: string; +}; + +type GitlabWebhookRequest = { + headers: Headers; + hostName: string; + projectID: string; + body: GLWebhookBody; +}; + +export const GITLAB_WEBHOOK_BACKEND_URL = '/api/dev-console/webhooks/gitlab'; + const removeLeadingSlash = (str: string) => str?.replace(/^\//, '') || ''; export class GitlabService extends BaseService { @@ -171,25 +188,32 @@ export class GitlabService extends BaseService { webhookSecret: string, ): Promise => { const projectID = await this.getProjectId(); - const headers = { - 'Content-Type': ['application/json'], - 'PRIVATE-TOKEN': [token], - }; - const body = { + const headers = new Headers({ + 'Content-Type': 'application/json', + 'PRIVATE-TOKEN': token, + }); + const body: GLWebhookBody = { url: webhookURL, push_events: true, merge_requests_events: true, enable_ssl_verification: sslVerification, token: webhookSecret, }; - /* Using DevConsole Proxy to create webhook as Gitlab is giving CORS error */ - const webhookResponse: ProxyResponse = await consoleFetchJSON.post(API_PROXY_URL, { - url: `${this.metadata.host}/api/v4/projects/${projectID}/hooks`, - method: 'POST', + + const webhookRequestBody: GitlabWebhookRequest = { headers, - body: JSON.stringify(body), - }); + hostName: this.metadata.host, + projectID: projectID.toString(), + body, + }; + const webhookResponse: DevConsoleEndpointResponse = await consoleFetchJSON.post( + GITLAB_WEBHOOK_BACKEND_URL, + webhookRequestBody, + ); + if (!webhookResponse.statusCode) { + throw new Error('Unexpected proxy response: Status code is missing!'); + } return webhookResponse.statusCode === 201; }; @@ -208,7 +232,8 @@ export class GitlabService extends BaseService { try { const projectID = await this.getProjectId(); const ref = this.metadata.defaultBranch || (this.repo as any)?.default_branch; - return await this.client.RepositoryFiles.showRaw(projectID, path, ref); + const filePath = path.replace(/^\/+/, ''); + return await this.client.RepositoryFiles.showRaw(projectID, filePath, ref); } catch (e) { return null; } diff --git a/frontend/packages/git-service/src/utils/__tests__/import-strategy-detector.spec.ts b/frontend/packages/git-service/src/utils/__tests__/import-strategy-detector.spec.ts index c03c22e0b4f..29caab30194 100644 --- a/frontend/packages/git-service/src/utils/__tests__/import-strategy-detector.spec.ts +++ b/frontend/packages/git-service/src/utils/__tests__/import-strategy-detector.spec.ts @@ -30,6 +30,23 @@ describe('Import strategy detection', () => { expect(types[0].detectedFiles).toEqual(['Dockerfile', 'Dockerfile.build']); }); + it('should detect containerfile strategy', async () => { + const files = ['src', 'Containerfile', 'Containerfile.build']; + const mockGitService: any = { + getRepoFileList: jest.fn(() => Promise.resolve({ files })), + getPackageJsonContent: jest.fn(), + isRepoReachable: jest.fn(() => Promise.resolve(RepoStatus.Reachable)), + }; + mockIsServerlessFxRepository.mockReturnValue(Promise.resolve(false)); + const data = await detectImportStrategies( + 'https://github.com/divyanshiGupta/bus.git', + mockGitService, + ); + const types = data.strategies; + expect(types[0].type).toEqual(ImportStrategy.DOCKERFILE); + expect(types[0].detectedFiles).toEqual(['Containerfile', 'Containerfile.build']); + }); + it('should detect devfile strategy', async () => { const files = ['src', 'devfile.yaml']; const mockGitService: any = { diff --git a/frontend/packages/git-service/src/utils/import-strategy-detector.ts b/frontend/packages/git-service/src/utils/import-strategy-detector.ts index 4a4a05b75e0..f6b7c462a3c 100644 --- a/frontend/packages/git-service/src/utils/import-strategy-detector.ts +++ b/frontend/packages/git-service/src/utils/import-strategy-detector.ts @@ -22,7 +22,7 @@ const ImportStrategyList: ImportStrategyType[] = [ { name: 'Dockerfile', type: ImportStrategy.DOCKERFILE, - expectedRegexp: /^Dockerfile.*/, + expectedRegexp: /^(Dockerfile|Containerfile).*/, priority: 2, }, { diff --git a/frontend/packages/helm-plugin/src/catalog/utils/catalog-utils.tsx b/frontend/packages/helm-plugin/src/catalog/utils/catalog-utils.tsx index ab59a7ba08a..aab7545fb57 100644 --- a/frontend/packages/helm-plugin/src/catalog/utils/catalog-utils.tsx +++ b/frontend/packages/helm-plugin/src/catalog/utils/catalog-utils.tsx @@ -173,8 +173,9 @@ export const normalizeHelmCharts = ( // group Helm chart with same name and different version together const existingChartIndex = normalizedCharts.findIndex((currentChart) => { return ( - currentChart.attributes?.name === name && - currentChart.attributes?.chartRepositoryTitle === chartRepositoryTitle + (currentChart.attributes?.name === name && + currentChart.attributes?.chartRepositoryTitle === chartRepositoryTitle) || + (currentChart.name === annotatedName && currentChart.provider === providerName) ); }); diff --git a/frontend/packages/helm-plugin/src/components/forms/install-upgrade/HelmChartVersionDropdown.tsx b/frontend/packages/helm-plugin/src/components/forms/install-upgrade/HelmChartVersionDropdown.tsx index dceaebcb5da..1c370aa8fc6 100644 --- a/frontend/packages/helm-plugin/src/components/forms/install-upgrade/HelmChartVersionDropdown.tsx +++ b/frontend/packages/helm-plugin/src/components/forms/install-upgrade/HelmChartVersionDropdown.tsx @@ -37,6 +37,8 @@ export type HelmChartVersionDropdownProps = { onVersionChange: (chart: HelmChart) => void; namespace: string; chartIndexEntry: string; + annotatedName?: string; + providerName?: string; }; type ModalCallback = () => void; @@ -47,6 +49,8 @@ const HelmChartVersionDropdown: React.FunctionComponent { const { t } = useTranslation(); const { @@ -127,6 +131,8 @@ const HelmChartVersionDropdown: React.FunctionComponent { ignore = true; }; - }, [chartName, chartRepoName, chartRepositories, t, namespace, chartIndexEntry, setFieldValue]); + }, [ + chartName, + chartRepoName, + chartRepositories, + t, + namespace, + chartIndexEntry, + setFieldValue, + annotatedName, + providerName, + ]); const onChartVersionChange = (value: string) => { const [version, repoName] = value.split('--'); diff --git a/frontend/packages/helm-plugin/src/components/forms/install-upgrade/HelmInstallUpgradeForm.tsx b/frontend/packages/helm-plugin/src/components/forms/install-upgrade/HelmInstallUpgradeForm.tsx index b161193a8ec..e4f792acad3 100644 --- a/frontend/packages/helm-plugin/src/components/forms/install-upgrade/HelmInstallUpgradeForm.tsx +++ b/frontend/packages/helm-plugin/src/components/forms/install-upgrade/HelmInstallUpgradeForm.tsx @@ -45,6 +45,8 @@ export interface HelmInstallUpgradeFormProps { chartError: Error; namespace: string; chartIndexEntry?: string; + annotatedName?: string; + providerName?: string; } const HelmInstallUpgradeForm: React.FC< @@ -64,6 +66,8 @@ const HelmInstallUpgradeForm: React.FC< chartError, namespace, chartIndexEntry, + annotatedName, + providerName, }) => { const { t } = useTranslation(); const { chartName, chartVersion, chartReadme, formData, formSchema, editorType } = values; @@ -158,6 +162,8 @@ const HelmInstallUpgradeForm: React.FC< onVersionChange={onVersionChange} namespace={namespace} chartIndexEntry={chartIndexEntry} + annotatedName={annotatedName} + providerName={providerName} /> diff --git a/frontend/packages/helm-plugin/src/components/forms/install-upgrade/HelmInstallUpgradePage.tsx b/frontend/packages/helm-plugin/src/components/forms/install-upgrade/HelmInstallUpgradePage.tsx index 53f179f45b8..f2ffd16f6c5 100644 --- a/frontend/packages/helm-plugin/src/components/forms/install-upgrade/HelmInstallUpgradePage.tsx +++ b/frontend/packages/helm-plugin/src/components/forms/install-upgrade/HelmInstallUpgradePage.tsx @@ -13,6 +13,7 @@ import { coFetchJSON } from '@console/internal/co-fetch'; import { history, LoadingBox } from '@console/internal/components/utils'; import { prune } from '@console/shared/src/components/dynamic-form/utils'; import { EditorType } from '@console/shared/src/components/synced-editor/editor-toggle'; +import { CHART_NAME_ANNOTATION, PROVIDER_NAME_ANNOTATION } from '../../../catalog/utils/const'; import { HelmActionType, HelmChart, @@ -214,6 +215,8 @@ const HelmInstallUpgradePage: React.FunctionComponent; } + const annotatedName = chartData?.metadata?.annotations?.[CHART_NAME_ANNOTATION] ?? ''; + const providerName = chartData?.metadata?.annotations?.[PROVIDER_NAME_ANNOTATION] ?? ''; const chartMetaDescription = ; @@ -238,6 +241,8 @@ const HelmInstallUpgradePage: React.FunctionComponent )} diff --git a/frontend/packages/helm-plugin/src/utils/helm-utils.ts b/frontend/packages/helm-plugin/src/utils/helm-utils.ts index 04076eed33e..6e9f7f58943 100644 --- a/frontend/packages/helm-plugin/src/utils/helm-utils.ts +++ b/frontend/packages/helm-plugin/src/utils/helm-utils.ts @@ -7,6 +7,7 @@ import { Flatten } from '@console/internal/components/factory/list-page'; import { RowFilter } from '@console/internal/components/filter-toolbar'; import { K8sResourceKind, modelFor, referenceFor } from '@console/internal/module/k8s'; import { toTitleCase, WORKLOAD_TYPES } from '@console/shared'; +import { CHART_NAME_ANNOTATION, PROVIDER_NAME_ANNOTATION } from '../catalog/utils/const'; import { HelmRelease, HelmChart, @@ -139,6 +140,8 @@ export const getChartEntriesByName = ( chartName: string, chartRepoName?: string, chartRepositories?: K8sResourceKind[], + annotatedName?: string, + providerName?: string, ): HelmChartMetaData[] => { if (chartName && chartRepoName) { const chartRepositoryTitle = getChartRepositoryTitle(chartRepositories, chartRepoName); @@ -156,7 +159,13 @@ export const getChartEntriesByName = ( const repoName = key.split('--').pop(); const chartRepositoryTitle = getChartRepositoryTitle(chartRepositories, repoName); charts.forEach((chart: HelmChartMetaData) => { - if (chart.name === chartName) { + if ( + chart.name === chartName || + (annotatedName && + providerName && + chart?.annotations?.[CHART_NAME_ANNOTATION] === annotatedName && + chart?.annotations?.[PROVIDER_NAME_ANNOTATION] === providerName) + ) { acc.push({ ...chart, repoName: chartRepositoryTitle }); } }); diff --git a/frontend/packages/insights-plugin/src/components/InsightsPopup/index.tsx b/frontend/packages/insights-plugin/src/components/InsightsPopup/index.tsx index 59875d922d2..b17c1763cf7 100644 --- a/frontend/packages/insights-plugin/src/components/InsightsPopup/index.tsx +++ b/frontend/packages/insights-plugin/src/components/InsightsPopup/index.tsx @@ -9,6 +9,7 @@ import { ExternalLink, getDocumentationURL, Timestamp, + isManaged, } from '@console/internal/components/utils'; import { K8sResourceKind } from '@console/internal/module/k8s'; import { PrometheusHealthPopupProps } from '@console/plugin-sdk'; @@ -163,7 +164,7 @@ export const InsightsPopup: React.FC = ({ responses, )} )} - {(waiting || disabled || error) && ( + {(waiting || disabled || error) && !isManaged() && ( )} diff --git a/frontend/packages/integration-tests-cypress/support/admin.ts b/frontend/packages/integration-tests-cypress/support/admin.ts new file mode 100644 index 00000000000..8a2a1d8ba61 --- /dev/null +++ b/frontend/packages/integration-tests-cypress/support/admin.ts @@ -0,0 +1,21 @@ +import { nav } from '../views/nav'; + +declare global { + namespace Cypress { + interface Chainable { + initAdmin(): Chainable; + initDeveloper(): Chainable; + } + } +} + +// any command added below, must be added to global Cypress interface above + +Cypress.Commands.add('initAdmin', () => { + cy.log('redirect to home'); + cy.visit('/'); + cy.byTestID('loading-indicator').should('not.exist'); + cy.log('ensure perspective switcher is set to Administrator'); + nav.sidenav.switcher.changePerspectiveTo('Administrator'); + nav.sidenav.switcher.shouldHaveText('Administrator'); +}); diff --git a/frontend/packages/integration-tests-cypress/support/index.ts b/frontend/packages/integration-tests-cypress/support/index.ts index 0d7bf4473a2..ebbb5297aa4 100644 --- a/frontend/packages/integration-tests-cypress/support/index.ts +++ b/frontend/packages/integration-tests-cypress/support/index.ts @@ -5,6 +5,7 @@ import './nav'; import './resources'; import './i18n'; import { a11yTestResults } from './a11y'; +import './admin'; Cypress.Cookies.debug(true); diff --git a/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/alertmanager.spec.ts b/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/alertmanager.spec.ts new file mode 100644 index 00000000000..1c5c35cca86 --- /dev/null +++ b/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/alertmanager.spec.ts @@ -0,0 +1,203 @@ +import { safeLoad } from 'js-yaml'; +import * as _ from 'lodash'; +import { + AlertmanagerConfig, + AlertmanagerRoute, +} from '@console/internal/components/monitoring/alertmanager/alertmanager-config'; +import { checkErrors, testName } from '../../../support'; +import { alertmanager } from '../../../views/alertmanager'; +import { detailsPage } from '../../../views/details-page'; +import { listPage } from '../../../views/list-page'; +import { modal } from '../../../views/modal'; +import { nav } from '../../../views/nav'; +import * as yamlEditor from '../../../views/yaml-editor'; + +describe('Alertmanager', () => { + before(() => { + cy.login(); + }); + + beforeEach(() => { + cy.initAdmin(); + }); + + afterEach(() => { + checkErrors(); + }); + + after(() => { + alertmanager.reset(); + }); + + it('displays the Alertmanager Configuration Details page', () => { + nav.sidenav.clickNavLink(['Administration', 'Cluster Settings']); + detailsPage.selectTab('Configuration'); + cy.byLegacyTestID('Alertmanager').click(); + cy.byTestSectionHeading('Alert routing').should('exist'); + }); + + it('launches Alert Routing modal, edits and saves correctly', () => { + alertmanager.visitAlertmanagerPage(); + cy.byTestID('edit-alert-routing-btn').click(); + cy.byLegacyTestID('input-group-by').type(', cluster'); + cy.byLegacyTestID('input-group-wait').clear(); + cy.byLegacyTestID('input-group-wait').type('60s'); + cy.byLegacyTestID('input-group-interval').clear(); + cy.byLegacyTestID('input-group-interval').type('10m'); + cy.byLegacyTestID('input-repeat-interval').clear(); + cy.byLegacyTestID('input-repeat-interval').type('24h'); + cy.byTestID('confirm-action').click(); + modal.shouldBeClosed(); + cy.byLegacyTestID('group_by_value').should('contain', ', cluster'); + cy.byLegacyTestID('group_wait_value').should('contain', '60s'); + cy.byLegacyTestID('group_interval_value').should('contain', '10m'); + cy.byLegacyTestID('repeat_interval_value').should('contain', '24h'); + }); + + it('displays the Alertmanager YAML page and saves Alertmanager YAML', () => { + alertmanager.visitAlertmanagerPage(); + detailsPage.selectTab('yaml'); + yamlEditor.isLoaded(); + cy.get('.yaml-editor__buttons .pf-m-success').should('not.exist'); + yamlEditor.clickSaveCreateButton(); + cy.get('.yaml-editor__buttons .pf-m-success').should('exist'); + }); + + it('creates and deletes a receiver', () => { + cy.log('create Webhook Receiver'); + const receiverName = `WebhookReceiver-${testName}`; + const receiverType = 'webhook'; + const configName = `${receiverType}_configs`; + const label = 'severity = warning'; + const webhookURL = 'http://mywebhookurl'; + alertmanager.createReceiver(receiverName, configName); + alertmanager.showAdvancedConfiguration(); + cy.byLegacyTestID('send-resolved-alerts').should('be.checked'); + cy.byLegacyTestID('webhook-url').type(webhookURL); + cy.byLegacyTestID('label-0').type(label); + alertmanager.save(); + alertmanager.validateCreation(receiverName, receiverType, label); + listPage.rows.clickKebabAction(receiverName, 'Delete Receiver'); + modal.submit(); + modal.shouldBeClosed(); + listPage.rows.shouldNotExist(receiverName); + }); + + it('prevents deletion and form edit of a receiver with sub-route', () => { + const yaml = `route: + routes: + - match: + service: database + receiver: team-DB-pager + routes: + - match: + owner: team-X + receiver: team-X-pager +receivers: +- name: 'team-X-pager' +- name: 'team-DB-pager'`; + alertmanager.visitAlertmanagerPage(); + alertmanager.visitYAMLPage(); + yamlEditor.setEditorContent(yaml); + yamlEditor.clickSaveCreateButton(); + cy.get('.yaml-editor__buttons .pf-m-success').should('exist'); + detailsPage.selectTab('details'); + cy.get(`[data-test-rows="resource-row"]`) + .contains('team-X-pager') + .parents('tr') + .within(() => { + cy.get('[data-test-id="kebab-button"]').click(); + }); + cy.get('[data-test-action="Delete Receiver"]').should('be.disabled'); + alertmanager.reset(); + }); + + it('converts existing match and match_re routing labels to matchers', () => { + const receiverName = `EmailReceiver-${testName}`; + const severity = 'severity'; + const warning = 'warning'; + const service = 'service'; + const regex = '^(foo1|foo2|baz)$'; + const matcher1 = `${severity} = ${warning}`; + const matcher2 = `${service} =~ ${regex}`; + const yaml = `global: + resolve_timeout: 5m +inhibit_rules: + - equal: + - namespace + - alertname + source_matchers: + - severity = critical + target_matchers: + - severity =~ warning|info + - equal: + - namespace + - alertname + source_matchers: + - severity = warning + target_matchers: + - severity = info + - equal: + - namespace + source_matchers: + - alertname = InfoInhibitor + target_matchers: + - severity = info +receivers: + - name: Default + - name: Watchdog + - name: Critical + - name: "null" + - name: ${receiverName} + email_configs: + - to: you@there.com + from: me@here.com + smarthost: "smarthost:8080" +route: + group_by: + - namespace + group_interval: 5m + group_wait: 30s + receiver: Default + repeat_interval: 12h + routes: + - matchers: + - alertname = Watchdog + receiver: Watchdog + - matchers: + - alertname = InfoInhibitor + receiver: "null" + - matchers: + - severity = critical + receiver: Critical + - receiver: ${receiverName} + match: + ${severity}: ${warning} + match_re: + ${service}: ${regex}`; + cy.log('add a receiver with match and match_re routing labels'); + alertmanager.visitAlertmanagerPage(); + alertmanager.visitYAMLPage(); + yamlEditor.setEditorContent(yaml); + yamlEditor.clickSaveCreateButton(); + cy.get('.yaml-editor__buttons .pf-m-success').should('exist'); + detailsPage.selectTab('details'); + listPage.rows.shouldExist(receiverName); + alertmanager.visitEditPage(receiverName); + cy.byLegacyTestID('label-0').should('have.value', matcher1); + cy.byLegacyTestID('label-1').should('have.value', matcher2); + alertmanager.save(); + + cy.log('verify match and match_re routing labels were converted to matchers'); + alertmanager.visitAlertmanagerPage(); + alertmanager.visitYAMLPage(); + yamlEditor.getEditorContent().then((content) => { + const config: AlertmanagerConfig = safeLoad(content); + const route: AlertmanagerRoute = _.find(config.route.routes, { + receiver: receiverName, + }); + expect(route.matchers[0]).toEqual(matcher1); + expect(route.matchers[1]).toEqual(matcher2); + }); + }); +}); diff --git a/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/receivers/email.spec.ts b/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/receivers/email.spec.ts new file mode 100644 index 00000000000..ffcdfc5abf7 --- /dev/null +++ b/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/receivers/email.spec.ts @@ -0,0 +1,120 @@ +import * as _ from 'lodash'; +import { checkErrors, testName } from '../../../../support'; +import { alertmanager, getGlobalsAndReceiverConfig } from '../../../../views/alertmanager'; +import * as yamlEditor from '../../../../views/yaml-editor'; + +const receiverName = `EmailReceiver-${testName}`; +const receiverType = 'email'; +const configName = `${receiverType}_configs`; +const localhost = 'localhost'; +const label = 'severity = warning'; +const emailTo = 'you@there.com'; +const emailFrom = 'me@here.com'; +const emailSmarthost = 'smarthost:8080'; +const username = 'username'; +const password = 'password'; +const identity = 'identity'; +const secret = 'secret'; +const html = 'myhtml'; + +describe('Alertmanager: Email Receiver Form', () => { + before(() => { + cy.login(); + cy.initAdmin(); + }); + + afterEach(() => { + checkErrors(); + }); + + after(() => { + alertmanager.reset(); + }); + + it('creates and edits Email Receiver correctly', () => { + cy.log('create Email Receiver'); + alertmanager.createReceiver(receiverName, configName); + // prior to smtp change, save as default is disabled + cy.byLegacyTestID('save-as-default').should('be.disabled'); + cy.byLegacyTestID('email-hello').invoke('val').should('eq', localhost); + cy.byLegacyTestID('email-require-tls').should('be.checked'); + alertmanager.showAdvancedConfiguration(); + cy.byLegacyTestID('send-resolved-alerts').should('not.be.checked'); + cy.byLegacyTestID('email-html') + .invoke('val') + .should('eq', '{{ template "email.default.html" . }}'); + cy.byLegacyTestID('email-to').type(emailTo); + cy.byLegacyTestID('email-from').type(emailFrom); + cy.byLegacyTestID('save-as-default').should('be.enabled'); + cy.byLegacyTestID('email-smarthost').type(emailSmarthost); + cy.byLegacyTestID('label-0').type(label); + alertmanager.save(); + + cy.log('verify Email Receiver was created correctly'); + alertmanager.validateCreation(receiverName, receiverType, label); + alertmanager.visitYAMLPage(); + yamlEditor.getEditorContent().then((content) => { + const configs = getGlobalsAndReceiverConfig(receiverName, configName, content); + expect(_.has(configs.globals, 'email_to')).toBeFalsy(); + expect(_.has(configs.globals, 'smtp_from')).toBeFalsy(); + expect(_.has(configs.globals, 'smtp_smarthost')).toBeFalsy(); + expect(_.has(configs.globals, 'smtp_require_tls')).toBeFalsy(); + expect(configs.receiverConfig.to).toBe(emailTo); + expect(configs.receiverConfig.from).toBe(emailFrom); + expect(configs.receiverConfig.smarthost).toBe(emailSmarthost); + expect(_.has(configs.receiverConfig, 'require_tls')).toBeFalsy(); // unchanged from global value + }); + + cy.log('save globals and advanced fields correctly'); + alertmanager.visitEditPage(receiverName); + cy.byLegacyTestID('email-to').invoke('val').should('eq', emailTo); + cy.byLegacyTestID('save-as-default').should('be.enabled'); // smtp_from different from global + cy.byLegacyTestID('save-as-default').should('not.be.checked'); + cy.byLegacyTestID('email-from').invoke('val').should('eq', emailFrom); + cy.byLegacyTestID('email-hello').invoke('val').should('eq', localhost); + cy.byLegacyTestID('email-auth-username').type(username); + cy.byLegacyTestID('email-auth-password').type(password); + cy.byLegacyTestID('email-auth-identity').type(identity); + cy.byLegacyTestID('email-auth-secret').type(secret); + cy.byLegacyTestID('email-require-tls').click(); + alertmanager.showAdvancedConfiguration(); + cy.byLegacyTestID('send-resolved-alerts').click(); + cy.byLegacyTestID('email-html').clear(); + cy.byLegacyTestID('email-html').type(html); + alertmanager.save(); + + cy.log('verify globals and advanced fields were saved correctly'); + alertmanager.visitYAMLPage(); + yamlEditor.getEditorContent().then((content) => { + const configs = getGlobalsAndReceiverConfig(receiverName, configName, content); + expect(_.has(configs.globals, 'smtp_auth_username')).toBeFalsy(); + expect(configs.receiverConfig.auth_username).toBe(username); + expect(configs.receiverConfig.auth_password).toBe(password); + expect(configs.receiverConfig.auth_identity).toBe(identity); + expect(configs.receiverConfig.auth_secret).toBe(secret); + expect(configs.receiverConfig.require_tls).toBeFalsy(); + expect(configs.receiverConfig.send_resolved).toBeTruthy(); + expect(configs.receiverConfig.html).toBe(html); + }); + + cy.log('save as default'); + alertmanager.visitEditPage(receiverName); + cy.byLegacyTestID('save-as-default').should('not.be.checked'); + cy.byLegacyTestID('save-as-default').click(); + alertmanager.save(); + alertmanager.visitYAMLPage(); + yamlEditor.getEditorContent().then((content) => { + const configs = getGlobalsAndReceiverConfig(receiverName, configName, content); + expect(configs.globals.smtp_from).toBe(emailFrom); + expect(configs.globals.smtp_hello).toBe(localhost); + expect(configs.globals.smtp_smarthost).toBe(emailSmarthost); + expect(configs.globals.smtp_auth_username).toBe(username); + expect(configs.globals.smtp_auth_password).toBe(password); + expect(configs.globals.smtp_auth_identity).toBe(identity); + expect(configs.globals.smtp_auth_secret).toBe(secret); + expect(configs.globals.smtp_require_tls).toBeFalsy(); + // non-global fields should still be saved with Receiver + expect(configs.receiverConfig.to).toBe(emailTo); + }); + }); +}); diff --git a/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/receivers/pagerduty.spec.ts b/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/receivers/pagerduty.spec.ts new file mode 100644 index 00000000000..47be021ea45 --- /dev/null +++ b/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/receivers/pagerduty.spec.ts @@ -0,0 +1,160 @@ +import * as _ from 'lodash'; +import { checkErrors, testName } from '../../../../support'; +import { alertmanager, getGlobalsAndReceiverConfig } from '../../../../views/alertmanager'; +import { listPage } from '../../../../views/list-page'; +import * as yamlEditor from '../../../../views/yaml-editor'; + +const receiverName = `PagerDutyReceiver-${testName}`; +const receiverType = 'pagerduty'; +const configName = `${receiverType}_configs`; +const severity = 'severity'; +const label = `${severity} = warning`; +const pagerDutyClient = '{{ template "pagerduty.default.client" . }}'; +const pagerDutyClientURL = '{{ template "pagerduty.default.clientURL" . }}'; +const pagerDutyURL1 = 'http://pagerduty-url-specific-to-receiver'; +const pagerDutyURL2 = 'http://global-pagerduty-url'; +const pagerDutyURL3 = 'http://pagerduty-url-specific-to-receiver'; +const clientURL = 'http://updated-client-url'; +const pagerDutyDescription = 'new description'; + +describe('Alertmanager: PagerDuty Receiver Form', () => { + before(() => { + cy.login(); + cy.initAdmin(); + }); + + afterEach(() => { + checkErrors(); + }); + + after(() => { + alertmanager.reset(); + }); + + it('creates and edits PagerDuty Receiver correctly', () => { + cy.log('create PagerDuty Receiver'); + alertmanager.createReceiver(receiverName, configName); + cy.byLegacyTestID('integration-key').type(''); + cy.byLegacyTestID('pagerduty-url') + .invoke('val') + .should('eq', 'https://events.pagerduty.com/v2/enqueue'); + alertmanager.showAdvancedConfiguration(); + cy.byLegacyTestID('send-resolved-alerts').should('be.checked'); + cy.byLegacyTestID('pagerduty-client').invoke('val').should('eq', pagerDutyClient); + cy.byLegacyTestID('pagerduty-client-url').invoke('val').should('eq', pagerDutyClientURL); + cy.byLegacyTestID('pagerduty-description') + .invoke('val') + .should('eq', '{{ template "pagerduty.default.description" .}}'); + cy.byLegacyTestID('pagerduty-severity').invoke('val').should('eq', 'error'); + cy.byLegacyTestID('label-0').type(label); + alertmanager.save(); + + cy.log('verify PagerDuty Receiver was created correctly'); + alertmanager.validateCreation(receiverName, receiverType, label); + + cy.log('update pagerduty_url'); + listPage.rows.clickKebabAction(receiverName, 'Edit Receiver'); + // Save as default checkbox disabled when url equals global url + cy.byLegacyTestID('save-as-default').should('be.disabled'); + // changing url enables Save as default checkbox, should save pagerduty_url with Receiver + cy.byLegacyTestID('pagerduty-url').clear(); + cy.byLegacyTestID('pagerduty-url').type(pagerDutyURL1); + cy.byLegacyTestID('save-as-default').should('be.enabled'); + alertmanager.save(); + + cy.log('verify pagerduty_url was saved with Receiver and not global'); + alertmanager.visitYAMLPage(); + yamlEditor.getEditorContent().then((content) => { + const configs = getGlobalsAndReceiverConfig(receiverName, configName, content); + expect(_.has(configs.globals, 'pagerduty_url')).toBeFalsy(); + expect(configs.receiverConfig.url).toBe(pagerDutyURL1); + }); + + cy.log('save pagerduty_url as global'); + alertmanager.visitEditPage(receiverName); + cy.byLegacyTestID('pagerduty-url').clear(); + cy.byLegacyTestID('pagerduty-url').type(pagerDutyURL2); + cy.byLegacyTestID('save-as-default').should('be.enabled').check(); + alertmanager.save(); + + cy.log('verify pagerduty_url was saved as global'); + alertmanager.visitYAMLPage(); + yamlEditor.getEditorContent().then((content) => { + const configs = getGlobalsAndReceiverConfig(receiverName, configName, content); + expect(configs.globals.pagerduty_url).toBe(pagerDutyURL2); + expect(_.has(configs.receiverConfig, 'url')).toBeFalsy(); + }); + + cy.log('add pagerduty_url to receiver with existing global'); + alertmanager.visitEditPage(receiverName); + cy.byLegacyTestID('pagerduty-url').clear(); + cy.byLegacyTestID('pagerduty-url').type(pagerDutyURL3); + cy.byLegacyTestID('save-as-default').should('be.enabled'); + cy.byLegacyTestID('save-as-default').should('not.be.checked'); + alertmanager.save(); + + cy.log( + 'verify pagerduty_url should be saved with Receiver, as well as having a global pagerduty_url prop', + ); + alertmanager.visitYAMLPage(); + yamlEditor.getEditorContent().then((content) => { + const configs = getGlobalsAndReceiverConfig(receiverName, configName, content); + expect(configs.globals.pagerduty_url).toBe(pagerDutyURL2); + expect(configs.receiverConfig.url).toBe(pagerDutyURL3); + }); + + cy.log('update advanced configuration fields correctly'); + alertmanager.visitEditPage(receiverName); + alertmanager.showAdvancedConfiguration(); + cy.byLegacyTestID('send-resolved-alerts').should('be.checked').click(); + cy.byLegacyTestID('send-resolved-alerts').should('not.be.checked'); + cy.byLegacyTestID('pagerduty-client').clear(); + cy.byLegacyTestID('pagerduty-client').type('updated-client'); + cy.byLegacyTestID('pagerduty-client-url').clear(); + cy.byLegacyTestID('pagerduty-client-url').type(clientURL); + alertmanager.save(); + + cy.log('verify 3 changed fields should be saved with Receiver'); + alertmanager.visitYAMLPage(); + yamlEditor.getEditorContent().then((content) => { + const configs = getGlobalsAndReceiverConfig(receiverName, configName, content); + expect(configs.receiverConfig.send_resolved).toBeFalsy(); + expect(configs.receiverConfig.client).toBe('updated-client'); + expect(configs.receiverConfig.client_url).toBe('http://updated-client-url'); + expect(configs.receiverConfig.description).toBe(undefined); + expect(configs.receiverConfig.severity).toBe(undefined); + }); + + cy.log( + 'restore default values for the 3, change desc and severity - which should then be saved with Receiver while initial 3 are removed from Receiver config', + ); + alertmanager.visitEditPage(receiverName); + alertmanager.showAdvancedConfiguration(); + cy.byLegacyTestID('send-resolved-alerts').should('not.be.checked').click(); + cy.byLegacyTestID('send-resolved-alerts').should('be.checked'); + cy.byLegacyTestID('pagerduty-client').clear(); + cy.byLegacyTestID('pagerduty-client').type(pagerDutyClient, { + parseSpecialCharSequences: false, + }); + cy.byLegacyTestID('pagerduty-client-url').clear(); + cy.byLegacyTestID('pagerduty-client-url').type(pagerDutyClientURL, { + parseSpecialCharSequences: false, + }); + cy.byLegacyTestID('pagerduty-description').clear(); + cy.byLegacyTestID('pagerduty-description').type(pagerDutyDescription); + cy.byLegacyTestID('pagerduty-severity').clear(); + cy.byLegacyTestID('pagerduty-severity').type(severity); + alertmanager.save(); + + cy.log('verify'); + alertmanager.visitYAMLPage(); + yamlEditor.getEditorContent().then((content) => { + const configs = getGlobalsAndReceiverConfig(receiverName, configName, content); + expect(configs.receiverConfig.send_resolved).toBe(undefined); + expect(configs.receiverConfig.client).toBe(undefined); + expect(configs.receiverConfig.client_url).toBe(undefined); + expect(configs.receiverConfig.description).toBe(pagerDutyDescription); + expect(configs.receiverConfig.severity).toBe(severity); + }); + }); +}); diff --git a/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/receivers/slack.spec.ts b/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/receivers/slack.spec.ts new file mode 100644 index 00000000000..761a9ed1b1d --- /dev/null +++ b/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/receivers/slack.spec.ts @@ -0,0 +1,103 @@ +import * as _ from 'lodash'; +import { checkErrors, testName } from '../../../../support'; +import { alertmanager, getGlobalsAndReceiverConfig } from '../../../../views/alertmanager'; +import * as yamlEditor from '../../../../views/yaml-editor'; + +const receiverName = `SlackReceiver-${testName}`; +const receiverType = 'slack'; +const configName = `${receiverType}_configs`; +const label = 'severity = warning'; +const slackAPIURL = 'http://myslackapi'; +const slackChannel = 'myslackchannel'; +const slackIconURL = 'http://slackiconurl'; +const slackUsername = 'slackusername'; + +describe('Alertmanager: Slack Receiver Form', () => { + before(() => { + cy.login(); + cy.initAdmin(); + }); + + afterEach(() => { + checkErrors(); + }); + + after(() => { + alertmanager.reset(); + }); + + it('creates and edits Slack Receiver correctly', () => { + cy.log('create Slack Receiver'); + alertmanager.createReceiver(receiverName, configName); + cy.byLegacyTestID('save-as-default').should('be.disabled'); + alertmanager.showAdvancedConfiguration(); + cy.byLegacyTestID('send-resolved-alerts').should('not.be.checked'); + cy.byLegacyTestID('slack-icon-url') + .invoke('val') + .should('eq', '{{ template "slack.default.iconurl" .}}'); + cy.byLegacyTestID('slack-icon-emoji').should('not.exist'); + cy.byLegacyTestID('slack-icon-type-emoji').click(); + cy.byLegacyTestID('slack-icon-url').should('not.exist'); + cy.byLegacyTestID('slack-icon-emoji') + .invoke('val') + .should('eq', '{{ template "slack.default.iconemoji" .}}'); + cy.byLegacyTestID('slack-username') + .invoke('val') + .should('eq', '{{ template "slack.default.username" . }}'); + cy.byLegacyTestID('slack-link-names').should('not.be.checked'); + cy.byLegacyTestID('slack-api-url').type(slackAPIURL); + cy.byLegacyTestID('save-as-default').should('be.enabled'); + cy.byLegacyTestID('slack-channel').type(slackChannel); + cy.byLegacyTestID('label-0').type(label); + alertmanager.save(); + + cy.log('verify Slack Receiver was created correctly'); + alertmanager.validateCreation(receiverName, receiverType, label); + alertmanager.visitYAMLPage(); + yamlEditor.getEditorContent().then((content) => { + const configs = getGlobalsAndReceiverConfig(receiverName, configName, content); + expect(_.has(configs.globals, 'slack_api_url')).toBeFalsy(); + expect(configs.receiverConfig.channel).toBe(slackChannel); + expect(configs.receiverConfig.api_url).toBe(slackAPIURL); + // make sure adv fields are not saved since they equal their global values + expect(_.has(configs.receiverConfig, 'send_resolved')).toBeFalsy(); + expect(_.has(configs.receiverConfig, 'username')).toBeFalsy(); + }); + + cy.log('save globals and advanced fields correctly'); + alertmanager.visitEditPage(receiverName); + cy.byLegacyTestID('slack-channel').invoke('val').should('eq', slackChannel); + cy.byLegacyTestID('save-as-default').should('be.enabled'); + cy.byLegacyTestID('slack-api-url').invoke('val').should('eq', slackAPIURL); + alertmanager.showAdvancedConfiguration(); + cy.byLegacyTestID('send-resolved-alerts').click(); + cy.byLegacyTestID('slack-icon-url').clear(); + cy.byLegacyTestID('slack-icon-url').type(slackIconURL); + cy.byLegacyTestID('slack-username').clear(); + cy.byLegacyTestID('slack-username').type(slackUsername); + cy.byLegacyTestID('slack-link-names').click(); + cy.byLegacyTestID('save-as-default').click(); + alertmanager.save(); + + cy.log('verify advanced fields were saved correctly'); + alertmanager.visitEditPage(receiverName); + alertmanager.showAdvancedConfiguration(); + cy.byLegacyTestID('send-resolved-alerts').should('be.checked'); + cy.byLegacyTestID('slack-icon-url').invoke('val').should('eq', slackIconURL); + cy.byLegacyTestID('slack-icon-emoji').should('not.exist'); + cy.byLegacyTestID('slack-username').invoke('val').should('eq', slackUsername); + cy.byLegacyTestID('slack-link-names').should('be.checked'); + alertmanager.visitAlertmanagerPage(); + alertmanager.visitYAMLPage(); + yamlEditor.getEditorContent().then((content) => { + const configs = getGlobalsAndReceiverConfig(receiverName, configName, content); + expect(configs.globals.slack_api_url).toBe(slackAPIURL); + expect(_.has(configs.receiverConfig, 'api_url')).toBeFalsy(); + expect(configs.receiverConfig.channel).toBe('myslackchannel'); + expect(configs.receiverConfig.send_resolved).toBeTruthy(); + expect(configs.receiverConfig.icon_url).toBe(slackIconURL); + expect(configs.receiverConfig.username).toBe(slackUsername); + expect(configs.receiverConfig.link_names).toBeTruthy(); + }); + }); +}); diff --git a/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/receivers/webhook.spec.ts b/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/receivers/webhook.spec.ts new file mode 100644 index 00000000000..28dc9b3dba0 --- /dev/null +++ b/frontend/packages/integration-tests-cypress/tests/cluster-settings/alertmanager/receivers/webhook.spec.ts @@ -0,0 +1,65 @@ +import * as _ from 'lodash'; +import { checkErrors, testName } from '../../../../support'; +import { alertmanager, getGlobalsAndReceiverConfig } from '../../../../views/alertmanager'; +import * as yamlEditor from '../../../../views/yaml-editor'; + +const receiverName = `WebhookReceiver-${testName}`; +const receiverType = 'webhook'; +const configName = `${receiverType}_configs`; +const label = 'severity = warning'; +const webhookURL = 'http://mywebhookurl'; +const updatedWebhookURL = 'http://myupdatedwebhookurl'; + +describe('Alertmanager: Webhook Receiver Form', () => { + before(() => { + cy.login(); + cy.initAdmin(); + }); + + afterEach(() => { + checkErrors(); + }); + + after(() => { + alertmanager.reset(); + }); + + it('creates and edits Webhook Receiver correctly', () => { + cy.log('create Webhook Receiver'); + alertmanager.createReceiver(receiverName, configName); + alertmanager.showAdvancedConfiguration(); + cy.byLegacyTestID('send-resolved-alerts').should('be.checked'); + cy.byLegacyTestID('webhook-url').type(webhookURL); + cy.byLegacyTestID('label-0').type(label); + alertmanager.save(); + + cy.log('verify Webhook Receiver was created correctly'); + alertmanager.validateCreation(receiverName, receiverType, label); + alertmanager.visitYAMLPage(); + yamlEditor.getEditorContent().then((content) => { + const configs = getGlobalsAndReceiverConfig(receiverName, configName, content); + expect(configs.receiverConfig.url).toBe(webhookURL); + expect(_.has(configs.receiverConfig, 'send_resolved')).toBeFalsy(); + }); + + cy.log('edits Webhook Receiver and saves advanced fields correctly'); + alertmanager.visitEditPage(receiverName); + cy.byLegacyTestID('webhook-url').invoke('val').should('eq', webhookURL); + cy.byLegacyTestID('webhook-url').clear(); + cy.byLegacyTestID('webhook-url').type(updatedWebhookURL); + alertmanager.showAdvancedConfiguration(); + cy.byLegacyTestID('send-resolved-alerts').click(); + alertmanager.save(); + + cy.log('verify advanced fields were saved correctly'); + alertmanager.visitEditPage(receiverName); + cy.byLegacyTestID('send-resolved-alerts').should('not.be.checked'); + alertmanager.visitAlertmanagerPage(); + alertmanager.visitYAMLPage(); + yamlEditor.getEditorContent().then((content) => { + const configs = getGlobalsAndReceiverConfig(receiverName, configName, content); + expect(configs.receiverConfig.url).toBe(updatedWebhookURL); + expect(configs.receiverConfig.send_resolved).toBeFalsy(); + }); + }); +}); diff --git a/frontend/packages/integration-tests-cypress/views/alertmanager.ts b/frontend/packages/integration-tests-cypress/views/alertmanager.ts new file mode 100644 index 00000000000..390ead805af --- /dev/null +++ b/frontend/packages/integration-tests-cypress/views/alertmanager.ts @@ -0,0 +1,90 @@ +import { Base64 } from 'js-base64'; +import { safeLoad } from 'js-yaml'; +import * as _ from 'lodash'; +import { + AlertmanagerConfig, + AlertmanagerReceiver, +} from '@console/internal/components/monitoring/alertmanager/alertmanager-config'; +import { detailsPage } from './details-page'; +import { listPage } from './list-page'; +import * as yamlEditor from './yaml-editor'; + +const defaultAlertmanagerYaml = Base64.encode(`"global": + "resolve_timeout": "5m" +"inhibit_rules": +- "equal": + - "namespace" + - "alertname" + "source_match": + "severity": "critical" + "target_match_re": + "severity": "warning|info" +- "equal": + - "namespace" + - "alertname" + "source_match": + "severity": "warning" + "target_match_re": + "severity": "info" +"receivers": +- "name": "Default" +- "name": "Watchdog" +- "name": "Critical" +"route": + "group_by": + - "namespace" + "group_interval": "5m" + "group_wait": "30s" + "receiver": "Default" + "repeat_interval": "12h" + "routes": + - "matchers": + - "alertname = Watchdog" + "receiver": "Watchdog" + - "matchers": + - "severity = critical" + "receiver": "Critical"`); + +export const getGlobalsAndReceiverConfig = (name: string, configName: string, content: string) => { + const config: AlertmanagerConfig = safeLoad(content); + const receiverConfig: AlertmanagerReceiver | undefined = _.find(config.receivers, { + name, + }); + return { + globals: config.global, + receiverConfig: receiverConfig?.[configName][0], + }; +}; + +export const alertmanager = { + createReceiver: (receiverName: string, configs: string) => { + alertmanager.visitAlertmanagerPage(); + cy.byLegacyTestID('create-receiver').click(); + cy.byLegacyTestID('receiver-name').type(receiverName); + cy.byLegacyTestID('dropdown-button').click(); + cy.get(`[data-test-dropdown-menu=${configs}]`).click(); + }, + reset: () => + cy.exec( + `kubectl patch secret 'alertmanager-main' -n 'openshift-monitoring' --type='json' -p='[{ op: 'replace', path: '/data/alertmanager.yaml', value: ${defaultAlertmanagerYaml}}]'`, + ), + save: () => cy.byLegacyTestID('save-changes').should('be.enabled').click(), + showAdvancedConfiguration: () => cy.get('button.pf-c-expandable-section__toggle').click(), + validateCreation: (receiverName: string, type: string, label: string) => { + cy.byLegacyTestID('item-filter').clear(); + cy.byLegacyTestID('item-filter').type(receiverName); + listPage.rows.shouldExist(receiverName); + listPage.rows.shouldExist(type); + listPage.rows.shouldExist(label); + }, + visitAlertmanagerPage: () => { + cy.visit('/monitoring/alertmanagerconfig'); + }, + visitEditPage: (receiverName: string) => { + cy.visit(`/monitoring/alertmanagerconfig/receivers/${receiverName}/edit`); + }, + visitYAMLPage: () => { + detailsPage.selectTab('yaml'); + yamlEditor.isLoaded(); + }, +}; diff --git a/frontend/packages/knative-plugin/console-extensions.json b/frontend/packages/knative-plugin/console-extensions.json index 45cdc33d2ed..d2aaf6e3366 100644 --- a/frontend/packages/knative-plugin/console-extensions.json +++ b/frontend/packages/knative-plugin/console-extensions.json @@ -681,7 +681,7 @@ "namespace": "knative-serving" }, "flags": { - "required": ["KNATIVE_SERVING"] + "required": ["KNATIVE_SERVING", "KNATIVE_SERVING_SERVICE"] } }, { @@ -1473,7 +1473,7 @@ "properties": { "model": { "group": "serving.knative.dev", - "version": "v1alpha1", + "version": "v1beta1", "kind": "DomainMapping" }, "label": "%knative-plugin~DomainMapping%", diff --git a/frontend/packages/knative-plugin/integration-tests/features/serverless/serverless-function.feature b/frontend/packages/knative-plugin/integration-tests/features/serverless/serverless-function.feature index 0f9fde55b66..d9f52a61032 100644 --- a/frontend/packages/knative-plugin/integration-tests/features/serverless/serverless-function.feature +++ b/frontend/packages/knative-plugin/integration-tests/features/serverless/serverless-function.feature @@ -80,6 +80,7 @@ Feature: Creation and Visualisation of serverless fuctions Scenario Outline: Create Serverless Function from the Import from Git Form on Add page with Pipeline: SF-01-TC07 Given user has installed OpenShift Pipelines Operator # Below manual creation of the Piepline and ClusterTasks can be removed when Serverless new version 1.28 is released + And user has created or selected namespace "aut-serverless-function" And user created Serverless Function node Pipeline And user is at Add page And user is at Import from Git form @@ -87,7 +88,6 @@ Feature: Creation and Visualisation of serverless fuctions And user enters Name as "" And user selects Add Pipeline checkbox in Pipelines section And user clicks Create button on Add page - And user clicks on List view button Then user is able to see workload "" in topology page And user clicks on the Knative Service workload "" And user switches to the "Details" tab @@ -121,7 +121,6 @@ Feature: Creation and Visualisation of serverless fuctions @regression @odc-6360 Scenario Outline: Pipeline section should not present in Create Serverless function form if Pipeline is not available: SF-01-TC09 - Given user has installed OpenShift Pipelines Operator And user is at Add page When user clicks on Create Serverless function card And user enters git url "" @@ -158,8 +157,8 @@ Feature: Creation and Visualisation of serverless fuctions | git_url | workload_name | invoke_format | | https://github.com/openshift-dev-console/kn-func-typescript-http | kn-func-typescript-http | HTTP | | https://github.com/openshift-dev-console/kn-func-typescript-cloudevents | kn-func-typescript-cloudevents | CloudEvent | - - + + @regression @odc-7316 Scenario: Create serverless form extensions cards: SF-01-TC11 Given user is at Add page diff --git a/frontend/packages/knative-plugin/integration-tests/support/pages/dev-perspective/test-serverless-function-page.ts b/frontend/packages/knative-plugin/integration-tests/support/pages/dev-perspective/test-serverless-function-page.ts new file mode 100644 index 00000000000..995fb84276a --- /dev/null +++ b/frontend/packages/knative-plugin/integration-tests/support/pages/dev-perspective/test-serverless-function-page.ts @@ -0,0 +1,24 @@ +import { guidedTour } from '@console/cypress-integration-tests/views/guided-tour'; +import { app } from '@console/dev-console/integration-tests/support/pages'; + +export const reloadPageUntilWorkloadIsDisplayed = ( + workloadName: string, + maxAttempts = 15, + attempts = 0, +) => { + if (attempts > maxAttempts) { + throw new Error('Timed out waiting for Workload to be displayed'); + } + app.waitForDocumentLoad(); + cy.get('[aria-label="Topology List View"]', { timeout: 15000 }).should('be.visible'); + cy.get('body').then(($el) => { + if (!$el.find(`[aria-label="${workloadName} sub-resources"]`).is(':visible')) { + cy.reload(); + app.waitForLoad(); + guidedTour.close(); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(5000); + reloadPageUntilWorkloadIsDisplayed(workloadName, maxAttempts, attempts + 1); + } + }); +}; diff --git a/frontend/packages/knative-plugin/integration-tests/support/step-definitions/serverless/serverless-function.ts b/frontend/packages/knative-plugin/integration-tests/support/step-definitions/serverless/serverless-function.ts index ece69db254b..d2aa7f2bfd5 100644 --- a/frontend/packages/knative-plugin/integration-tests/support/step-definitions/serverless/serverless-function.ts +++ b/frontend/packages/knative-plugin/integration-tests/support/step-definitions/serverless/serverless-function.ts @@ -25,7 +25,7 @@ When('user is able to see the runtime details', () => { }); Then('user is able to see Type as Function', () => { - cy.byTestID('serverless-function-type').should('be.visible'); + cy.get('[data-test="serverless-function-type"]', { timeout: 30000 }).should('be.visible'); cy.byTestID('serverless-function-type').find('dt').should('have.text', 'Type'); cy.byTestID('serverless-function-type').find('dd').should('have.text', 'Function'); }); diff --git a/frontend/packages/knative-plugin/integration-tests/support/step-definitions/serverless/test-serverless-function.ts b/frontend/packages/knative-plugin/integration-tests/support/step-definitions/serverless/test-serverless-function.ts index 1d3919eb238..2fb763a4b7f 100644 --- a/frontend/packages/knative-plugin/integration-tests/support/step-definitions/serverless/test-serverless-function.ts +++ b/frontend/packages/knative-plugin/integration-tests/support/step-definitions/serverless/test-serverless-function.ts @@ -2,15 +2,14 @@ import { When, Then } from 'cypress-cucumber-preprocessor/steps'; import * as jsonEditor from '@console/cypress-integration-tests/views/yaml-editor'; import { topologyPO } from '@console/topology/integration-tests/support/page-objects/topology-po'; import { topologyPage } from '@console/topology/integration-tests/support/pages/topology'; +import { reloadPageUntilWorkloadIsDisplayed } from '../../pages/dev-perspective/test-serverless-function-page'; When( 'user sees workload {string} along with a revision in topology page', (workloadName: string) => { topologyPage.verifyWorkloadInTopologyPage(workloadName); cy.get(topologyPO.quickSearchPO.listView).click(); - // eslint-disable-next-line cypress/no-unnecessary-waiting - cy.wait(5000); /* Revision taking more time to appear than the default timeout */ - cy.get(`[id$=${workloadName}]`).should('be.visible'); + reloadPageUntilWorkloadIsDisplayed(workloadName); cy.get(topologyPO.quickSearchPO.graphView).click(); }, ); @@ -59,7 +58,7 @@ When( ); When('user clicks the {string} Button', (button: string) => { - cy.byTestID(`${button.toLowerCase()}-action`).should('be.visible').click(); + cy.byTestID(`${button.toLowerCase()}-action`).eq(0).should('be.visible').click(); }); Then('user is able to see a Success Alert', () => { diff --git a/frontend/packages/knative-plugin/src/components/add/channels/ChannelForm.tsx b/frontend/packages/knative-plugin/src/components/add/channels/ChannelForm.tsx index 90cb6ae24d8..63201c7d304 100644 --- a/frontend/packages/knative-plugin/src/components/add/channels/ChannelForm.tsx +++ b/frontend/packages/knative-plugin/src/components/add/channels/ChannelForm.tsx @@ -84,7 +84,9 @@ const ChannelForm: React.FC & OwnProps> = ({ }), ); setFieldTouched('yamlData', true); - validateForm(); + setTimeout(() => { + validateForm(); + }, 0); }, [setErrors, setStatus, setFieldValue, setFieldTouched, values.formData, validateForm], ); diff --git a/frontend/packages/knative-plugin/src/hooks/useKameletsData.ts b/frontend/packages/knative-plugin/src/hooks/useKameletsData.ts index 0e48d9b3449..49034ee3d99 100644 --- a/frontend/packages/knative-plugin/src/hooks/useKameletsData.ts +++ b/frontend/packages/knative-plugin/src/hooks/useKameletsData.ts @@ -1,4 +1,5 @@ import * as React from 'react'; +import uniqBy from 'lodash-es/uniqBy'; import { useK8sWatchResources } from '@console/internal/components/utils/k8s-watch-hook'; import { K8sResourceKind, referenceForModel } from '@console/internal/module/k8s'; import { CAMEL_K_OPERATOR_NS, GLOBAL_OPERATOR_NS } from '../const'; @@ -38,19 +39,17 @@ export const useKameletsData = (namespace: string): [K8sResourceKind[], boolean, }>(watchedResources); React.useEffect(() => { - const resDataLoaded = Object.keys(extraResources).every((key) => extraResources[key].loaded); + const resDataLoaded = Object.keys(extraResources).some((key) => extraResources[key].loaded); const resDataloadError = Object.keys(extraResources).every( (key) => extraResources[key].loadError, ); const { kamelets: kameletsData, kameletsGlobalNs, kameletsGlobalNs2 } = extraResources; if (resDataLoaded) { - if (kameletsData.data.length > 0) { - setKamelets(kameletsData.data); - } else if (kameletsGlobalNs.data.length > 0) { - setKamelets(kameletsGlobalNs.data); - } else { - setKamelets(kameletsGlobalNs2.data); - } + const allKamelets = uniqBy( + [...kameletsData.data, ...kameletsGlobalNs.data, ...kameletsGlobalNs2.data], + (kamelet) => kamelet?.metadata?.uid, + ); + setKamelets(allKamelets); setKameletsLoaded(kameletsData.loaded || kameletsGlobalNs.loaded || kameletsGlobalNs2.loaded); } else if (resDataloadError) { setKameletsLoadError( diff --git a/frontend/packages/knative-plugin/src/models.ts b/frontend/packages/knative-plugin/src/models.ts index c0e72d78559..8888e0814dc 100644 --- a/frontend/packages/knative-plugin/src/models.ts +++ b/frontend/packages/knative-plugin/src/models.ts @@ -103,7 +103,7 @@ export const ServiceModel: K8sKind = { export const DomainMappingModel: K8sKind = { apiGroup: KNATIVE_SERVING_APIGROUP, - apiVersion: 'v1alpha1', + apiVersion: 'v1beta1', kind: 'DomainMapping', label: 'DomainMapping', // t('knative-plugin~DomainMapping') diff --git a/frontend/packages/knative-plugin/src/utils/__tests__/get-knative-resources.spec.ts b/frontend/packages/knative-plugin/src/utils/__tests__/get-knative-resources.spec.ts index 0fde6eb7725..cea1be4f18d 100644 --- a/frontend/packages/knative-plugin/src/utils/__tests__/get-knative-resources.spec.ts +++ b/frontend/packages/knative-plugin/src/utils/__tests__/get-knative-resources.spec.ts @@ -202,7 +202,7 @@ describe('Get knative resources', () => { const domainMappingResource = knativeCamelDomainMappingResourceWatchers(SAMPLE_NAMESPACE); expect(domainMappingResource.domainmappings).toEqual({ isList: true, - kind: 'serving.knative.dev~v1alpha1~DomainMapping', + kind: 'serving.knative.dev~v1beta1~DomainMapping', namespace: 'mynamespace', optional: true, }); diff --git a/frontend/packages/knative-plugin/src/utils/create-knative-utils.ts b/frontend/packages/knative-plugin/src/utils/create-knative-utils.ts index 5047f5b486f..878d26b41af 100644 --- a/frontend/packages/knative-plugin/src/utils/create-knative-utils.ts +++ b/frontend/packages/knative-plugin/src/utils/create-knative-utils.ts @@ -182,6 +182,7 @@ export const getKnativeServiceDepResource = ( knativeServiceUpdated = _.omit(originalKnativeService, [ 'status', 'spec.template.metadata.name', + 'spec.template.spec.containers', ]); } const knativeDeployResource = mergeData(knativeServiceUpdated || {}, newKnativeDeployResource); diff --git a/frontend/packages/kubevirt-plugin/console-extensions.json b/frontend/packages/kubevirt-plugin/console-extensions.json index 8806cf2af0c..86e44f30f49 100644 --- a/frontend/packages/kubevirt-plugin/console-extensions.json +++ b/frontend/packages/kubevirt-plugin/console-extensions.json @@ -90,16 +90,6 @@ "required": ["KUBEVIRT"] } }, - { - "type": "console.pvc/create-prop", - "properties": { - "label": "%kubevirt-plugin~With Data upload form%", - "path": "~new/data" - }, - "flags": { - "required": ["KUBEVIRT"] - } - }, { "type": "console.pvc/alert", "properties": { diff --git a/frontend/packages/kubevirt-plugin/locales/en/kubevirt-plugin.json b/frontend/packages/kubevirt-plugin/locales/en/kubevirt-plugin.json index 039bfa4b91b..96e71cbb841 100644 --- a/frontend/packages/kubevirt-plugin/locales/en/kubevirt-plugin.json +++ b/frontend/packages/kubevirt-plugin/locales/en/kubevirt-plugin.json @@ -4,7 +4,6 @@ "**Virtual Machines** have templates for quickly creating a virtual machine.": "**Virtual Machines** have templates for quickly creating a virtual machine.", "Template Providers": "Template Providers", "VM templates": "VM templates", - "With Data upload form": "With Data upload form", "Access mode": "Access mode", "Add": "Add", "Access and Volume modes should follow storage feature matrix": "Access and Volume modes should follow storage feature matrix", diff --git a/frontend/packages/kubevirt-plugin/src/topology/kubevirt-data-transformer.ts b/frontend/packages/kubevirt-plugin/src/topology/kubevirt-data-transformer.ts index 1ffceba880f..d535aac1ca8 100644 --- a/frontend/packages/kubevirt-plugin/src/topology/kubevirt-data-transformer.ts +++ b/frontend/packages/kubevirt-plugin/src/topology/kubevirt-data-transformer.ts @@ -6,7 +6,7 @@ import { PodKind, referenceFor, } from '@console/internal/module/k8s'; -import { OverviewItem } from '@console/shared'; +import { OverviewItem, WORKLOAD_TYPES } from '@console/shared'; import { getTopologyEdgeItems, getTopologyGroupItems, @@ -113,8 +113,17 @@ export const getKubevirtTopologyDataModel = ( ), ); vmsDataModel.edges.push(...getTopologyEdgeItems(resource, resources.virtualmachines.data)); + WORKLOAD_TYPES.forEach((workload) => { + vmsDataModel.edges.push(...getTopologyEdgeItems(resource, resources[workload]?.data)); // create visual connector from all WORKLOAD_TYPES to VMs + }); mergeGroup(getTopologyGroupItems(resource), vmsDataModel.nodes); }); + + WORKLOAD_TYPES.forEach((resource) => { + resources[resource]?.data?.forEach((d) => { + vmsDataModel.edges.push(...getTopologyEdgeItems(d, resources.virtualmachines.data)); // create visual connector from VMs to all WORKLOAD_TYPES + }); + }); } return Promise.resolve(vmsDataModel); diff --git a/frontend/packages/metal3-plugin/src/k8s/requests/bare-metal-host.ts b/frontend/packages/metal3-plugin/src/k8s/requests/bare-metal-host.ts index 1c952deeb51..7d9228f7c3e 100644 --- a/frontend/packages/metal3-plugin/src/k8s/requests/bare-metal-host.ts +++ b/frontend/packages/metal3-plugin/src/k8s/requests/bare-metal-host.ts @@ -33,7 +33,7 @@ export const restartHost = (host: BareMetalHostKind) => path: '/metadata/annotations', value: { ...host.metadata.annotations, - 'reboot.metal3.io': 'UI', // value is irrelevant + 'reboot.metal3.io': '', }, }, ]); diff --git a/frontend/packages/operator-lifecycle-manager/locales/en/olm.json b/frontend/packages/operator-lifecycle-manager/locales/en/olm.json index b09eeb79295..51a0ebf3b18 100644 --- a/frontend/packages/operator-lifecycle-manager/locales/en/olm.json +++ b/frontend/packages/operator-lifecycle-manager/locales/en/olm.json @@ -53,7 +53,8 @@ "Status": "Status", "Last updated": "Last updated", "Provided APIs": "Provided APIs", - "Installed Operators are represented by ClusterServiceVersions within this Namespace. For more information, see the <2>Understanding Operators documentation. Or create an Operator and ClusterServiceVersion using the <5>Operator SDK.": "Installed Operators are represented by ClusterServiceVersions within this Namespace. For more information, see the <2>Understanding Operators documentation. Or create an Operator and ClusterServiceVersion using the <5>Operator SDK.", + "Installed Operators are represented by ClusterServiceVersions within this Namespace.": "Installed Operators are represented by ClusterServiceVersions within this Namespace.", + " For more information, see the <3>Understanding Operators documentation. Or create an Operator and ClusterServiceVersion using the <6>Operator SDK.": " For more information, see the <3>Understanding Operators documentation. Or create an Operator and ClusterServiceVersion using the <6>Operator SDK.", "Required": "Required", "Create instance": "Create instance", "No Kubernetes APIs are being provided by this Operator.": "No Kubernetes APIs are being provided by this Operator.", @@ -271,6 +272,8 @@ "Container image": "Container image", "Cluster in STS Mode": "Cluster in STS Mode", "This cluster is using AWS Security Token Service to reach the cloud API. In order for this operator to take the actions it requires directly with the cloud API, you will need to provide a role ARN (with an attached policy) during installation. Please see the operator description for more details.": "This cluster is using AWS Security Token Service to reach the cloud API. In order for this operator to take the actions it requires directly with the cloud API, you will need to provide a role ARN (with an attached policy) during installation. Please see the operator description for more details.", + "Cluster in Azure Workload Identity / Federated Identity Mode": "Cluster in Azure Workload Identity / Federated Identity Mode", + "This cluster is using Azure Workload Identity / Federated Identity to reach the cloud API. In order for this operator to take the actions it requires directly with the cloud API, provide the Client ID, Tenant ID, and Subscription ID during installation. See the operator description for more details.": "This cluster is using Azure Workload Identity / Federated Identity to reach the cloud API. In order for this operator to take the actions it requires directly with the cloud API, provide the Client ID, Tenant ID, and Subscription ID during installation. See the operator description for more details.", "Installed": "Installed", "Not Installed": "Not Installed", "provided by {{provider}}": "provided by {{provider}}", @@ -308,7 +311,8 @@ "Namespace <1>{{operatorSuggestedNamespace}} does not exist and will be created.": "Namespace <1>{{operatorSuggestedNamespace}} does not exist and will be created.", "Enable Operator recommended cluster monitoring on this Namespace": "Enable Operator recommended cluster monitoring on this Namespace", "Namespace monitoring": "Namespace monitoring", - "Please note that installing non-Red Hat operators into OpenShift namespaces and enabling monitoring voids user support. Enabling cluster monitoring for non-Red Hat operators can lead to malicious metrics data overriding existing cluster metrics. For more information, see the <2>cluster monitoring documentation.": "Please note that installing non-Red Hat operators into OpenShift namespaces and enabling monitoring voids user support. Enabling cluster monitoring for non-Red Hat operators can lead to malicious metrics data overriding existing cluster metrics. For more information, see the <2>cluster monitoring documentation.", + "Please note that installing non-Red Hat operators into OpenShift namespaces and enabling monitoring voids user support. Enabling cluster monitoring for non-Red Hat operators can lead to malicious metrics data overriding existing cluster metrics.": "Please note that installing non-Red Hat operators into OpenShift namespaces and enabling monitoring voids user support. Enabling cluster monitoring for non-Red Hat operators can lead to malicious metrics data overriding existing cluster metrics.", + " For more information, see the <3>cluster monitoring documentation.": " For more information, see the <3>cluster monitoring documentation.", "Operator recommended Namespace:": "Operator recommended Namespace:", "Select a Namespace": "Select a Namespace", "Not installing the Operator into the recommended namespace can cause unexpected behavior.": "Not installing the Operator into the recommended namespace can cause unexpected behavior.", @@ -316,8 +320,15 @@ "Operator Installation": "Operator Installation", "Install your Operator by subscribing to one of the update channels to keep the Operator up to date. The strategy determines either manual or automatic updates.": "Install your Operator by subscribing to one of the update channels to keep the Operator up to date. The strategy determines either manual or automatic updates.", "This cluster is using AWS Security Token Service to reach the cloud API. In order for this operator to take the actions it requires directly with the cloud API, you will need to provide a role ARN (with an attached policy) during installation. Manual subscriptions are highly recommended as steps should be taken prior to upgrade to ensure that the permissions required by the next version are properly accounted for in the role. Please see the operator description for more details.": "This cluster is using AWS Security Token Service to reach the cloud API. In order for this operator to take the actions it requires directly with the cloud API, you will need to provide a role ARN (with an attached policy) during installation. Manual subscriptions are highly recommended as steps should be taken prior to upgrade to ensure that the permissions required by the next version are properly accounted for in the role. Please see the operator description for more details.", + "This cluster is using Azure Workload Identity / Federated Identity to reach the cloud API. In order for this operator to take the actions it requires directly with the cloud API, provide the Client ID, Tenant ID, and Subscription ID during installation. Manual subscriptions are highly recommended as steps should be taken before upgrade to ensure that the permissions required by the next version are properly accounted for in the role. See the operator description for more details.": "This cluster is using Azure Workload Identity / Federated Identity to reach the cloud API. In order for this operator to take the actions it requires directly with the cloud API, provide the Client ID, Tenant ID, and Subscription ID during installation. Manual subscriptions are highly recommended as steps should be taken before upgrade to ensure that the permissions required by the next version are properly accounted for in the role. See the operator description for more details.", "role ARN": "role ARN", "The role ARN required for the operator to access the cloud API.": "The role ARN required for the operator to access the cloud API.", + "Azure Client ID": "Azure Client ID", + "The Azure Client ID required for the operator to access the cloud API.": "The Azure Client ID required for the operator to access the cloud API.", + "Azure Tenant ID": "Azure Tenant ID", + "The Azure Tenant ID required for the operator to access the cloud API.": "The Azure Tenant ID required for the operator to access the cloud API.", + "Azure Subscription ID": "Azure Subscription ID", + "The Azure Subscription ID required for the operator to access the cloud API.": "The Azure Subscription ID required for the operator to access the cloud API.", "Update channel": "Update channel", "The channel to track and receive the updates from.": "The channel to track and receive the updates from.", "Installation mode": "Installation mode", diff --git a/frontend/packages/operator-lifecycle-manager/src/components/clusterserviceversion.tsx b/frontend/packages/operator-lifecycle-manager/src/components/clusterserviceversion.tsx index cd5abc11f9f..6cf830b6a36 100644 --- a/frontend/packages/operator-lifecycle-manager/src/components/clusterserviceversion.tsx +++ b/frontend/packages/operator-lifecycle-manager/src/components/clusterserviceversion.tsx @@ -40,6 +40,7 @@ import { ExternalLink, FirehoseResult, getDocumentationURL, + isManaged, Kebab, KebabAction, KebabOption, @@ -730,13 +731,20 @@ export const ClusterServiceVersionsPage: React.FC - Installed Operators are represented by ClusterServiceVersions within this Namespace. For more - information, see the{' '} - Understanding Operators documentation. Or create an - Operator and ClusterServiceVersion using the{' '} - Operator SDK. - + <> + {t( + 'olm~Installed Operators are represented by ClusterServiceVersions within this Namespace.', + )} + {!isManaged() && ( + + {' '} + For more information, see the{' '} + Understanding Operators documentation. Or + create an Operator and ClusterServiceVersion using the{' '} + Operator SDK. + + )} + ); const flatten: Flatten<{ @@ -757,7 +765,7 @@ export const ClusterServiceVersionsPage: React.FC - [obj?.status?.currentCSV, obj?.spec?.startingCSV].includes(metadata.name), + [obj?.status?.currentCSV, obj?.spec?.startingCSV].includes(metadata?.name), ), ), ); @@ -777,6 +785,15 @@ export const ClusterServiceVersionsPage: React.FC = ({
{isAWSSTSCluster(cloudCredentials, infrastructure, authentication) && showWarn && - infraFeatures?.find((i) => i === InfraFeatures[shortLivedTokenAuth]) && ( + infraFeatures?.find((i) => i === InfraFeatures.TokenAuth) && ( = ({

)} + {isAzureWIFCluster(cloudCredentials, infrastructure, authentication) && + showWarn && + infraFeatures?.find((i) => i === InfraFeatures.TokenAuth) && ( + setShowWarn(false)} />} + className="pf-u-mb-lg" + > +

+ {t( + 'olm~This cluster is using Azure Workload Identity / Federated Identity to reach the cloud API. In order for this operator to take the actions it requires directly with the cloud API, provide the Client ID, Tenant ID, and Subscription ID during installation. See the operator description for more details.', + )} +

+
+ )} { return 1; case InfraFeatures.FipsMode: return 2; - case InfraFeatures[shortLivedTokenAuth]: + case InfraFeatures.TokenAuth: return 3; default: return 4; @@ -426,10 +426,21 @@ export const OperatorHubTileView: React.FC = (props) = currentItem.infrastructure, currentItem.authentication, ) && - currentItem.infraFeatures?.find((i) => i === InfraFeatures[shortLivedTokenAuth]) + currentItem.infraFeatures?.find((i) => i === InfraFeatures.TokenAuth) ) { setTokenizedAuth('AWS'); } + if ( + currentItem && + isAzureWIFCluster( + currentItem.cloudCredentials, + currentItem.infrastructure, + currentItem.authentication, + ) && + currentItem.infraFeatures?.find((i) => i === InfraFeatures.TokenAuth) + ) { + setTokenizedAuth('Azure'); + } }, [filteredItems]); const showCommunityOperator = (item: OperatorHubItem) => (ignoreWarning = false) => { diff --git a/frontend/packages/operator-lifecycle-manager/src/components/operator-hub/operator-hub-page.tsx b/frontend/packages/operator-lifecycle-manager/src/components/operator-hub/operator-hub-page.tsx index b06836f26db..5a52ccea0ce 100644 --- a/frontend/packages/operator-lifecycle-manager/src/components/operator-hub/operator-hub-page.tsx +++ b/frontend/packages/operator-lifecycle-manager/src/components/operator-hub/operator-hub-page.tsx @@ -47,8 +47,8 @@ import { subscriptionFor } from '../operator-group'; import { OperatorHubTileView } from './operator-hub-items'; import { getCatalogSourceDisplayName, - shortLivedTokenAuth, isAWSSTSCluster, + isAzureWIFCluster, } from './operator-hub-utils'; import { OperatorHubItem, @@ -151,11 +151,6 @@ export const OperatorHubList: React.FC = ({ [], ); - // TODO remove lodash and implement using Array.prototye.reduce - const infraFeatures = _.uniq( - _.compact(_.map(parsedInfraFeatures, (key) => InfraFeatures[key])), - ); - const { certifiedLevel, healthIndex, @@ -164,7 +159,18 @@ export const OperatorHubList: React.FC = ({ createdAt, support, capabilities: capabilityLevel, + [OperatorHubCSVAnnotationKey.disconnected]: disconnected, + [OperatorHubCSVAnnotationKey.fipsCompliant]: fipsCompliant, + [OperatorHubCSVAnnotationKey.proxyAware]: proxyAware, + [OperatorHubCSVAnnotationKey.cnf]: cnf, + [OperatorHubCSVAnnotationKey.cni]: cni, + [OperatorHubCSVAnnotationKey.csi]: csi, + // tlsProfiles requires addtional changes + // [OperatorHubCSVAnnotationKey.tlsProfiles]: tlsProfiles, [OperatorHubCSVAnnotationKey.tokenAuthAWS]: tokenAuthAWS, + [OperatorHubCSVAnnotationKey.tokenAuthAzure]: tokenAuthAzure, + // tokenAuthGCP requires additional changes + // [OperatorHubCSVAnnotationKey.tokenAuthGCP]: tokenAuthGCP, [OperatorHubCSVAnnotationKey.actionText]: marketplaceActionText, [OperatorHubCSVAnnotationKey.remoteWorkflow]: marketplaceRemoteWorkflow, [OperatorHubCSVAnnotationKey.supportWorkflow]: marketplaceSupportWorkflow, @@ -179,13 +185,39 @@ export const OperatorHubList: React.FC = ({ const auth = loaded && authentication?.data; - // FIXME: this is a temporary hack and should be fixed as part of - // a refactor to include the new style of infrastructure features - // tracked in PORTENABLE-525 + // old infra feature annotation + let infrastructureFeatures: InfraFeatures[] = parsedInfraFeatures.map( + (key) => InfraFeatures[key], + ); + + // new infra feature annotation + const featuresAnnotationsObjects = [ + { key: InfraFeatures.Disconnected, value: disconnected }, + { key: InfraFeatures.FipsMode, value: fipsCompliant }, + { key: InfraFeatures.Proxy, value: proxyAware }, + { key: InfraFeatures.cnf, value: cnf }, + { key: InfraFeatures.cni, value: cni }, + { key: InfraFeatures.csi, value: csi }, + ]; + + // override old with new + featuresAnnotationsObjects.forEach(({ key, value }) => { + if (value === 'false') { + // override existing operators.openshift.io/infrastructure-features annotation value + infrastructureFeatures = infrastructureFeatures.filter((feature) => feature !== key); + } else if (value === 'true') { + infrastructureFeatures.push(key); + } + }); + if (tokenAuthAWS === 'true' && isAWSSTSCluster(cloudCredential, infra, auth)) { - infraFeatures.push(shortLivedTokenAuth); + infrastructureFeatures.push(InfraFeatures.TokenAuth); + } else if (tokenAuthAzure === 'true' && isAzureWIFCluster(cloudCredential, infra, auth)) { + infrastructureFeatures.push(InfraFeatures.TokenAuth); } + const infraFeatures = _.uniq(_.compact(infrastructureFeatures)); + const clusterServiceVersion = loaded && clusterServiceVersionFor( diff --git a/frontend/packages/operator-lifecycle-manager/src/components/operator-hub/operator-hub-subscribe.tsx b/frontend/packages/operator-lifecycle-manager/src/components/operator-hub/operator-hub-subscribe.tsx index 2ce21a021e3..91bd37b703f 100644 --- a/frontend/packages/operator-lifecycle-manager/src/components/operator-hub/operator-hub-subscribe.tsx +++ b/frontend/packages/operator-lifecycle-manager/src/components/operator-hub/operator-hub-subscribe.tsx @@ -21,6 +21,7 @@ import { getDocumentationURL, getURLSearchParams, history, + isManaged, MsgBox, NsDropdown, PageHeading, @@ -82,7 +83,10 @@ export const OperatorHubSubscribeForm: React.FC = const { provider, channels = [], packageName, catalogSource, catalogSourceNamespace } = packageManifest?.status ?? {}; - const [roleARNText, setRoleARNText] = React.useState(null); + const [roleARNText, setRoleARNText] = React.useState(''); + const [azureTenantId, setAzureTenantId] = React.useState(''); + const [azureClientId, setAzureClientId] = React.useState(''); + const [azureSubscriptionId, setAzureSubscriptionId] = React.useState(''); const { catalogNamespace, channel, pkg, tokenizedAuth, version } = getURLSearchParams(); const [targetNamespace, setTargetNamespace] = React.useState(null); const [installMode, setInstallMode] = React.useState(null); @@ -253,7 +257,8 @@ export const OperatorHubSubscribeForm: React.FC = if ( version !== currentLatestVersion || manualSubscriptionsInNamespace?.length > 0 || - tokenizedAuth === 'AWS' + tokenizedAuth === 'AWS' || + tokenizedAuth === 'Azure' ) { setApproval(InstallPlanApproval.Manual); } else setApproval(InstallPlanApproval.Automatic); @@ -416,15 +421,37 @@ export const OperatorHubSubscribeForm: React.FC = }, }; - if (tokenizedAuth === 'AWS') { - subscription.spec.config = { - env: [ - { - name: 'ROLEARN', - value: roleARNText, - }, - ], - }; + switch (tokenizedAuth) { + case 'AWS': + subscription.spec.config = { + env: [ + { + name: 'ROLEARN', + value: roleARNText, + }, + ], + }; + break; + case 'Azure': + subscription.spec.config = { + env: [ + { + name: 'CLIENTID', + value: azureClientId, + }, + { + name: 'TENANTID', + value: azureTenantId, + }, + { + name: 'SUBSCRIPTIONID', + value: azureSubscriptionId, + }, + ], + }; + break; + default: + break; } try { @@ -474,7 +501,9 @@ export const OperatorHubSubscribeForm: React.FC = !namespaceSupports(selectedTargetNamespace)(selectedInstallMode) || (selectedTargetNamespace && cannotResolve) || !_.isEmpty(conflictingProvidedAPIs(selectedTargetNamespace)) || - (tokenizedAuth === 'AWS' && _.isNull(roleARNText)); + (tokenizedAuth === 'AWS' && _.isEmpty(roleARNText)) || + (tokenizedAuth === 'Azure' && + [azureClientId, azureTenantId, azureSubscriptionId].some((v) => _.isEmpty(v))); const formError = () => { return ( @@ -608,24 +637,34 @@ export const OperatorHubSubscribeForm: React.FC = id="enable-monitoring-checkbox" data-test="enable-monitoring" label={t('olm~Enable Operator recommended cluster monitoring on this Namespace')} - onChange={setEnableMonitoring} + onChange={(value) => { + setEnableMonitoring(value); + }} isChecked={enableMonitoring} data-checked-state={enableMonitoring} /> - {props.packageManifest.data[0].metadata.labels['opsrc-provider'] !== 'redhat' && ( + {!props.packageManifest.data[0].metadata.labels.provider?.includes('Red Hat') && ( - - Please note that installing non-Red Hat operators into OpenShift namespaces and - enabling monitoring voids user support. Enabling cluster monitoring for non-Red Hat - operators can lead to malicious metrics data overriding existing cluster metrics. - For more information, see the{' '} - cluster monitoring documentation. - + <> + {t( + 'olm~Please note that installing non-Red Hat operators into OpenShift namespaces and enabling monitoring voids user support. Enabling cluster monitoring for non-Red Hat operators can lead to malicious metrics data overriding existing cluster metrics.', + )} + {!isManaged() && ( + + {' '} + For more information, see the{' '} + + cluster monitoring documentation + + . + + )} + )}
@@ -745,6 +784,21 @@ export const OperatorHubSubscribeForm: React.FC =

)} + {tokenizedAuth === 'Azure' && showSTSWarn && ( + setShowSTSWarn(false)} />} + className="pf-u-mb-lg" + > +

+ {t( + 'olm~This cluster is using Azure Workload Identity / Federated Identity to reach the cloud API. In order for this operator to take the actions it requires directly with the cloud API, provide the Client ID, Tenant ID, and Subscription ID during installation. Manual subscriptions are highly recommended as steps should be taken before upgrade to ensure that the permissions required by the next version are properly accounted for in the role. See the operator description for more details.', + )} +

+
+ )}
<> @@ -770,6 +824,70 @@ export const OperatorHubSubscribeForm: React.FC =
)} + {tokenizedAuth === 'Azure' && ( +
+
+ + + {t( + 'olm~The Azure Client ID required for the operator to access the cloud API.', + )} + +
+ { + setAzureClientId(value); + }} + /> +
+
+
+ + + {t( + 'olm~The Azure Tenant ID required for the operator to access the cloud API.', + )} + +
+ { + setAzureTenantId(value); + }} + /> +
+
+
+ + + {t( + 'olm~The Azure Subscription ID required for the operator to access the cloud API.', + )} + +
+ { + setAzureSubscriptionId(value); + }} + /> +
+
+
+ )}
diff --git a/frontend/packages/operator-lifecycle-manager/src/components/operator-hub/operator-hub-utils.ts b/frontend/packages/operator-lifecycle-manager/src/components/operator-hub/operator-hub-utils.ts index d10763560fb..176e6533709 100644 --- a/frontend/packages/operator-lifecycle-manager/src/components/operator-hub/operator-hub-utils.ts +++ b/frontend/packages/operator-lifecycle-manager/src/components/operator-hub/operator-hub-utils.ts @@ -20,8 +20,6 @@ export const getCatalogSourceDisplayName = (packageManifest: PackageManifestKind ); }; -export const shortLivedTokenAuth = 'Short-lived token authentication'; - export const isAWSSTSCluster = ( cloudcreds: CloudCredentialKind, infra: InfrastructureKind, @@ -33,3 +31,15 @@ export const isAWSSTSCluster = ( auth?.spec?.serviceAccountIssuer !== '' ); }; + +export const isAzureWIFCluster = ( + cloudcreds: CloudCredentialKind, + infra: InfrastructureKind, + auth: AuthenticationKind, +) => { + return ( + cloudcreds?.spec?.credentialsMode === 'Manual' && + infra?.status?.platform === 'Azure' && + auth?.spec?.serviceAccountIssuer !== '' + ); +}; diff --git a/frontend/packages/operator-lifecycle-manager/src/utils/useClusterServiceVersions.tsx b/frontend/packages/operator-lifecycle-manager/src/utils/useClusterServiceVersions.tsx index 8c4bcaf3ffb..5ecc3151868 100644 --- a/frontend/packages/operator-lifecycle-manager/src/utils/useClusterServiceVersions.tsx +++ b/frontend/packages/operator-lifecycle-manager/src/utils/useClusterServiceVersions.tsx @@ -44,6 +44,7 @@ export const ExpandCollapseDescription: React.FC const normalizeClusterServiceVersions = ( clusterServiceVersions: ClusterServiceVersionKind[], + namespace: string, t: TFunction, ): CatalogItem[] => { const formatTileDescription = (csvDescription: string): string => @@ -125,7 +126,7 @@ const normalizeClusterServiceVersions = ( }, cta: { label: t('public~Create'), - href: `/k8s/ns/${desc.csv.metadata.namespace}/clusterserviceversions/${ + href: `/k8s/ns/${namespace}/clusterserviceversions/${ desc.csv.metadata.name }/${referenceForProvidedAPI(desc)}/~new`, }, @@ -175,9 +176,10 @@ const useClusterServiceVersions: ExtensionHook = ({ () => normalizeClusterServiceVersions( [...(csvsResources.csvs?.data ?? []), ...(csvsResources.globalCsvs?.data ?? [])], + namespace, t, ), - [csvsResources, t], + [csvsResources, namespace, t], ); return [normalizedCSVs, csvsResources.csvs?.loaded, csvsResources.csvs?.loadError]; diff --git a/frontend/packages/pipelines-plugin/console-extensions.json b/frontend/packages/pipelines-plugin/console-extensions.json index 4d65b87c48d..a5df0b16625 100644 --- a/frontend/packages/pipelines-plugin/console-extensions.json +++ b/frontend/packages/pipelines-plugin/console-extensions.json @@ -158,7 +158,7 @@ "properties": { "model": { "group": "triggers.tekton.dev", - "version": "v1alpha1", + "version": "v1beta1", "kind": "TriggerBinding" }, "color": "#38812f", @@ -173,7 +173,7 @@ "properties": { "model": { "group": "triggers.tekton.dev", - "version": "v1alpha1", + "version": "v1beta1", "kind": "ClusterTriggerBinding" }, "color": "#38812f", @@ -188,7 +188,7 @@ "properties": { "model": { "group": "triggers.tekton.dev", - "version": "v1alpha1", + "version": "v1beta1", "kind": "TriggerTemplate" }, "color": "#38812f", @@ -203,7 +203,7 @@ "properties": { "model": { "group": "triggers.tekton.dev", - "version": "v1alpha1", + "version": "v1beta1", "kind": "EventListener" }, "color": "#38812f", @@ -281,6 +281,9 @@ "kind": "Pipeline" }, "component": { "$codeRef": "pipelinesComponent.PipelineDetailsPage" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_PIPELINE_DETAILS"] } }, { @@ -292,6 +295,9 @@ "kind": "Pipeline" }, "component": { "$codeRef": "pipelinesComponent.PipelineDetailsPage" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_PIPELINE_DETAILS"] } }, { @@ -316,7 +322,8 @@ "component": { "$codeRef": "pipelinesComponent.PipelineConditionDetailsPage" } }, "flags": { - "required": ["OPENSHIFT_PIPELINE_CONDITION"] + "required": ["OPENSHIFT_PIPELINE_CONDITION"], + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_CONDITIONS_DETAILS"] } }, { @@ -328,6 +335,9 @@ "kind": "PipelineRun" }, "component": { "$codeRef": "pipelinesComponent.PipelineRunDetailsPage" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_PIPELINERUN_DETAILS"] } }, { @@ -339,6 +349,9 @@ "kind": "PipelineRun" }, "component": { "$codeRef": "pipelinesComponent.PipelineRunDetailsPage" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_PIPELINERUN_DETAILS"] } }, { @@ -350,6 +363,9 @@ "kind": "TaskRun" }, "component": { "$codeRef": "tasksComponent.TaskRunDetailsPage" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_TASKRUN_DETAILS"] } }, { @@ -361,6 +377,9 @@ "kind": "TaskRun" }, "component": { "$codeRef": "tasksComponent.TaskRunDetailsPage" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_TASKRUN_DETAILS"] } }, { @@ -372,6 +391,9 @@ "kind": "Repository" }, "component": { "$codeRef": "pipelinesComponent.RepositoryDetailsPage" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_REPOSITORY_DETAILS"] } }, { @@ -383,6 +405,9 @@ "kind": "ClusterTask" }, "component": { "$codeRef": "tasksComponent.ClusterTaskDetailsPage" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_CLUSTERTASK_DETAILS"] } }, { @@ -394,6 +419,9 @@ "kind": "Task" }, "component": { "$codeRef": "tasksComponent.TaskDetailsPage" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_TASK_DETAILS"] } }, { @@ -405,6 +433,9 @@ "kind": "Task" }, "component": { "$codeRef": "tasksComponent.TaskDetailsPage" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_TASK_DETAILS"] } }, { @@ -412,10 +443,13 @@ "properties": { "model": { "group": "triggers.tekton.dev", - "version": "v1alpha1", + "version": "v1beta1", "kind": "EventListener" }, "component": { "$codeRef": "pipelinesComponent.EventListenerPage" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_EVENTLISTENERS_DETAILS"] } }, { @@ -423,10 +457,13 @@ "properties": { "model": { "group": "triggers.tekton.dev", - "version": "v1alpha1", + "version": "v1beta1", "kind": "TriggerTemplate" }, "component": { "$codeRef": "pipelinesComponent.TriggerTemplatePage" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_TRIGGERTEMPLATE_DETAILS"] } }, { @@ -434,10 +471,13 @@ "properties": { "model": { "group": "triggers.tekton.dev", - "version": "v1alpha1", + "version": "v1beta1", "kind": "TriggerBinding" }, "component": { "$codeRef": "pipelinesComponent.TriggerBindingPage" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_TRIGGERBINDING_DETAILS"] } }, { @@ -445,21 +485,13 @@ "properties": { "model": { "group": "triggers.tekton.dev", - "version": "v1alpha1", - "kind": "ClusterTriggerBinding" - }, - "component": { "$codeRef": "pipelinesComponent.ClusterTriggerBindingPage" } - } - }, - { - "type": "console.page/resource/details", - "properties": { - "model": { - "group": "triggers.tekton.dev", - "version": "v1alpha1", + "version": "v1beta1", "kind": "ClusterTriggerBinding" }, "component": { "$codeRef": "pipelinesComponent.ClusterTriggerBindingPage" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_CLUSTERTRIGGERSBINDING_DETAILS"] } }, { @@ -471,6 +503,9 @@ "kind": "Pipeline" }, "component": { "$codeRef": "pipelinesComponent.PipelinesResourceList" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_PIPELINES_LIST"] } }, { @@ -482,6 +517,9 @@ "kind": "PipelineRun" }, "component": { "$codeRef": "pipelinesComponent.PipelineRunsResourceList" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_PIPELINERUNS_LIST"] } }, { @@ -493,6 +531,9 @@ "kind": "Pipeline" }, "component": { "$codeRef": "pipelinesComponent.PipelinesResourceList" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_PIPELINES_LIST"] } }, { @@ -504,6 +545,9 @@ "kind": "PipelineRun" }, "component": { "$codeRef": "pipelinesComponent.PipelineRunsResourceList" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_PIPELINERUNS_LIST"] } }, { @@ -515,6 +559,23 @@ "kind": "TaskRun" }, "component": { "$codeRef": "tasksComponent.TaskRunsListPage" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_TASKRUNS_LIST"] + } + }, + { + "type": "console.page/resource/list", + "properties": { + "model": { + "group": "tekton.dev", + "version": "v1", + "kind": "TaskRun" + }, + "component": { "$codeRef": "tasksComponent.TaskRunsListPage" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_TASKRUNS_LIST"] } }, { @@ -528,7 +589,8 @@ "component": { "$codeRef": "pipelinesComponent.ConditionListPage" } }, "flags": { - "required": ["OPENSHIFT_PIPELINE_CONDITION"] + "required": ["OPENSHIFT_PIPELINE_CONDITION"], + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_CONDITIONS_LIST"] } }, { @@ -540,6 +602,23 @@ "kind": "Task" }, "component": { "$codeRef": "tasksComponent.TaskListPage" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_TASKS_LIST"] + } + }, + { + "type": "console.page/resource/list", + "properties": { + "model": { + "group": "tekton.dev", + "version": "v1", + "kind": "Task" + }, + "component": { "$codeRef": "tasksComponent.TaskListPage" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_TASKS_LIST"] } }, { @@ -551,6 +630,23 @@ "kind": "ClusterTask" }, "component": { "$codeRef": "tasksComponent.ClusterTaskListPage" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_CLUSTERTASKS_LIST"] + } + }, + { + "type": "console.page/resource/list", + "properties": { + "model": { + "group": "tekton.dev", + "version": "v1", + "kind": "ClusterTask" + }, + "component": { "$codeRef": "tasksComponent.ClusterTaskListPage" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_CLUSTERTASKS_LIST"] } }, { @@ -571,6 +667,9 @@ "component": { "$codeRef": "pipelinesComponent.PipelinesListsPage" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_PIPELINE_NAV_OPTION"] } }, { @@ -581,6 +680,9 @@ "component": { "$codeRef": "tasksComponent.TasksListsPage" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_TASKS_NAV_OPTION"] } }, { @@ -591,6 +693,9 @@ "component": { "$codeRef": "triggersComponent.TriggersPage" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_TRIGGERS_NAV_OPTION"] } }, { @@ -615,6 +720,9 @@ "component": { "$codeRef": "pipelinesComponent.PipelinesPage" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_PIPELINES_LIST"] } }, { @@ -627,6 +735,17 @@ } } }, + { + "type": "console.page/route", + "properties": { + "exact": true, + "path": [ + "/k8s/ns/:ns/tekton.dev~v1~PipelineRun/:plrName/logs/:taskName", + "/k8s/ns/:ns/tekton.dev~v1beta1~PipelineRun/:plrName/logs/:taskName" + ], + "component": { "$codeRef": "pipelinesComponent.LogURLRedirect" } + } + }, { "type": "console.page/route", "properties": { @@ -679,6 +798,9 @@ "component": { "$codeRef": "pipelinesComponent.PipelineRunsPage" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_PIPELINERUNS_LIST"] } }, { @@ -788,7 +910,8 @@ } }, "flags": { - "required": ["OPENSHIFT_PIPELINE", "FLAG_TEKTON_V1_ENABLED"] + "required": ["OPENSHIFT_PIPELINE", "FLAG_TEKTON_V1_ENABLED"], + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_PIPELINE_NAV_OPTION"] } }, { @@ -808,7 +931,7 @@ }, "flags": { "required": ["OPENSHIFT_PIPELINE_V1BETA1"], - "disallowed": ["FLAG_TEKTON_V1_ENABLED"] + "disallowed": ["FLAG_TEKTON_V1_ENABLED", "HIDE_STATIC_PIPELINE_PLUGIN_PIPELINE_NAV_OPTION"] } }, { @@ -825,7 +948,8 @@ } }, "flags": { - "required": ["OPENSHIFT_PIPELINE", "FLAG_TEKTON_V1_ENABLED"] + "required": ["OPENSHIFT_PIPELINE", "FLAG_TEKTON_V1_ENABLED"], + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_PIPELINE_NAV_OPTION"] } }, { @@ -839,7 +963,8 @@ "namespaced": true }, "flags": { - "required": ["OPENSHIFT_PIPELINE", "FLAG_TEKTON_V1_ENABLED"] + "required": ["OPENSHIFT_PIPELINE", "FLAG_TEKTON_V1_ENABLED"], + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_TASKS_NAV_OPTION"] } }, { @@ -853,7 +978,8 @@ "namespaced": true }, "flags": { - "required": ["OPENSHIFT_PIPELINE", "FLAG_TEKTON_V1_ENABLED"] + "required": ["OPENSHIFT_PIPELINE", "FLAG_TEKTON_V1_ENABLED"], + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_TRIGGERS_NAV_OPTION"] } }, { @@ -871,7 +997,7 @@ }, "flags": { "required": ["OPENSHIFT_PIPELINE_V1BETA1"], - "disallowed": ["FLAG_TEKTON_V1_ENABLED"] + "disallowed": ["FLAG_TEKTON_V1_ENABLED", "HIDE_STATIC_PIPELINE_PLUGIN_PIPELINE_NAV_OPTION"] } }, { @@ -886,7 +1012,7 @@ }, "flags": { "required": ["OPENSHIFT_PIPELINE_V1BETA1"], - "disallowed": ["FLAG_TEKTON_V1_ENABLED"] + "disallowed": ["FLAG_TEKTON_V1_ENABLED", "HIDE_STATIC_PIPELINE_PLUGIN_TASKS_NAV_OPTION"] } }, { @@ -901,7 +1027,7 @@ }, "flags": { "required": ["OPENSHIFT_PIPELINE_V1BETA1"], - "disallowed": ["FLAG_TEKTON_V1_ENABLED"] + "disallowed": ["FLAG_TEKTON_V1_ENABLED", "HIDE_STATIC_PIPELINE_PLUGIN_TRIGGERS_NAV_OPTION"] } }, { @@ -1060,7 +1186,8 @@ "component": { "$codeRef": "pipelinesComponent.RepositoriesList" } }, "flags": { - "required": ["OPENSHIFT_PIPELINE_AS_CODE"] + "required": ["OPENSHIFT_PIPELINE_AS_CODE"], + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_REPOSITORIES_LIST"] } }, { @@ -1079,5 +1206,41 @@ "flags": { "required": ["OPENSHIFT_PIPELINE"] } + }, + { + "type": "console.tab/horizontalNav", + "properties": { + "model": { + "group": "tekton.dev", + "version": "v1", + "kind": "Pipeline" + }, + "page": { + "name": "%pipelines-plugin~Metrics%", + "href": "metrics" + }, + "component": { "$codeRef": "pipelinesComponent.PipelineMetrics" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_PIPELINE_DETAIL_METRICS_TAB"] + } + }, + { + "type": "console.tab/horizontalNav", + "properties": { + "model": { + "group": "tekton.dev", + "version": "v1beta1", + "kind": "Pipeline" + }, + "page": { + "name": "%pipelines-plugin~Metrics%", + "href": "metrics" + }, + "component": { "$codeRef": "pipelinesComponent.PipelineMetrics" } + }, + "flags": { + "disallowed": ["HIDE_STATIC_PIPELINE_PLUGIN_PIPELINE_DETAIL_METRICS_TAB"] + } } ] diff --git a/frontend/packages/pipelines-plugin/integration-tests/features/pipelines/create-from-builder-page.feature b/frontend/packages/pipelines-plugin/integration-tests/features/pipelines/create-from-builder-page.feature index 8a2b0471718..195df73839f 100644 --- a/frontend/packages/pipelines-plugin/integration-tests/features/pipelines/create-from-builder-page.feature +++ b/frontend/packages/pipelines-plugin/integration-tests/features/pipelines/create-from-builder-page.feature @@ -360,7 +360,8 @@ Feature: Create the pipeline from builder page | pipeline_yaml | pipeline_name | | testData/pipelineWithParameterTypeArray.yaml | pipeline-with-array-parameter | - @regression @odc-7246 + //Issue with install tasks provided by ArtifactHub + @regression @broken-test @odc-7246 Scenario: Install task provided by ArtifactHub and add create a Pipeline: P-02-TC25 Given user is at Pipeline Builder page When user enters pipeline name as "pl-task-from-artifacthub" diff --git a/frontend/packages/pipelines-plugin/integration-tests/testData/petclinic-pipeline-all.yaml b/frontend/packages/pipelines-plugin/integration-tests/testData/petclinic-pipeline-all.yaml index 6aa994fcc5d..533accbff8f 100644 --- a/frontend/packages/pipelines-plugin/integration-tests/testData/petclinic-pipeline-all.yaml +++ b/frontend/packages/pipelines-plugin/integration-tests/testData/petclinic-pipeline-all.yaml @@ -270,18 +270,18 @@ metadata: name: spring-petclinic spec: ports: - - name: 8080-tcp - port: 8080 - protocol: TCP - targetPort: 8080 - - name: 8443-tcp - port: 8443 - protocol: TCP - targetPort: 8443 - - name: 8778-tcp - port: 8778 - protocol: TCP - targetPort: 8778 + - name: 8080-tcp + port: 8080 + protocol: TCP + targetPort: 8080 + - name: 8443-tcp + port: 8443 + protocol: TCP + targetPort: 8443 + - name: 8778-tcp + port: 8778 + protocol: TCP + targetPort: 8778 selector: app: spring-petclinic sessionAffinity: None @@ -309,36 +309,36 @@ spec: app: spring-petclinic spec: containers: - - image: quay.io/siamaksade/spring-petclinic:latest - imagePullPolicy: Always - livenessProbe: - failureThreshold: 3 - httpGet: - path: / - port: 8080 - scheme: HTTP - initialDelaySeconds: 45 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 1 - name: spring-petclinic - ports: - - containerPort: 8080 - protocol: TCP - - containerPort: 8443 - protocol: TCP - - containerPort: 8778 - protocol: TCP - readinessProbe: - failureThreshold: 3 - httpGet: - path: / - port: 8080 - scheme: HTTP - initialDelaySeconds: 45 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 5 + - image: quay.io/siamaksade/spring-petclinic:latest + imagePullPolicy: Always + livenessProbe: + failureThreshold: 3 + httpGet: + path: / + port: 8080 + scheme: HTTP + initialDelaySeconds: 45 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: spring-petclinic + ports: + - containerPort: 8080 + protocol: TCP + - containerPort: 8443 + protocol: TCP + - containerPort: 8778 + protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: / + port: 8080 + scheme: HTTP + initialDelaySeconds: 45 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 --- apiVersion: route.openshift.io/v1 kind: Route @@ -376,81 +376,81 @@ metadata: name: petclinic-deploy spec: params: - - default: spring-petclinic - description: The application deployment name - name: APP_NAME - type: string - - default: https://github.com/siamaksade/spring-petclinic/ - description: The application git repository url - name: APP_GIT_URL - type: string - - default: master - description: The application git repository revision - name: APP_GIT_REVISION - type: string - - default: spring-petclinic:latest - description: The application image stream - name: APP_IMAGE_STREAM - type: string + - default: spring-petclinic + description: The application deployment name + name: APP_NAME + type: string + - default: https://github.com/siamaksade/spring-petclinic/ + description: The application git repository url + name: APP_GIT_URL + type: string + - default: master + description: The application git repository revision + name: APP_GIT_REVISION + type: string + - default: spring-petclinic:latest + description: The application image stream + name: APP_IMAGE_STREAM + type: string tasks: - - name: git-clone - params: - - name: url - value: $(params.APP_GIT_URL) - - name: revision - value: $(params.APP_GIT_REVISION) - - name: deleteExisting - value: "true" - taskRef: - kind: ClusterTask - name: git-clone - workspaces: - - name: output - workspace: app-source - - name: build-jar - params: - - name: GOALS - value: - - package - - -DskipTests - runAfter: - - git-clone - taskRef: - name: maven - workspaces: + - name: git-clone + params: + - name: url + value: $(params.APP_GIT_URL) + - name: revision + value: $(params.APP_GIT_REVISION) + - name: deleteExisting + value: 'true' + taskRef: + kind: ClusterTask + name: git-clone + workspaces: + - name: output + workspace: app-source + - name: build-jar + params: + - name: GOALS + value: + - package + - -DskipTests + runAfter: + - git-clone + taskRef: + name: maven + workspaces: + - name: maven-cache + workspace: maven-cache + - name: maven-settings + workspace: maven-settings + - name: source + workspace: app-source + - name: build-image + params: + - name: TLSVERIFY + value: 'false' + - name: OUTPUT_IMAGE_STREAM + value: $(params.APP_IMAGE_STREAM) + runAfter: + - build-jar + taskRef: + name: s2i-java-11-binary + workspaces: + - name: source + workspace: app-source + - name: deploy + params: + - name: DEPLOYMENT + value: $(params.APP_NAME) + - name: IMAGE_STREAM + value: $(params.APP_IMAGE_STREAM) + runAfter: + - build-image + taskRef: + name: redeploy + workspaces: - name: maven-cache - workspace: maven-cache - name: maven-settings - workspace: maven-settings - - name: source - workspace: app-source - - name: build-image - params: - - name: TLSVERIFY - value: "false" - - name: OUTPUT_IMAGE_STREAM - value: $(params.APP_IMAGE_STREAM) - runAfter: - - build-jar - taskRef: - name: s2i-java-11-binary - workspaces: - - name: source - workspace: app-source - - name: deploy - params: - - name: DEPLOYMENT - value: $(params.APP_NAME) - - name: IMAGE_STREAM - value: $(params.APP_IMAGE_STREAM) - runAfter: - - build-image - taskRef: - name: redeploy - workspaces: - - name: maven-cache - - name: maven-settings - - name: app-source + - name: app-source --- apiVersion: tekton.dev/v1beta1 kind: Task @@ -458,26 +458,26 @@ metadata: name: maven spec: params: - - default: - - package - description: maven goals to run - name: GOALS - type: array + - default: + - package + description: maven goals to run + name: GOALS + type: array steps: - - args: - - -Dmaven.repo.local=$(workspaces.maven-cache.path) - - -s - - $(workspaces.maven-settings.path)/settings.xml - - $(params.GOALS) - command: - - /usr/bin/mvn - image: gcr.io/cloud-builders/mvn - name: mvn - workingDir: $(workspaces.source.path) + - args: + - -Dmaven.repo.local=$(workspaces.maven-cache.path) + - -s + - $(workspaces.maven-settings.path)/settings.xml + - $(params.GOALS) + command: + - /usr/bin/mvn + image: gcr.io/cloud-builders/mvn + name: mvn + workingDir: $(workspaces.source.path) workspaces: - - name: source - - name: maven-cache - - name: maven-settings + - name: source + - name: maven-cache + - name: maven-settings --- apiVersion: tekton.dev/v1beta1 kind: Task @@ -485,28 +485,28 @@ metadata: name: redeploy spec: params: - - name: DEPLOYMENT - type: string - - name: IMAGE_STREAM - type: string + - name: DEPLOYMENT + type: string + - name: IMAGE_STREAM + type: string steps: - - env: - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - image: image-registry.openshift-image-registry.svc:5000/openshift/cli:latest - name: deploy - script: | - #!/usr/bin/env bash + - env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + image: image-registry.openshift-image-registry.svc:5000/openshift/cli:latest + name: deploy + script: | + #!/usr/bin/env bash - image_ref="image-registry.openshift-image-registry.svc:5000/$POD_NAMESPACE/$(params.IMAGE_STREAM)" + image_ref="image-registry.openshift-image-registry.svc:5000/$POD_NAMESPACE/$(params.IMAGE_STREAM)" - echo "Deploying $image_ref" + echo "Deploying $image_ref" - oc set image deployment/$(params.DEPLOYMENT) $(params.DEPLOYMENT)=$image_ref - oc patch deployment $(params.DEPLOYMENT) -p "{\"spec\": {\"template\": {\"metadata\": { \"labels\": { \"redeploy\": \"$(date +%s)\"}}}}}" - oc rollout status deployment/$(params.DEPLOYMENT) + oc set image deployment/$(params.DEPLOYMENT) $(params.DEPLOYMENT)=$image_ref + oc patch deployment $(params.DEPLOYMENT) -p "{\"spec\": {\"template\": {\"metadata\": { \"labels\": { \"redeploy\": \"$(date +%s)\"}}}}}" + oc rollout status deployment/$(params.DEPLOYMENT) --- apiVersion: tekton.dev/v1beta1 kind: Task @@ -514,139 +514,139 @@ metadata: name: s2i-java-11-binary spec: params: - - default: . - description: The location of the path to run s2i from - name: PATH_CONTEXT - type: string - - default: "false" - description: Verify the TLS on the registry endpoint (for push/pull to a non-TLS registry) - name: TLSVERIFY - type: string - - description: The application image url in registry - name: OUTPUT_IMAGE_STREAM - type: string + - default: . + description: The location of the path to run s2i from + name: PATH_CONTEXT + type: string + - default: 'false' + description: Verify the TLS on the registry endpoint (for push/pull to a non-TLS registry) + name: TLSVERIFY + type: string + - description: The application image url in registry + name: OUTPUT_IMAGE_STREAM + type: string stepTemplate: env: - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace steps: - - command: - - s2i - - build - - $(params.PATH_CONTEXT) - - registry.access.redhat.com/openjdk/openjdk-11-rhel7 - - --image-scripts-url - - image:///usr/local/s2i - - --as-dockerfile - - /gen-source/Dockerfile.gen - image: registry.redhat.io/ocp-tools-43-tech-preview/source-to-image-rhel8 - name: generate - volumeMounts: - - mountPath: /env-params - name: envparams - - mountPath: /gen-source - name: gen-source - workingdir: $(workspaces.source.path)/target - - command: - - buildah - - bud - - --tls-verify=$(params.TLSVERIFY) - - --layers - - -f - - /gen-source/Dockerfile.gen - - -t - - image-registry.openshift-image-registry.svc:5000/$(POD_NAMESPACE)/$(params.OUTPUT_IMAGE_STREAM) - - . - image: registry.redhat.io/rhel8/buildah - name: build - securityContext: - privileged: true - volumeMounts: - - mountPath: /var/lib/containers + - command: + - s2i + - build + - $(params.PATH_CONTEXT) + - registry.access.redhat.com/openjdk/openjdk-11-rhel7 + - --image-scripts-url + - image:///usr/local/s2i + - --as-dockerfile + - /gen-source/Dockerfile.gen + image: registry.redhat.io/ocp-tools-43-tech-preview/source-to-image-rhel8 + name: generate + volumeMounts: + - mountPath: /env-params + name: envparams + - mountPath: /gen-source + name: gen-source + workingdir: $(workspaces.source.path)/target + - command: + - buildah + - bud + - --tls-verify=$(params.TLSVERIFY) + - --layers + - -f + - /gen-source/Dockerfile.gen + - -t + - image-registry.openshift-image-registry.svc:5000/$(POD_NAMESPACE)/$(params.OUTPUT_IMAGE_STREAM) + - . + image: registry.redhat.io/rhel8/buildah + name: build + securityContext: + privileged: true + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + - mountPath: /gen-source + name: gen-source + workingdir: /gen-source + - command: + - buildah + - push + - --tls-verify=$(params.TLSVERIFY) + - image-registry.openshift-image-registry.svc:5000/$(POD_NAMESPACE)/$(params.OUTPUT_IMAGE_STREAM) + - docker://image-registry.openshift-image-registry.svc:5000/$(POD_NAMESPACE)/$(params.OUTPUT_IMAGE_STREAM) + image: registry.redhat.io/rhel8/buildah + name: push + securityContext: + privileged: true + volumeMounts: + - mountPath: /var/lib/containers + name: varlibcontainers + volumes: + - emptyDir: {} name: varlibcontainers - - mountPath: /gen-source + - emptyDir: {} name: gen-source - workingdir: /gen-source - - command: - - buildah - - push - - --tls-verify=$(params.TLSVERIFY) - - image-registry.openshift-image-registry.svc:5000/$(POD_NAMESPACE)/$(params.OUTPUT_IMAGE_STREAM) - - docker://image-registry.openshift-image-registry.svc:5000/$(POD_NAMESPACE)/$(params.OUTPUT_IMAGE_STREAM) - image: registry.redhat.io/rhel8/buildah - name: push - securityContext: - privileged: true - volumeMounts: - - mountPath: /var/lib/containers - name: varlibcontainers - volumes: - - emptyDir: {} - name: varlibcontainers - - emptyDir: {} - name: gen-source - - emptyDir: {} - name: envparams + - emptyDir: {} + name: envparams workspaces: - - name: source + - name: source --- -apiVersion: triggers.tekton.dev/v1alpha1 +apiVersion: triggers.tekton.dev/v1beta1 kind: EventListener metadata: name: petclinic-event-listener spec: serviceAccountName: pipeline triggers: - - bindings: - - kind: ClusterTriggerBinding - ref: github-push - template: - name: trigger-template-petclinic-deploy + - bindings: + - kind: ClusterTriggerBinding + ref: github-push + template: + name: trigger-template-petclinic-deploy --- -apiVersion: triggers.tekton.dev/v1alpha1 +apiVersion: triggers.tekton.dev/v1beta1 kind: TriggerTemplate metadata: name: trigger-template-petclinic-deploy spec: params: - - name: git-revision - - name: git-commit-message - - name: git-repo-url - - name: git-repo-name - - name: content-type - - name: pusher-name + - name: git-revision + - name: git-commit-message + - name: git-repo-url + - name: git-repo-name + - name: content-type + - name: pusher-name resourcetemplates: - - apiVersion: tekton.dev/v1beta1 - kind: PipelineRun - metadata: - labels: - tekton.dev/pipeline: petclinic-deploy - name: petclinic-deploy-$(uid) - namespace: demo - spec: - params: - - name: APP_NAME - value: spring-petclinic - - name: APP_GIT_URL - value: https://github.com/siamaksade/spring-petclinic/ - - name: APP_GIT_REVISION - value: $(params.git-revision) - - name: APP_IMAGE_STREAM - value: spring-petclinic:$(params.git-revision) - pipelineRef: - name: petclinic-deploy - workspaces: - - name: app-source - persistentVolumeClaim: - claimName: app-source-pvc - - name: maven-cache - persistentVolumeClaim: - claimName: maven-cache-pvc - - configMap: - name: maven-settings - name: maven-settings + - apiVersion: tekton.dev/v1beta1 + kind: PipelineRun + metadata: + labels: + tekton.dev/pipeline: petclinic-deploy + name: petclinic-deploy-$(uid) + namespace: demo + spec: + params: + - name: APP_NAME + value: spring-petclinic + - name: APP_GIT_URL + value: https://github.com/siamaksade/spring-petclinic/ + - name: APP_GIT_REVISION + value: $(params.git-revision) + - name: APP_IMAGE_STREAM + value: spring-petclinic:$(params.git-revision) + pipelineRef: + name: petclinic-deploy + workspaces: + - name: app-source + persistentVolumeClaim: + claimName: app-source-pvc + - name: maven-cache + persistentVolumeClaim: + claimName: maven-cache-pvc + - configMap: + name: maven-settings + name: maven-settings --- apiVersion: v1 kind: PersistentVolumeClaim @@ -654,7 +654,7 @@ metadata: name: app-source-pvc spec: accessModes: - - ReadWriteOnce + - ReadWriteOnce persistentVolumeReclaimPolicy: Recycle resources: requests: @@ -667,9 +667,9 @@ metadata: name: maven-cache-pvc spec: accessModes: - - ReadWriteOnce + - ReadWriteOnce persistentVolumeReclaimPolicy: Retain resources: requests: storage: 5Gi - volumeMode: Filesystem \ No newline at end of file + volumeMode: Filesystem diff --git a/frontend/packages/pipelines-plugin/integration-tests/testData/repository-crd-testdata/pac-installation-release0.1.yaml b/frontend/packages/pipelines-plugin/integration-tests/testData/repository-crd-testdata/pac-installation-release0.1.yaml index dd32050a57e..e394cb5eed0 100644 --- a/frontend/packages/pipelines-plugin/integration-tests/testData/repository-crd-testdata/pac-installation-release0.1.yaml +++ b/frontend/packages/pipelines-plugin/integration-tests/testData/repository-crd-testdata/pac-installation-release0.1.yaml @@ -55,24 +55,24 @@ metadata: app.kubernetes.io/instance: default app.kubernetes.io/part-of: pipelines-as-code rules: - - apiGroups: ["triggers.tekton.dev"] - resources: ["eventlisteners", "triggerbindings", "triggertemplates", "triggers"] - verbs: ["get", "list", "watch"] - - apiGroups: [""] + - apiGroups: ['triggers.tekton.dev'] + resources: ['eventlisteners', 'triggerbindings', 'triggertemplates', 'triggers'] + verbs: ['get', 'list', 'watch'] + - apiGroups: [''] # secrets are only needed for Github/Gitlab interceptors, serviceaccounts only for per trigger authorization - resources: ["configmaps", "secrets"] - verbs: ["get", "list", "watch"] + resources: ['configmaps', 'secrets'] + verbs: ['get', 'list', 'watch'] # Permissions to create resources in associated TriggerTemplates - - apiGroups: ["tekton.dev"] - resources: ["pipelineruns", "taskruns"] - verbs: ["create"] - - apiGroups: [""] - resources: ["serviceaccounts"] - verbs: ["impersonate"] - - apiGroups: ["policy"] - resources: ["podsecuritypolicies"] - resourceNames: ["tekton-triggers"] - verbs: ["use"] + - apiGroups: ['tekton.dev'] + resources: ['pipelineruns', 'taskruns'] + verbs: ['create'] + - apiGroups: [''] + resources: ['serviceaccounts'] + verbs: ['impersonate'] + - apiGroups: ['policy'] + resources: ['podsecuritypolicies'] + resourceNames: ['tekton-triggers'] + verbs: ['use'] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -101,25 +101,25 @@ metadata: app.kubernetes.io/part-of: pipelines-as-code rules: # Permissions to list repositories on cluster - - apiGroups: [""] - resources: ["namespaces", "pods", "pods/log"] - verbs: ["get", "list", "watch"] + - apiGroups: [''] + resources: ['namespaces', 'pods', 'pods/log'] + verbs: ['get', 'list', 'watch'] # Permissions to list repositories on cluster - - apiGroups: ["pipelinesascode.tekton.dev"] - resources: ["repositories"] - verbs: ["get", "list", "update"] - - apiGroups: ["triggers.tekton.dev"] - resources: ["clustertriggerbindings", "clusterinterceptors"] - verbs: ["get", "list", "watch"] - - apiGroups: ["tekton.dev"] - resources: ["pipelineruns"] - verbs: ["get", "delete", "list", "create", "watch"] - - apiGroups: ["tekton.dev"] - resources: ["taskruns"] - verbs: ["get"] - - apiGroups: ["route.openshift.io"] - resources: ["routes"] - verbs: ["get"] + - apiGroups: ['pipelinesascode.tekton.dev'] + resources: ['repositories'] + verbs: ['get', 'list', 'update'] + - apiGroups: ['triggers.tekton.dev'] + resources: ['clustertriggerbindings', 'clusterinterceptors'] + verbs: ['get', 'list', 'watch'] + - apiGroups: ['tekton.dev'] + resources: ['pipelineruns'] + verbs: ['get', 'delete', 'list', 'create', 'watch'] + - apiGroups: ['tekton.dev'] + resources: ['taskruns'] + verbs: ['get'] + - apiGroups: ['route.openshift.io'] + resources: ['routes'] + verbs: ['get'] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -130,9 +130,9 @@ metadata: app.kubernetes.io/instance: default app.kubernetes.io/part-of: pipelines-as-code subjects: -- kind: ServiceAccount - name: pipelines-as-code-sa-el - namespace: pipelines-as-code + - kind: ServiceAccount + name: pipelines-as-code-sa-el + namespace: pipelines-as-code roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -177,16 +177,16 @@ spec: type: string - name: Succeeded type: string - jsonPath: ".pipelinerun_status[-1].conditions[?(@.type==\"Succeeded\")].status" + jsonPath: '.pipelinerun_status[-1].conditions[?(@.type=="Succeeded")].status' - name: Reason type: string - jsonPath: ".pipelinerun_status[-1].conditions[?(@.type==\"Succeeded\")].reason" + jsonPath: '.pipelinerun_status[-1].conditions[?(@.type=="Succeeded")].reason' - name: StartTime type: date - jsonPath: ".pipelinerun_status[-1].startTime" + jsonPath: '.pipelinerun_status[-1].startTime' - name: CompletionTime type: date - jsonPath: ".pipelinerun_status[-1].completionTime" + jsonPath: '.pipelinerun_status[-1].completionTime' served: true storage: true schema: @@ -229,7 +229,7 @@ spec: singular: repository kind: Repository shortNames: - - repo + - repo --- # Copyright 2021 Red Hat # @@ -248,10 +248,10 @@ spec: apiVersion: v1 data: # The number of days kept for pipelinerun inside pipelines-as-code namespace - max-keep-days: "5" + max-keep-days: '5' # The application name, you can customize this label - application-name: "Pipelines as Code" + application-name: 'Pipelines as Code' kind: ConfigMap metadata: name: pipelines-as-code @@ -288,25 +288,25 @@ spec: template: spec: containers: - - command: - - /bin/bash - - -c - - test -e /etc/config/max-keep-days && export MAX_DAY_KEEP=$(cat /etc/config/max-keep-days);for pr in $(kubectl get pipelineruns -l app.kubernetes.io/managed-by=pipelines-as-code -o json | python3 -c "import - os, sys, datetime, json;jeez=json.load(sys.stdin);res=[ i['metadata']['name'] - for i in jeez['items'] if datetime.datetime.now() > datetime.datetime.strptime(i['metadata']['creationTimestamp'], - '%Y-%m-%dT%H:%M:%SZ') + datetime.timedelta(days=int(os.environ.get('MAX_DAY_KEEP', 1))) ];print(' '.join(res))");do - kubectl delete pipelinerun ${pr};done - image: quay.io/openshift/origin-cli:4.8 - imagePullPolicy: IfNotPresent - name: cleanup - terminationMessagePath: /dev/termination-log - terminationMessagePolicy: File - volumeMounts: - - name: config-volume - mountPath: /etc/config - env: - - name: MAX_DAY_KEEP - value: "1" + - command: + - /bin/bash + - -c + - test -e /etc/config/max-keep-days && export MAX_DAY_KEEP=$(cat /etc/config/max-keep-days);for pr in $(kubectl get pipelineruns -l app.kubernetes.io/managed-by=pipelines-as-code -o json | python3 -c "import + os, sys, datetime, json;jeez=json.load(sys.stdin);res=[ i['metadata']['name'] + for i in jeez['items'] if datetime.datetime.now() > datetime.datetime.strptime(i['metadata']['creationTimestamp'], + '%Y-%m-%dT%H:%M:%SZ') + datetime.timedelta(days=int(os.environ.get('MAX_DAY_KEEP', 1))) ];print(' '.join(res))");do + kubectl delete pipelinerun ${pr};done + image: quay.io/openshift/origin-cli:4.8 + imagePullPolicy: IfNotPresent + name: cleanup + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - name: config-volume + mountPath: /etc/config + env: + - name: MAX_DAY_KEEP + value: '1' dnsPolicy: ClusterFirst restartPolicy: Never schedulerName: default-scheduler @@ -318,7 +318,7 @@ spec: configMap: name: pipelines-as-code optional: true - schedule: "0 * * * *" + schedule: '0 * * * *' successfulJobsHistoryLimit: 1 suspend: false --- @@ -336,7 +336,7 @@ spec: # See the License for the specific language governing permissions and # limitations under the License. -apiVersion: triggers.tekton.dev/v1alpha1 +apiVersion: triggers.tekton.dev/v1beta1 kind: EventListener metadata: name: pipelines-as-code-interceptor @@ -354,8 +354,8 @@ spec: interceptors: - github: secretRef: - secretName: "github-app-secret" - secretKey: "webhook.secret" + secretName: 'github-app-secret' + secretKey: 'webhook.secret' eventTypes: - issue_comment - cel: @@ -375,8 +375,8 @@ spec: interceptors: - github: secretRef: - secretName: "github-app-secret" - secretKey: "webhook.secret" + secretName: 'github-app-secret' + secretKey: 'webhook.secret' eventTypes: - issue_comment - cel: @@ -397,8 +397,8 @@ spec: interceptors: - github: secretRef: - secretName: "github-app-secret" - secretKey: "webhook.secret" + secretName: 'github-app-secret' + secretKey: 'webhook.secret' eventTypes: - push - cel: @@ -415,8 +415,8 @@ spec: interceptors: - github: secretRef: - secretName: "github-app-secret" - secretKey: "webhook.secret" + secretName: 'github-app-secret' + secretKey: 'webhook.secret' eventTypes: - check_run - cel: @@ -432,8 +432,8 @@ spec: interceptors: - github: secretRef: - secretName: "github-app-secret" - secretKey: "webhook.secret" + secretName: 'github-app-secret' + secretKey: 'webhook.secret' eventTypes: - pull_request - cel: @@ -443,7 +443,7 @@ spec: ref: pipelines-as-code-template-pullreq # Triggers >0.13 Changed almost everything for EventListener # --- -# apiVersion: triggers.tekton.dev/v1alpha1 +# apiVersion: triggers.tekton.dev/v1beta1 # kind: EventListener # metadata: # name: pipelines-as-code-interceptor @@ -492,7 +492,7 @@ spec: # See the License for the specific language governing permissions and # limitations under the License. -apiVersion: triggers.tekton.dev/v1alpha1 +apiVersion: triggers.tekton.dev/v1beta1 kind: TriggerBinding metadata: name: pipelines-as-code-bindings-recheck @@ -509,10 +509,10 @@ spec: - name: head_sha value: $(body.check_run.check_suite.head_sha) - name: trigger_target - value: "issue-recheck" + value: 'issue-recheck' - name: event_type value: $(header.X-GitHub-Event) - - name: "ghe_host" + - name: 'ghe_host' value: $(header.X-GitHub-Enterprise-Host) - name: owner value: $(body.repository.owner.login) @@ -530,7 +530,7 @@ spec: value: $(body.installation.id) --- -apiVersion: triggers.tekton.dev/v1alpha1 +apiVersion: triggers.tekton.dev/v1beta1 kind: TriggerTemplate metadata: name: pipelines-as-code-template-recheck @@ -553,7 +553,7 @@ spec: - name: installation_id - name: trigger_target - name: ghe_host - default: "api.github.com" + default: 'api.github.com' resourcetemplates: - apiVersion: tekton.dev/v1beta1 kind: PipelineRun @@ -644,7 +644,7 @@ spec: type: string - name: pull_request_number type: string - default: "000" + default: '000' - name: sender type: string - name: token @@ -654,13 +654,13 @@ spec: steps: - name: apply-and-launch imagePullPolicy: Always - image: "quay.io/openshift-pipeline/pipelines-as-code:0.1" + image: 'quay.io/openshift-pipeline/pipelines-as-code:0.1' env: - - name: PAC_APPLICATION_NAME - valueFrom: - configMapKeyRef: - name: pipelines-as-code - key: application-name + - name: PAC_APPLICATION_NAME + valueFrom: + configMapKeyRef: + name: pipelines-as-code + key: application-name script: | #!/usr/bin/env bash set -eux @@ -698,29 +698,29 @@ spec: - name: ghe_host value: $(params.ghe_host) - name: action - value: "$(params.action)" + value: '$(params.action)' - name: head_branch - value: "$(params.head_branch)" + value: '$(params.head_branch)' - name: head_sha - value: "$(params.head_sha)" + value: '$(params.head_sha)' - name: trigger_target - value: "$(params.trigger_target)" + value: '$(params.trigger_target)' - name: event_type - value: "$(params.event_type)" + value: '$(params.event_type)' - name: owner - value: "$(params.owner)" + value: '$(params.owner)' - name: url - value: "$(params.url)" + value: '$(params.url)' - name: repository - value: "$(params.repository)" + value: '$(params.repository)' - name: default_branch - value: "$(params.default_branch)" + value: '$(params.default_branch)' - name: pull_request_number - value: "$(params.pull_request_number)" + value: '$(params.pull_request_number)' - name: sender - value: "$(params.sender)" + value: '$(params.sender)' - name: token - value: "$(tasks.get-token.results.token)" + value: '$(tasks.get-token.results.token)' workspaces: - name: secrets secret: @@ -740,7 +740,7 @@ spec: # See the License for the specific language governing permissions and # limitations under the License. -apiVersion: triggers.tekton.dev/v1alpha1 +apiVersion: triggers.tekton.dev/v1beta1 kind: TriggerBinding metadata: name: pipelines-as-code-bindings-pullreq @@ -751,32 +751,32 @@ metadata: spec: params: - name: trigger_target - value: "pull-request" - - name: "event_type" + value: 'pull-request' + - name: 'event_type' value: $(header.X-GitHub-Event) - - name: "ghe_host" + - name: 'ghe_host' value: $(header.X-GitHub-Enterprise-Host) - - name: "owner" + - name: 'owner' value: $(body.repository.owner.login) - - name: "repository" + - name: 'repository' value: $(body.repository.name) - - name: "default_branch" + - name: 'default_branch' value: $(body.repository.default_branch) - - name: "url" + - name: 'url' value: $(body.repository.html_url) - - name: "sender" + - name: 'sender' value: $(body.pull_request.user.login) - - name: "base_ref" + - name: 'base_ref' value: $(body.pull_request.base.ref) - - name: "sha" + - name: 'sha' value: $(body.pull_request.head.sha) - - name: "head_ref" + - name: 'head_ref' value: $(body.pull_request.head.ref) - - name: "installation_id" + - name: 'installation_id' value: $(body.installation.id) --- -apiVersion: triggers.tekton.dev/v1alpha1 +apiVersion: triggers.tekton.dev/v1beta1 kind: TriggerTemplate metadata: name: pipelines-as-code-template-pullreq @@ -798,7 +798,7 @@ spec: - name: installation_id - name: trigger_target - name: ghe_host - default: "api.github.com" + default: 'api.github.com' resourcetemplates: - apiVersion: tekton.dev/v1beta1 kind: PipelineRun @@ -893,13 +893,13 @@ spec: steps: - name: apply-and-launch env: - - name: PAC_APPLICATION_NAME - valueFrom: - configMapKeyRef: - name: pipelines-as-code - key: application-name + - name: PAC_APPLICATION_NAME + valueFrom: + configMapKeyRef: + name: pipelines-as-code + key: application-name imagePullPolicy: Always - image: "quay.io/openshift-pipeline/pipelines-as-code:0.1" + image: 'quay.io/openshift-pipeline/pipelines-as-code:0.1' script: | cat << EOF > /tmp/payload.json { @@ -954,7 +954,7 @@ spec: - name: head_ref value: $(params.head_ref) - name: token - value: "$(tasks.get-token.results.token)" + value: '$(tasks.get-token.results.token)' workspaces: - name: secrets @@ -975,7 +975,7 @@ spec: # See the License for the specific language governing permissions and # limitations under the License. -apiVersion: triggers.tekton.dev/v1alpha1 +apiVersion: triggers.tekton.dev/v1beta1 kind: TriggerBinding metadata: name: pipelines-as-code-bindings-push @@ -985,33 +985,33 @@ metadata: app.kubernetes.io/part-of: pipelines-as-code spec: params: - - name: "event_type" + - name: 'event_type' value: $(header.X-GitHub-Event) - - name: "ghe_host" + - name: 'ghe_host' value: $(header.X-GitHub-Enterprise-Host) - - name: "trigger_target" - value: "push" - - name: "owner" + - name: 'trigger_target' + value: 'push' + - name: 'owner' value: $(body.repository.owner.login) - - name: "repository" + - name: 'repository' value: $(body.repository.name) - - name: "default_branch" + - name: 'default_branch' value: $(body.repository.default_branch) - - name: "sha" + - name: 'sha' value: $(body.head_commit.id) - - name: "url" + - name: 'url' value: $(body.repository.html_url) - - name: "sender" + - name: 'sender' value: $(body.sender.login) - - name: "base_ref" + - name: 'base_ref' value: $(body.ref) - - name: "head_ref" # head ref is the same as base ref + - name: 'head_ref' # head ref is the same as base ref value: $(body.ref) - - name: "installation_id" + - name: 'installation_id' value: $(body.installation.id) --- -apiVersion: triggers.tekton.dev/v1alpha1 +apiVersion: triggers.tekton.dev/v1beta1 kind: TriggerTemplate metadata: name: pipelines-as-code-template-push @@ -1023,7 +1023,7 @@ spec: params: - name: event_type - name: ghe_host - default: "api.github.com" + default: 'api.github.com' - name: owner - name: repository - name: default_branch @@ -1128,13 +1128,13 @@ spec: steps: - name: apply-and-launch imagePullPolicy: Always - image: "quay.io/openshift-pipeline/pipelines-as-code:0.1" + image: 'quay.io/openshift-pipeline/pipelines-as-code:0.1' env: - - name: PAC_APPLICATION_NAME - valueFrom: - configMapKeyRef: - name: pipelines-as-code - key: application-name + - name: PAC_APPLICATION_NAME + valueFrom: + configMapKeyRef: + name: pipelines-as-code + key: application-name script: | cat << EOF > /tmp/payload.json { @@ -1182,7 +1182,7 @@ spec: - name: head_ref value: $(params.head_ref) - name: token - value: "$(tasks.get-token.results.token)" + value: '$(tasks.get-token.results.token)' workspaces: - name: secrets @@ -1203,7 +1203,7 @@ spec: # See the License for the specific language governing permissions and # limitations under the License. -apiVersion: triggers.tekton.dev/v1alpha1 +apiVersion: triggers.tekton.dev/v1beta1 kind: TriggerBinding metadata: name: pipelines-as-code-bindings-retest-comment @@ -1214,12 +1214,12 @@ metadata: spec: params: - name: trigger_target - value: "retest-comment" + value: 'retest-comment' - name: action value: $(body.action) - name: event_type value: $(header.X-GitHub-Event) - - name: "ghe_host" + - name: 'ghe_host' value: $(header.X-GitHub-Enterprise-Host) - name: owner value: $(body.repository.owner.login) @@ -1232,7 +1232,7 @@ spec: - name: installation_id value: $(body.installation.id) --- -apiVersion: triggers.tekton.dev/v1alpha1 +apiVersion: triggers.tekton.dev/v1beta1 kind: TriggerTemplate metadata: name: pipelines-as-code-template-retest-comment @@ -1251,7 +1251,7 @@ spec: - name: installation_id - name: trigger_target - name: ghe_host - default: "api.github.com" + default: 'api.github.com' resourcetemplates: - apiVersion: tekton.dev/v1beta1 kind: PipelineRun @@ -1331,13 +1331,13 @@ spec: steps: - name: apply-and-launch imagePullPolicy: Always - image: "quay.io/openshift-pipeline/pipelines-as-code:0.1" + image: 'quay.io/openshift-pipeline/pipelines-as-code:0.1' env: - - name: PAC_APPLICATION_NAME - valueFrom: - configMapKeyRef: - name: pipelines-as-code - key: application-name + - name: PAC_APPLICATION_NAME + valueFrom: + configMapKeyRef: + name: pipelines-as-code + key: application-name script: | env env|grep PAC_APPLICATION_NAME @@ -1365,23 +1365,23 @@ spec: --payload-file=/tmp/payload.json --token="$(params.token)" --webhook-type="$(params.event_type)" params: - name: action - value: "$(params.action)" + value: '$(params.action)' - name: ghe_host - value: "$(params.ghe_host)" + value: '$(params.ghe_host)' - name: trigger_target - value: "$(params.trigger_target)" + value: '$(params.trigger_target)' - name: event_type - value: "$(params.event_type)" + value: '$(params.event_type)' - name: owner - value: "$(params.owner)" + value: '$(params.owner)' - name: sender - value: "$(params.sender)" + value: '$(params.sender)' - name: repository - value: "$(params.repository)" + value: '$(params.repository)' - name: pull_request_url - value: "$(params.pull_request_url)" + value: '$(params.pull_request_url)' - name: token - value: "$(tasks.get-token.results.token)" + value: '$(tasks.get-token.results.token)' workspaces: - name: secrets secret: @@ -1401,7 +1401,7 @@ spec: # See the License for the specific language governing permissions and # limitations under the License. -apiVersion: triggers.tekton.dev/v1alpha1 +apiVersion: triggers.tekton.dev/v1beta1 kind: TriggerBinding metadata: name: pipelines-as-code-bindings-ok-to-test-comment @@ -1412,12 +1412,12 @@ metadata: spec: params: - name: action - value: "$(body.action)" + value: '$(body.action)' - name: trigger_target - value: "ok-to-test-comment" + value: 'ok-to-test-comment' - name: event_type value: $(header.X-GitHub-Event) - - name: "ghe_host" + - name: 'ghe_host' value: $(header.X-GitHub-Enterprise-Host) - name: owner value: $(body.repository.owner.login) @@ -1454,11 +1454,11 @@ metadata: labels: app.kubernetes.io/instance: default app.kubernetes.io/part-of: pipelines-as-code - app.kubernetes.io/version: "0.2" + app.kubernetes.io/version: '0.2' annotations: - tekton.dev/pipelines.minVersion: "0.12.1" + tekton.dev/pipelines.minVersion: '0.12.1' tekton.dev/tags: github - tekton.dev/displayName: "github app token" + tekton.dev/displayName: 'github app token' spec: description: >- Retrieve a user token from a GitHub application @@ -1481,27 +1481,27 @@ spec: - name: application_id description: The application id for the GitHub application to request a token for. type: string - default: "api.github.com" + default: 'api.github.com' - name: private_key_path description: The key path inside the secret workspace type: string - default: "private.key" + default: 'private.key' - name: application_id_path description: The path for the application id inside the secret workspace type: string - default: "application_id" + default: 'application_id' - name: token_expiration_minutes description: Token expiration time in minutes type: string - default: "10" + default: '10' - name: github_api_url description: GitHUB API URL endpoint type: string - default: "api.github.com" + default: 'api.github.com' steps: - name: get-token @@ -1613,4 +1613,4 @@ spec: open(os.environ.get('GITHUBAPP_RESULT_PATH'), 'w').write(github_app.token) if __name__ == '__main__': - main() \ No newline at end of file + main() diff --git a/frontend/packages/pipelines-plugin/locales/en/pipelines-plugin.json b/frontend/packages/pipelines-plugin/locales/en/pipelines-plugin.json index ccc6198a407..337f6eb2014 100644 --- a/frontend/packages/pipelines-plugin/locales/en/pipelines-plugin.json +++ b/frontend/packages/pipelines-plugin/locales/en/pipelines-plugin.json @@ -29,6 +29,7 @@ "Browse for openshift pipeline tasks available in the cluster.": "Browse for openshift pipeline tasks available in the cluster.", "Community": "Community", "Browse tekton hub tasks.": "Browse tekton hub tasks.", + "Metrics": "Metrics", "Add": "Add", "{{clusterTaskLabel}} details": "{{clusterTaskLabel}} details", "Once your Pipeline Repository is configured, in order to update your {{translatedResourceName}} <4>{{name}} automatically, update the following in your <6>.tekton PipelineRun:": "Once your Pipeline Repository is configured, in order to update your {{translatedResourceName}} <4>{{name}} automatically, update the following in your <6>.tekton PipelineRun:", @@ -323,7 +324,6 @@ "Operator": "Operator", "Select operator": "Select operator", "Values": "Values", - "Metrics": "Metrics", "Select a Project to view the list of Pipelines<1>.": "Select a Project to view the list of Pipelines<1>.", "Create Pipeline": "Create Pipeline", "by name": "by name", diff --git a/frontend/packages/pipelines-plugin/src/components/LogURLRedirect.tsx b/frontend/packages/pipelines-plugin/src/components/LogURLRedirect.tsx new file mode 100644 index 00000000000..9abbf223e09 --- /dev/null +++ b/frontend/packages/pipelines-plugin/src/components/LogURLRedirect.tsx @@ -0,0 +1,15 @@ +import * as React from 'react'; +import { Redirect, RouteComponentProps } from 'react-router-dom'; + +type LogURLRedirectProps = RouteComponentProps<{ ns: string; taskName: string; plrName: string }>; + +const createLogURL = (pathname: string, taskName: string): string => { + const basePath = pathname.replace(/\/$/, ''); + const detailsURL = basePath.split('/logs/'); + return `${detailsURL[0]}/logs?taskName=${taskName}`; +}; + +export const LogURLRedirect: React.FC = ({ match }) => { + const { taskName } = match.params; + return ; +}; diff --git a/frontend/packages/pipelines-plugin/src/components/catalog/apis/artifactHub.ts b/frontend/packages/pipelines-plugin/src/components/catalog/apis/artifactHub.ts index 5fbb723b31a..2b1b0971596 100644 --- a/frontend/packages/pipelines-plugin/src/components/catalog/apis/artifactHub.ts +++ b/frontend/packages/pipelines-plugin/src/components/catalog/apis/artifactHub.ts @@ -2,59 +2,23 @@ import * as React from 'react'; import * as _ from 'lodash'; import { CatalogItem } from '@console/dynamic-plugin-sdk'; import { K8sResourceKind, k8sCreate, k8sUpdate } from '@console/internal/module/k8s'; -import { consoleProxyFetchJSON } from '@console/shared/src/utils/proxy'; -import { ARTIFACTHUB_API_BASE_URL } from '../../../const'; +import { GITHUB_BASE_URL } from '../../../const'; import { TaskModel, TaskModelV1Beta1 } from '../../../models'; import { TektonTaskAnnotation } from '../../pipelines/const'; import { ARTIFACTHUB } from '../../quicksearch/const'; import { ApiResult } from '../hooks/useApiResponse'; - -export type ArtifactHubRepository = { - name: string; - kind: number; - url: string; - display_name: string; - repository_id: string; - organization_name: string; - organization_display_name: string; -}; - -export type ArtifactHubVersion = { - version: string; - contains_security_update: boolean; - prerelease: boolean; - ts: number; -}; - -export type ArtifactHubTask = { - package_id: string; - name: string; - description: string; - version: string; - display_name: string; - repository: ArtifactHubRepository; -}; - -export type ArtifactHubTaskDetails = { - package_id: string; - name: string; - description: string; - display_name: string; - keywords: string[]; - platforms: string[]; - version: ArtifactHubVersion[]; - available_versions: []; - content_url: string; - repository: ArtifactHubRepository; -}; - -const ARTIFACRHUB_TASKS_SEARCH_URL = `${ARTIFACTHUB_API_BASE_URL}/packages/search?offset=0&limit=60&facets=false&kind=7&deprecated=false&sort=relevance`; +import { + ArtifactHubTask, + ArtifactHubTaskDetails, + getTaskDetails, + getTaskYAMLFromGithub, + searchTasks, +} from './utils'; export const getArtifactHubTaskDetails = async ( item: CatalogItem, v?: string, ): Promise => { - const API_BASE_URL = `${ARTIFACTHUB_API_BASE_URL}/packages/tekton-task`; const { name, data } = item; const { task: { @@ -62,8 +26,8 @@ export const getArtifactHubTaskDetails = async ( repository: { name: repoName }, }, } = data; - const url = `${API_BASE_URL}/${repoName}/${name}/${v || version}`; - return consoleProxyFetchJSON({ url, method: 'GET' }); + + return getTaskDetails({ repoName, name, version: v || version }); }; export const useGetArtifactHubTasks = (hasPermission: boolean): ApiResult => { @@ -74,11 +38,8 @@ export const useGetArtifactHubTasks = (hasPermission: boolean): ApiResult { let mounted = true; if (hasPermission) { - consoleProxyFetchJSON<{ packages: ArtifactHubTask[] }>({ - url: ARTIFACRHUB_TASKS_SEARCH_URL, - method: 'GET', - }) - .then(({ packages }) => { + searchTasks() + .then((packages) => { if (mounted) { setLoaded(true); setResult(packages); @@ -101,7 +62,12 @@ export const useGetArtifactHubTasks = (hasPermission: boolean): ApiResult { - return consoleProxyFetchJSON({ url, method: 'GET' }) + const yamlPath = url.startsWith(GITHUB_BASE_URL) ? url.slice(GITHUB_BASE_URL.length) : null; + if (!yamlPath) { + throw new Error('Not a valid GitHub URL'); + } + + return getTaskYAMLFromGithub({ yamlPath }) .then((task: K8sResourceKind) => { task.metadata.namespace = namespace; task.metadata.annotations = { @@ -125,7 +91,12 @@ export const updateArtifactHubTask = async ( name: string, version: string, ) => { - return consoleProxyFetchJSON({ url, method: 'GET' }) + const yamlPath = url.startsWith(GITHUB_BASE_URL) ? url.slice(GITHUB_BASE_URL.length) : null; + if (!yamlPath) { + throw new Error('Not a valid GitHub raw URL'); + } + + return getTaskYAMLFromGithub({ yamlPath }) .then((task: K8sResourceKind) => { task.metadata.namespace = namespace; task.metadata.annotations = { diff --git a/frontend/packages/pipelines-plugin/src/components/catalog/apis/utils.ts b/frontend/packages/pipelines-plugin/src/components/catalog/apis/utils.ts new file mode 100644 index 00000000000..948a7552a2d --- /dev/null +++ b/frontend/packages/pipelines-plugin/src/components/catalog/apis/utils.ts @@ -0,0 +1,162 @@ +import { safeLoad } from 'js-yaml'; +import { consoleFetchJSON, K8sResourceKind } from '@console/dynamic-plugin-sdk/src/lib-core'; +import { HttpError } from '@console/dynamic-plugin-sdk/src/utils/error/http-error'; + +export const ARTIFACTHUB_SEARCH_URL = '/api/dev-console/artifacthub/search'; +export const ARTIFACTHUB_TASK_DETAILS_URL = '/api/dev-console/artifacthub/get'; +export const GITHUB_ARTIFACTHUB_TASK_YAML_URL = '/api/dev-console/artifacthub/yaml'; + +export type ArtifactHubRepository = { + name: string; + kind: number; + url: string; + display_name: string; + repository_id: string; + organization_name: string; + organization_display_name: string; +}; + +export type ArtifactHubVersion = { + version: string; + contains_security_update: boolean; + prerelease: boolean; + ts: number; +}; + +export type ArtifactHubTask = { + package_id: string; + name: string; + description: string; + version: string; + display_name: string; + repository: ArtifactHubRepository; +}; + +export type ArtifactHubTaskDetails = { + package_id: string; + name: string; + description: string; + display_name: string; + keywords: string[]; + platforms: string[]; + version: ArtifactHubVersion[]; + available_versions: []; + content_url: string; + repository: ArtifactHubRepository; +}; + +export type DevConsoleEndpointResponse = { + statusCode: number; + headers: Record; + body: string; +}; + +type TaskSearchRequest = { + searchQuery?: string; +}; + +type TaskDetailsRequest = { + repoName: string; + name: string; + version: string; +}; + +type TaskYAMLRequest = { + yamlPath: string; +}; + +/** + * Fetches the YAML content of a task from GitHub. + * @param taskYAMLRequest The request object containing the path to the task YAML file. + * @returns The parsed YAML content of the task. + */ +export const getTaskYAMLFromGithub = async ( + taskYAMLRequest: TaskYAMLRequest, +): Promise => { + const taskYAMLResponse: DevConsoleEndpointResponse = await consoleFetchJSON.post( + GITHUB_ARTIFACTHUB_TASK_YAML_URL, + taskYAMLRequest, + ); + + if (!taskYAMLResponse.statusCode) { + throw new Error('Unexpected proxy response: Status code is missing!'); + } + + if (taskYAMLResponse.statusCode < 200 || taskYAMLResponse.statusCode >= 300) { + throw new HttpError( + `Unexpected status code: ${taskYAMLResponse.statusCode}`, + taskYAMLResponse.statusCode, + null, + taskYAMLResponse, + ); + } + + try { + // Parse the YAML response body + return safeLoad(taskYAMLResponse.body); + } catch (e) { + throw new Error('Failed to parse task YAML response body as YAML'); + } +}; + +/** + * Fetches the details of a task from ArtifactHub. + * @param taskDetailsRequest The request object containing the task details. + * @returns The details of the task. + */ +export const getTaskDetails = async ( + taskDetailsRequest: TaskDetailsRequest, +): Promise => { + const taskDetailsResponse: DevConsoleEndpointResponse = await consoleFetchJSON.post( + ARTIFACTHUB_TASK_DETAILS_URL, + taskDetailsRequest, + ); + if (!taskDetailsResponse.statusCode) { + throw new Error('Unexpected proxy response: Status code is missing!'); + } + if (taskDetailsResponse.statusCode < 200 || taskDetailsResponse.statusCode >= 300) { + throw new HttpError( + `Unexpected status code: ${taskDetailsResponse.statusCode}`, + taskDetailsResponse.statusCode, + null, + taskDetailsResponse, + ); + } + + try { + return JSON.parse(taskDetailsResponse.body) as ArtifactHubTaskDetails; + } catch (e) { + throw new Error('Failed to parse task details response body as JSON'); + } +}; + +/** + * Fetches the tasks from ArtifactHub. + * @param (optional) searchrequest The search request object. + * @returns The array of tasks matching the search request. + */ +export const searchTasks = async ( + searchrequest?: TaskSearchRequest, +): Promise => { + const searchResponse: DevConsoleEndpointResponse = await consoleFetchJSON.post( + ARTIFACTHUB_SEARCH_URL, + searchrequest || {}, + ); + if (!searchResponse.statusCode) { + throw new Error('Unexpected proxy response: Status code is missing!'); + } + if (searchResponse.statusCode < 200 || searchResponse.statusCode >= 300) { + throw new HttpError( + `Unexpected status code: ${searchResponse.statusCode}`, + searchResponse.statusCode, + null, + searchResponse, + ); + } + + try { + return JSON.parse(searchResponse.body).packages as ArtifactHubTask[]; + } catch (e) { + throw new Error('Failed to parse search response body as JSON'); + } +}; diff --git a/frontend/packages/pipelines-plugin/src/components/catalog/providers/useArtifactHubTasksProvider.tsx b/frontend/packages/pipelines-plugin/src/components/catalog/providers/useArtifactHubTasksProvider.tsx index 2cc32f9bcf5..0b90a3b1726 100644 --- a/frontend/packages/pipelines-plugin/src/components/catalog/providers/useArtifactHubTasksProvider.tsx +++ b/frontend/packages/pipelines-plugin/src/components/catalog/providers/useArtifactHubTasksProvider.tsx @@ -6,8 +6,9 @@ import { referenceForModel } from '@console/internal/module/k8s'; import { TaskModel } from '../../../models/pipelines'; import { TaskProviders } from '../../pipelines/const'; import { ARTIFACTHUB } from '../../quicksearch/const'; -import { ArtifactHubTask, useGetArtifactHubTasks } from '../apis/artifactHub'; +import { useGetArtifactHubTasks } from '../apis/artifactHub'; import { TektonHubTask } from '../apis/tektonHub'; +import { ArtifactHubTask } from '../apis/utils'; import { useTektonHubIntegration } from '../catalog-utils'; const normalizeArtifactHubTasks = (artifactHubTasks: ArtifactHubTask[]): CatalogItem[] => { diff --git a/frontend/packages/pipelines-plugin/src/components/import/pipeline/PipelineTemplate.tsx b/frontend/packages/pipelines-plugin/src/components/import/pipeline/PipelineTemplate.tsx index 9fb9e5260d0..5d3324589c4 100644 --- a/frontend/packages/pipelines-plugin/src/components/import/pipeline/PipelineTemplate.tsx +++ b/frontend/packages/pipelines-plugin/src/components/import/pipeline/PipelineTemplate.tsx @@ -4,7 +4,10 @@ import { useFormikContext, FormikValues } from 'formik'; import i18next from 'i18next'; import * as _ from 'lodash'; import { useTranslation } from 'react-i18next'; -import { ReadableResourcesNames } from '@console/dev-console/src/components/import/import-types'; +import { + BuildOptions, + ReadableResourcesNames, +} from '@console/dev-console/src/components/import/import-types'; import { NormalizedBuilderImages } from '@console/dev-console/src/utils/imagestream-utils'; import { getGitService } from '@console/git-service/src'; import { LoadingInline } from '@console/internal/components/utils'; @@ -184,7 +187,6 @@ const PipelineTemplate: React.FC = ({ builderImages, exis } else { setFieldValue('pipeline.template', null); setFieldValue('pipeline.templateSelected', ''); - setFieldValue('pipeline.enabled', false); setNoTemplateForRuntime(true); } }; @@ -245,12 +247,21 @@ const PipelineTemplate: React.FC = ({ builderImages, exis }, 0); }; + const handlePipelineCheckbox = (checked: boolean) => { + if (checked) { + setFieldValue('build.option', BuildOptions.PIPELINES); + } else { + setFieldValue('build.option', null); + } + }; + return pipeline.template ? ( <> {pipeline.enabled && isPacRepo && ( = (props) => { const { kindObj, match } = props; const operatorVersion = usePipelineOperatorVersion(props.namespace); + const [taskRuns] = useTaskRuns(props.namespace); const menuActions: KebabAction[] = useMenuActionsWithUserAnnotation( - getPipelineRunKebabActions(operatorVersion, true), + getPipelineRunKebabActions(operatorVersion, taskRuns, true), ); const breadcrumbsFor = useDevPipelinesBreadcrumbsFor(kindObj, match); const badge = usePipelineTechPreviewBadge(props.namespace); @@ -48,7 +50,6 @@ const PipelineRunDetailsPage: React.FC = (props) => { }, { href: 'logs', - path: 'logs/:name?', // t('pipelines-plugin~Logs') nameKey: 'pipelines-plugin~Logs', component: PipelineRunLogsWithActiveTask, diff --git a/frontend/packages/pipelines-plugin/src/components/pipelineruns/__tests__/PipelineRunDetailsPage.spec.tsx b/frontend/packages/pipelines-plugin/src/components/pipelineruns/__tests__/PipelineRunDetailsPage.spec.tsx index 46720f09aa3..7b0acdb2dd5 100644 --- a/frontend/packages/pipelines-plugin/src/components/pipelineruns/__tests__/PipelineRunDetailsPage.spec.tsx +++ b/frontend/packages/pipelines-plugin/src/components/pipelineruns/__tests__/PipelineRunDetailsPage.spec.tsx @@ -6,6 +6,7 @@ import { referenceForModel } from '@console/internal/module/k8s'; import { PipelineRunModel } from '../../../models'; import { getPipelineRunKebabActions } from '../../../utils/pipeline-actions'; import * as hookUtils from '../../pipelines/hooks'; +import * as taskRunUtils from '../../taskruns/useTaskRuns'; import TaskRuns from '../detail-page-tabs/TaskRuns'; import PipelineRunEvents from '../events/PipelineRunEvents'; import PipelineRunDetailsPage from '../PipelineRunDetailsPage'; @@ -13,6 +14,7 @@ import * as triggerHooksModule from '../triggered-by/hooks'; const menuActions = jest.spyOn(triggerHooksModule, 'useMenuActionsWithUserAnnotation'); const breadCrumbs = jest.spyOn(hookUtils, 'useDevPipelinesBreadcrumbsFor'); +const taskRuns = jest.spyOn(taskRunUtils, 'useTaskRuns'); type PipelineRunDetailsPageProps = React.ComponentProps; const i18nNS = 'public'; const i18nPipelineNS = 'pipelines-plugin'; @@ -33,8 +35,9 @@ describe('PipelineRunDetailsPage:', () => { }, }, }; - menuActions.mockReturnValue([getPipelineRunKebabActions(new SemVer('1.9.0'), true)]); + menuActions.mockReturnValue([getPipelineRunKebabActions(new SemVer('1.9.0'), [], true)]); breadCrumbs.mockReturnValue([{ label: 'PipelineRuns' }, { label: 'PipelineRuns Details' }]); + taskRuns.mockReturnValue([]); wrapper = shallow(); }); diff --git a/frontend/packages/pipelines-plugin/src/components/pipelineruns/detail-page-tabs/PipelineRunLogs.tsx b/frontend/packages/pipelines-plugin/src/components/pipelineruns/detail-page-tabs/PipelineRunLogs.tsx index 1c2f4ac727c..1fa1995ac1a 100644 --- a/frontend/packages/pipelines-plugin/src/components/pipelineruns/detail-page-tabs/PipelineRunLogs.tsx +++ b/frontend/packages/pipelines-plugin/src/components/pipelineruns/detail-page-tabs/PipelineRunLogs.tsx @@ -3,7 +3,6 @@ import { Nav, NavItem, NavList } from '@patternfly/react-core'; import { TFunction } from 'i18next'; import * as _ from 'lodash'; import { withTranslation } from 'react-i18next'; -import { RouteComponentProps } from 'react-router'; import { Link } from 'react-router-dom'; import { Firehose, resourcePathFromModel } from '@console/internal/components/utils'; import { PipelineRunModel } from '../../../models'; @@ -119,7 +118,8 @@ class PipelineRunLogsWithTranslation extends React.Component< PipelineRunModel, obj.metadata.name, obj.metadata.namespace, - )}/logs/`; + )}/logs`; + return (
@@ -135,12 +135,11 @@ class PipelineRunLogsWithTranslation extends React.Component< className="odc-pipeline-run-logs__navitem" > = ({ obj, - params, }) => { - const activeTask = _.get(params, 'match.params.name'); + const searchParams = new URLSearchParams(window.location.search); + const activeTask = searchParams?.get('taskName'); const [taskRuns, taskRunsLoaded] = useTaskRuns(obj?.metadata?.namespace, obj?.metadata?.name); return ( taskRunsLoaded && diff --git a/frontend/packages/pipelines-plugin/src/components/pipelineruns/list-page/PipelineRunList.tsx b/frontend/packages/pipelines-plugin/src/components/pipelineruns/list-page/PipelineRunList.tsx index 16102034259..3d9c16ee704 100644 --- a/frontend/packages/pipelines-plugin/src/components/pipelineruns/list-page/PipelineRunList.tsx +++ b/frontend/packages/pipelines-plugin/src/components/pipelineruns/list-page/PipelineRunList.tsx @@ -8,6 +8,7 @@ import { useUserSettings } from '@console/shared/src'; import { PREFERRED_DEV_PIPELINE_PAGE_TAB_USER_SETTING_KEY } from '../../../const'; import { PipelineRunModel } from '../../../models'; import { usePipelineOperatorVersion } from '../../pipelines/utils/pipeline-operator'; +import { useTaskRuns } from '../../taskruns/useTaskRuns'; import PipelineRunHeader from './PipelineRunHeader'; import PipelineRunRow from './PipelineRunRow'; @@ -23,6 +24,7 @@ export const PipelineRunList: React.FC = (props) => { PREFERRED_DEV_PIPELINE_PAGE_TAB_USER_SETTING_KEY, 'pipelines', ); + const [taskRuns, taskRunsLoaded] = useTaskRuns(props.namespace); React.useEffect(() => { if (preferredTabLoaded && activePerspective === 'dev') { @@ -42,7 +44,7 @@ export const PipelineRunList: React.FC = (props) => { defaultSortOrder={SortByDirection.desc} Header={PipelineRunHeader} Row={PipelineRunRow} - customData={operatorVersion} + customData={{ operatorVersion, taskRuns: taskRunsLoaded ? taskRuns : [], taskRunsLoaded }} virtualize /> diff --git a/frontend/packages/pipelines-plugin/src/components/pipelineruns/list-page/PipelineRunRow.tsx b/frontend/packages/pipelines-plugin/src/components/pipelineruns/list-page/PipelineRunRow.tsx index 28802581017..13b190c35a9 100644 --- a/frontend/packages/pipelines-plugin/src/components/pipelineruns/list-page/PipelineRunRow.tsx +++ b/frontend/packages/pipelines-plugin/src/components/pipelineruns/list-page/PipelineRunRow.tsx @@ -3,13 +3,14 @@ import { TableData, RowFunctionArgs } from '@console/internal/components/factory import { ResourceLink, Timestamp } from '@console/internal/components/utils'; import { referenceForModel } from '@console/internal/module/k8s'; import { PipelineRunModel } from '../../../models'; -import { PipelineRunKind } from '../../../types'; +import { PipelineRunKind, TaskRunKind } from '../../../types'; import { getPipelineRunKebabActions } from '../../../utils/pipeline-actions'; import { pipelineRunFilterReducer, pipelineRunTitleFilterReducer, } from '../../../utils/pipeline-filter-reducer'; import { pipelineRunDuration } from '../../../utils/pipeline-utils'; +import { getTaskRunsOfPipelineRun } from '../../taskruns/useTaskRuns'; import LinkedPipelineRunTaskStatus from '../status/LinkedPipelineRunTaskStatus'; import PipelineRunStatus from '../status/PipelineRunStatus'; import { ResourceKebabWithUserLabel } from '../triggered-by'; @@ -19,22 +20,25 @@ const pipelinerunReference = referenceForModel(PipelineRunModel); type PLRStatusProps = { obj: PipelineRunKind; + taskRuns: TaskRunKind[]; + taskRunsLoaded: boolean; }; -const PLRStatus: React.FC = ({ obj }) => { +const PLRStatus: React.FC = ({ obj, taskRuns, taskRunsLoaded }) => { return ( ); }; -const PipelineRunRow: React.FC> = ({ - obj, - customData: operatorVersion, -}) => { +const PipelineRunRow: React.FC> = ({ obj, customData }) => { + const { operatorVersion, taskRuns, taskRunsLoaded } = customData; + const PLRTaskRuns = getTaskRunsOfPipelineRun(taskRuns, obj?.metadata?.name); return ( <> @@ -49,10 +53,14 @@ const PipelineRunRow: React.FC> = ({ - + - + @@ -60,7 +68,7 @@ const PipelineRunRow: React.FC> = ({ {pipelineRunDuration(obj)} diff --git a/frontend/packages/pipelines-plugin/src/components/pipelineruns/status/LinkedPipelineRunTaskStatus.tsx b/frontend/packages/pipelines-plugin/src/components/pipelineruns/status/LinkedPipelineRunTaskStatus.tsx index 963c8113a5e..51dfad28e4f 100644 --- a/frontend/packages/pipelines-plugin/src/components/pipelineruns/status/LinkedPipelineRunTaskStatus.tsx +++ b/frontend/packages/pipelines-plugin/src/components/pipelineruns/status/LinkedPipelineRunTaskStatus.tsx @@ -2,13 +2,15 @@ import * as React from 'react'; import { useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom'; import { LoadingInline, resourcePathFromModel } from '@console/internal/components/utils'; +import { DASH } from '@console/shared'; import { PipelineRunModel } from '../../../models'; -import { PipelineRunKind } from '../../../types'; -import { useTaskRuns } from '../../taskruns/useTaskRuns'; +import { PipelineRunKind, TaskRunKind } from '../../../types'; import { PipelineBars } from './PipelineBars'; export interface LinkedPipelineRunTaskStatusProps { pipelineRun: PipelineRunKind; + taskRuns: TaskRunKind[]; + taskRunsLoaded?: boolean; } /** @@ -17,17 +19,22 @@ export interface LinkedPipelineRunTaskStatusProps { */ const LinkedPipelineRunTaskStatus: React.FC = ({ pipelineRun, + taskRuns, + taskRunsLoaded, }) => { const { t } = useTranslation(); - const [taskRuns, taskRunsLoaded] = useTaskRuns( - pipelineRun.metadata.namespace, - pipelineRun.metadata.name, - ); - const pipelineStatus = taskRunsLoaded ? ( - - ) : ( - - ); + const pipelineStatus = + taskRuns.length > 0 ? ( + + ) : taskRunsLoaded && taskRuns.length === 0 ? ( + <>{DASH} + ) : ( + + ); if (pipelineRun.metadata?.name && pipelineRun.metadata?.namespace) { return ( diff --git a/frontend/packages/pipelines-plugin/src/components/pipelineruns/status/PipelineRunStatus.tsx b/frontend/packages/pipelines-plugin/src/components/pipelineruns/status/PipelineRunStatus.tsx index 8b4549fa802..1fb5935c07c 100644 --- a/frontend/packages/pipelines-plugin/src/components/pipelineruns/status/PipelineRunStatus.tsx +++ b/frontend/packages/pipelines-plugin/src/components/pipelineruns/status/PipelineRunStatus.tsx @@ -4,8 +4,7 @@ import { Link } from 'react-router-dom'; import { LoadingInline, resourcePathFromModel } from '@console/internal/components/utils'; import { DASH } from '@console/shared'; import { PipelineRunModel } from '../../../models'; -import { PipelineRunKind } from '../../../types'; -import { useTaskRuns } from '../../taskruns/useTaskRuns'; +import { PipelineRunKind, TaskRunKind } from '../../../types'; import { getPLRLogSnippet } from '../logs/pipelineRunLogSnippet'; import PipelineResourceStatus from './PipelineResourceStatus'; import StatusPopoverContent from './StatusPopoverContent'; @@ -14,15 +13,19 @@ type PipelineRunStatusProps = { status: string; pipelineRun: PipelineRunKind; title?: string; + taskRuns: TaskRunKind[]; + taskRunsLoaded?: boolean; }; -const PipelineRunStatus: React.FC = ({ status, pipelineRun, title }) => { +const PipelineRunStatus: React.FC = ({ + status, + pipelineRun, + title, + taskRuns, + taskRunsLoaded, +}) => { const { t } = useTranslation(); - const [taskRuns, taskRunsLoaded] = useTaskRuns( - pipelineRun?.metadata?.namespace, - pipelineRun?.metadata?.name, - ); return pipelineRun ? ( - taskRunsLoaded ? ( + taskRuns.length > 0 || (taskRunsLoaded && taskRuns.length === 0) ? ( = (props) => { breadcrumbsFor={() => breadcrumbsFor} pages={[ navFactory.details(PipelineDetails), - { - href: 'metrics', - // t('pipelines-plugin~Metrics') - nameKey: 'pipelines-plugin~Metrics', - component: PipelineMetrics, - }, navFactory.editYaml(), { href: 'Runs', diff --git a/frontend/packages/pipelines-plugin/src/components/pipelines/__tests__/pipelineDetailsPage.spec.tsx b/frontend/packages/pipelines-plugin/src/components/pipelines/__tests__/pipelineDetailsPage.spec.tsx index 2144df74a38..7281ef067a5 100644 --- a/frontend/packages/pipelines-plugin/src/components/pipelines/__tests__/pipelineDetailsPage.spec.tsx +++ b/frontend/packages/pipelines-plugin/src/components/pipelines/__tests__/pipelineDetailsPage.spec.tsx @@ -14,14 +14,10 @@ import { referenceForModel } from '@console/internal/module/k8s'; import store from '@console/internal/redux'; import { PipelineModel } from '../../../models'; import { pipelineTestData, PipelineExampleNames } from '../../../test-data/pipeline-data'; -import { - sampleTektonConfig, - sampleTektonConfigMetrics, -} from '../../../test-data/tekon-config-data'; +import { sampleTektonConfig } from '../../../test-data/tekon-config-data'; import { PipelineRunKind } from '../../../types'; import { getPipelineKebabActions } from '../../../utils/pipeline-actions'; import * as triggerHooksModule from '../../pipelineruns/triggered-by/hooks'; -import { PipelineMetricsLevel } from '../const'; import * as hookUtils from '../hooks'; import { MetricsQueryPrefix } from '../pipeline-metrics/pipeline-metrics-utils'; import PipelineDetailsPage from '../PipelineDetailsPage'; @@ -166,29 +162,4 @@ describe('PipelineDetailsPage:', () => { ); expect(startLastRun).toBeDefined(); }); - - describe('Pipeline Details page Metrics Tab:', () => { - test('It should contain metrics tab if the user has view permission in the openshift pipelines namespace', async () => { - const wrapper: ReactWrapper = await renderPipelineDetailsPage(); - const tabs = wrapper.find(DetailsPage).props().pages; - const metricsTab = tabs.find((t) => t.nameKey === 'pipelines-plugin~Metrics'); - - expect(tabs).toHaveLength(5); - expect(metricsTab).toBeDefined(); - }); - - test('It should contain metrics tab if the user has permission and osp 1.5.2 is installed', async () => { - spyUseAccessReview.mockReturnValue([true]); - ((operatorVersion as unknown) as jest.Mock).mockReturnValue(new SemVer('1.5.2')); - spyPipelineConfig.mockReturnValue([ - sampleTektonConfigMetrics[PipelineMetricsLevel.UNSIMPLIFIED_METRICS_LEVEL], - ]); - const wrapper: ReactWrapper = await renderPipelineDetailsPage(); - const tabs = wrapper.find(DetailsPage).props().pages; - const metricsTab = tabs.find((t) => t.nameKey === 'pipelines-plugin~Metrics'); - - expect(tabs).toHaveLength(5); - expect(metricsTab).toBeDefined(); - }); - }); }); diff --git a/frontend/packages/pipelines-plugin/src/components/pipelines/detail-page-tabs/PipelineForm.tsx b/frontend/packages/pipelines-plugin/src/components/pipelines/detail-page-tabs/PipelineForm.tsx index 5eeb9d3aae5..666a68f66f9 100644 --- a/frontend/packages/pipelines-plugin/src/components/pipelines/detail-page-tabs/PipelineForm.tsx +++ b/frontend/packages/pipelines-plugin/src/components/pipelines/detail-page-tabs/PipelineForm.tsx @@ -22,7 +22,6 @@ const PipelineForm: React.FC = ({ const { t } = useTranslation(); const initialValues = { parameters: _.get(obj.spec, 'params', []), - resources: _.get(obj.spec, 'resources', []), }; const handleSubmit = (values, actions) => { @@ -33,7 +32,6 @@ const PipelineForm: React.FC = ({ spec: { ...obj.spec, params: sanitizePipelineParams(values.parameters), - resources: values.resources, }, }, obj.metadata.namespace, @@ -43,7 +41,6 @@ const PipelineForm: React.FC = ({ actions.resetForm({ values: { parameters: _.get(newObj.spec, 'params', []), - resources: _.get(newObj.spec, 'resources', []), }, status: { success: t('pipelines-plugin~Successfully updated the pipeline {{formName}}.', { diff --git a/frontend/packages/pipelines-plugin/src/components/pipelines/index.ts b/frontend/packages/pipelines-plugin/src/components/pipelines/index.ts index 152f5d94e9b..6b8039d6c45 100644 --- a/frontend/packages/pipelines-plugin/src/components/pipelines/index.ts +++ b/frontend/packages/pipelines-plugin/src/components/pipelines/index.ts @@ -11,3 +11,4 @@ export { default as PipelineBuilderPage } from './pipeline-builder/PipelineBuild export { default as PipelineBuilderEditPage } from './pipeline-builder/PipelineBuilderEditPage'; export { default as PipelineTabbedPage } from './PipelineTabbedPage'; export { default as PipelineParameters } from './PipelineParameters'; +export { default as PipelineMetrics } from './pipeline-metrics/PipelineMetrics'; diff --git a/frontend/packages/pipelines-plugin/src/components/pipelines/list-page/PipelineList.tsx b/frontend/packages/pipelines-plugin/src/components/pipelines/list-page/PipelineList.tsx index f483ae2266e..8c82e3fb218 100644 --- a/frontend/packages/pipelines-plugin/src/components/pipelines/list-page/PipelineList.tsx +++ b/frontend/packages/pipelines-plugin/src/components/pipelines/list-page/PipelineList.tsx @@ -4,15 +4,18 @@ import { useTranslation } from 'react-i18next'; import { Table } from '@console/internal/components/factory'; import { PipelineModel } from '../../../models'; import { PropPipelineData } from '../../../utils/pipeline-augment'; +import { useTaskRuns } from '../../taskruns/useTaskRuns'; import PipelineHeader from './PipelineHeader'; import PipelineRow from './PipelineRow'; export interface PipelineListProps { data?: PropPipelineData[]; + namespace: string; } const PipelineList: React.FC = (props) => { const { t } = useTranslation(); + const [taskRuns, taskRunsLoaded] = useTaskRuns(props.namespace); return ( = (props) => { aria-label={t(PipelineModel.labelPluralKey)} Header={PipelineHeader} Row={PipelineRow} + customData={{ taskRuns: taskRunsLoaded ? taskRuns : [], taskRunsLoaded }} virtualize /> ); diff --git a/frontend/packages/pipelines-plugin/src/components/pipelines/list-page/PipelineRow.tsx b/frontend/packages/pipelines-plugin/src/components/pipelines/list-page/PipelineRow.tsx index ea625263899..0a6d6309958 100644 --- a/frontend/packages/pipelines-plugin/src/components/pipelines/list-page/PipelineRow.tsx +++ b/frontend/packages/pipelines-plugin/src/components/pipelines/list-page/PipelineRow.tsx @@ -3,13 +3,14 @@ import { RowFunctionArgs, TableData } from '@console/internal/components/factory import { ResourceLink, Timestamp } from '@console/internal/components/utils'; import { referenceForModel } from '@console/internal/module/k8s'; import { PipelineModel, PipelineRunModel } from '../../../models'; -import { PipelineWithLatest } from '../../../types'; +import { PipelineWithLatest, TaskRunKind } from '../../../types'; import { pipelineFilterReducer, pipelineTitleFilterReducer, } from '../../../utils/pipeline-filter-reducer'; import LinkedPipelineRunTaskStatus from '../../pipelineruns/status/LinkedPipelineRunTaskStatus'; import PipelineRunStatus from '../../pipelineruns/status/PipelineRunStatus'; +import { getTaskRunsOfPipelineRun } from '../../taskruns/useTaskRuns'; import { tableColumnClasses } from './pipeline-table'; import PipelineRowKebabActions from './PipelineRowKebabActions'; @@ -18,19 +19,25 @@ const pipelinerunReference = referenceForModel(PipelineRunModel); type PipelineStatusProps = { obj: PipelineWithLatest; + taskRuns: TaskRunKind[]; + taskRunsLoaded?: boolean; }; -const PipelineStatus: React.FC = ({ obj }) => { +const PipelineStatus: React.FC = ({ obj, taskRuns, taskRunsLoaded }) => { return ( ); }; -const PipelineRow: React.FC> = ({ obj }) => { +const PipelineRow: React.FC> = ({ obj, customData }) => { + const { taskRuns, taskRunsLoaded } = customData; + const PLRTaskRuns = getTaskRunsOfPipelineRun(taskRuns, obj?.latestRun?.metadata?.name); return ( <> @@ -55,10 +62,18 @@ const PipelineRow: React.FC> = ({ obj }) => )} - {obj.latestRun ? : '-'} + {obj.latestRun ? ( + + ) : ( + '-' + )} - + {(obj.latestRun?.status?.startTime && ( diff --git a/frontend/packages/pipelines-plugin/src/components/pipelines/modals/common/SecretForm.tsx b/frontend/packages/pipelines-plugin/src/components/pipelines/modals/common/SecretForm.tsx index 136ec565cec..476bb662d6b 100644 --- a/frontend/packages/pipelines-plugin/src/components/pipelines/modals/common/SecretForm.tsx +++ b/frontend/packages/pipelines-plugin/src/components/pipelines/modals/common/SecretForm.tsx @@ -123,14 +123,11 @@ const SecretForm: React.FC> = ({ [t], ); - const setValues = (type: SecretType) => { + const setValues = (type: SecretType, data) => { if (type === SecretType.dockerconfigjson) { - setFieldValue( - 'formData', - _.mapValues({ '.dockerconfigjson': stringData[type] }, JSON.stringify), - ); + setFieldValue('formData', _.mapValues({ '.dockerconfigjson': data[type] }, JSON.stringify)); } else { - setFieldValue('formData', stringData[type]); + setFieldValue('formData', data[type]); } if (values.type !== type) { clearServerURL(); @@ -138,8 +135,10 @@ const SecretForm: React.FC> = ({ }; const onDataChanged = (value: string) => { - setStringData({ ...stringData, [values.type]: value }); - setValues(values.type); + setStringData((prevState) => { + setValues(values.type, { ...prevState, [values.type]: value }); + return { ...prevState, [values.type]: value }; + }); }; return ( @@ -172,7 +171,7 @@ const SecretForm: React.FC> = ({ label={t('pipelines-plugin~Authentication type')} items={authTypes} title={authTypes[values.type]} - onChange={(type: SecretType) => setValues(type)} + onChange={(type: SecretType) => setValues(type, stringData)} fullWidth required /> diff --git a/frontend/packages/pipelines-plugin/src/components/pipelines/modals/common/utils.ts b/frontend/packages/pipelines-plugin/src/components/pipelines/modals/common/utils.ts index 44cf57978fa..8e9b06a51c8 100644 --- a/frontend/packages/pipelines-plugin/src/components/pipelines/modals/common/utils.ts +++ b/frontend/packages/pipelines-plugin/src/components/pipelines/modals/common/utils.ts @@ -110,6 +110,9 @@ export const getPipelineRunData = ( ); delete annotations['kubectl.kubernetes.io/last-applied-configuration']; delete annotations['tekton.dev/v1beta1TaskRuns']; + delete annotations['results.tekton.dev/log']; + delete annotations['results.tekton.dev/record']; + delete annotations['results.tekton.dev/result']; const newPipelineRun = { apiVersion: pipeline ? pipeline.apiVersion : latestRun.apiVersion, diff --git a/frontend/packages/pipelines-plugin/src/components/pipelines/pipeline-builder/utils.ts b/frontend/packages/pipelines-plugin/src/components/pipelines/pipeline-builder/utils.ts index bc09a3412df..195a83bc506 100644 --- a/frontend/packages/pipelines-plugin/src/components/pipelines/pipeline-builder/utils.ts +++ b/frontend/packages/pipelines-plugin/src/components/pipelines/pipeline-builder/utils.ts @@ -348,7 +348,7 @@ export const convertBuilderFormToPipeline = ( kind: PipelineModel.kind, metadata: { ...existingPipeline?.metadata, - name, + name: existingPipeline?.metadata?.name ? existingPipeline?.metadata?.name : name, namespace, }, spec: { @@ -363,7 +363,7 @@ export const convertBuilderFormToPipeline = ( ? tasks : existingPipeline?.spec?.tasks ?? [].map((task) => task && removeEmptyFormFields(removeListRunAfters(task, listIds))), - finally: finallyTasks.length > 0 ? finallyTasks : existingPipeline?.spec?.finally ?? [], + finally: finallyTasks, }, }; }; diff --git a/frontend/packages/pipelines-plugin/src/components/pipelines/pipeline-metrics/PipelineMetrics.scss b/frontend/packages/pipelines-plugin/src/components/pipelines/pipeline-metrics/PipelineMetrics.scss index 0df480b83dd..f2f679307d4 100644 --- a/frontend/packages/pipelines-plugin/src/components/pipelines/pipeline-metrics/PipelineMetrics.scss +++ b/frontend/packages/pipelines-plugin/src/components/pipelines/pipeline-metrics/PipelineMetrics.scss @@ -18,3 +18,10 @@ .pipeline-metrics-empty-state { background-color: var(--pf-global--BackgroundColor--200); } + +.pipeline-metrics { + &__pipelinerun-taskrun-card-body { + //reduce padding pipelinerun taskrun duration card to provide extra space for the legends + padding: 10px 15px 10px 10px; + } +} diff --git a/frontend/packages/pipelines-plugin/src/components/pipelines/pipeline-metrics/PipelineMetrics.tsx b/frontend/packages/pipelines-plugin/src/components/pipelines/pipeline-metrics/PipelineMetrics.tsx index 4f80d8082e1..61b2f4e272d 100644 --- a/frontend/packages/pipelines-plugin/src/components/pipelines/pipeline-metrics/PipelineMetrics.tsx +++ b/frontend/packages/pipelines-plugin/src/components/pipelines/pipeline-metrics/PipelineMetrics.tsx @@ -70,7 +70,7 @@ const PipelineMetrics: React.FC = ({ obj, customData }) } return ( - + {hasUpdatePermission && metricsLevel === PipelineMetricsLevel.PIPELINE_TASK_LEVEL && ( @@ -160,7 +160,7 @@ const PipelineMetrics: React.FC = ({ obj, customData }) {t('pipelines-plugin~TaskRun Duration')} - + { + // The first child should be a with a `width` and `height` prop giving the legend's content width and height + const width = children?.[0]?.[0]?.props?.width ?? '100%'; + const height = children?.[0]?.[0]?.props?.height ?? '100%'; + return ( + +
+ + {children} + +
+
+ ); +}; + const PipelineRunTaskRunGraph: React.FC = ({ pipeline, timespan, @@ -133,18 +148,18 @@ const PipelineRunTaskRunGraph: React.FC = ({ tickValues={tickValues} width={contentRect.bounds.width} height={chartHeight} - legendPosition="bottom-left" legendComponent={ - + !_.isEmpty(getLegendData()) && ( + } + gutter={18} + name="legend" + data={getLegendData()} + style={{ + labels: { fontSize: 11, fill: 'var(--pf-global--Color--100)' }, + }} + /> + ) } containerComponent={ ; @@ -31,7 +33,6 @@ export const LineChart: React.FC = ({ hiddenSeries, yTickFormatter, legendComponent, - legendPosition, events, tickValues, }) => { @@ -72,8 +73,6 @@ export const LineChart: React.FC = ({ height={height || DEFAULT_CHART_HEIGHT} domain={domain} events={events} - legendComponent={legendComponent} - legendPosition={legendPosition} domainPadding={{ x: 10, y: 10 }} padding={{ bottom: 80, @@ -118,6 +117,7 @@ export const LineChart: React.FC = ({ /> ))} + {legendComponent} ); diff --git a/frontend/packages/pipelines-plugin/src/components/pipelines/pipeline-topology/PipelineTaskNode.tsx b/frontend/packages/pipelines-plugin/src/components/pipelines/pipeline-topology/PipelineTaskNode.tsx index 3a42bafd254..00a9de6cafa 100644 --- a/frontend/packages/pipelines-plugin/src/components/pipelines/pipeline-topology/PipelineTaskNode.tsx +++ b/frontend/packages/pipelines-plugin/src/components/pipelines/pipeline-topology/PipelineTaskNode.tsx @@ -132,7 +132,11 @@ const PipelineTaskNode: React.FunctionComponent = ({ const { name: plrName, namespace } = pipelineRun?.metadata; const path = plrName - ? `${resourcePathFromModel(PipelineRunModel, plrName, namespace)}/logs/${element.getLabel()}` + ? `${resourcePathFromModel( + PipelineRunModel, + plrName, + namespace, + )}/logs?taskName=${element.getLabel()}` : undefined; const enableLogLink = diff --git a/frontend/packages/pipelines-plugin/src/components/pipelines/pipeline-topology/utils.ts b/frontend/packages/pipelines-plugin/src/components/pipelines/pipeline-topology/utils.ts index b35e1fa6eae..4e405f1e340 100644 --- a/frontend/packages/pipelines-plugin/src/components/pipelines/pipeline-topology/utils.ts +++ b/frontend/packages/pipelines-plugin/src/components/pipelines/pipeline-topology/utils.ts @@ -478,10 +478,12 @@ export const getGraphDataModel = ( } if (task?.when) { task.when.forEach(({ input, values }) => { - depsFromContextVariables.push(...extractDepsFromContextVariables(input)); - values.forEach((whenValue) => { - depsFromContextVariables.push(...extractDepsFromContextVariables(whenValue)); - }); + if (values) { + depsFromContextVariables.push(...extractDepsFromContextVariables(input)); + values.forEach((whenValue) => { + depsFromContextVariables.push(...extractDepsFromContextVariables(whenValue)); + }); + } }); } const dependancies = _.uniq([...vertex.dependancyNames]); diff --git a/frontend/packages/pipelines-plugin/src/components/quicksearch/PipelineQuickSearch.tsx b/frontend/packages/pipelines-plugin/src/components/quicksearch/PipelineQuickSearch.tsx index 879d7f4fdd3..3a48cdf9217 100644 --- a/frontend/packages/pipelines-plugin/src/components/quicksearch/PipelineQuickSearch.tsx +++ b/frontend/packages/pipelines-plugin/src/components/quicksearch/PipelineQuickSearch.tsx @@ -66,7 +66,7 @@ const Contents: React.FC< ) { item.attributes.installed = ''; if (installedTask) { - item.attributes.installed = installedTask.attributes?.versions[0]?.version.toString(); + item.attributes.installed = installedTask.attributes?.versions[0]?.version?.toString(); } } diff --git a/frontend/packages/pipelines-plugin/src/components/quicksearch/PipelineQuickSearchVersionDropdown.tsx b/frontend/packages/pipelines-plugin/src/components/quicksearch/PipelineQuickSearchVersionDropdown.tsx index 41da0301804..1ebf024bb44 100644 --- a/frontend/packages/pipelines-plugin/src/components/quicksearch/PipelineQuickSearchVersionDropdown.tsx +++ b/frontend/packages/pipelines-plugin/src/components/quicksearch/PipelineQuickSearchVersionDropdown.tsx @@ -23,14 +23,16 @@ const PipelineQuickSearchVersionDropdown: React.FC setOpen((v) => !v), []); - if (!versions || versions?.length === 0) { + if (!versions || !versions.length) { return null; } const versionItems = versions.reduce((acc, { version }) => { - acc[version.toString()] = - version === item.data?.latestVersion?.version - ? i18n.t('pipelines-plugin~{{version}} (latest)', { version }) - : version; + if (version) { + acc[version.toString()] = + version === item.data?.latestVersion?.version + ? i18n.t('pipelines-plugin~{{version}} (latest)', { version }) + : version; + } return acc; }, {}); return ( diff --git a/frontend/packages/pipelines-plugin/src/components/quicksearch/pipeline-quicksearch-utils.ts b/frontend/packages/pipelines-plugin/src/components/quicksearch/pipeline-quicksearch-utils.ts index 03991e7ead9..0ba68aa1d36 100644 --- a/frontend/packages/pipelines-plugin/src/components/quicksearch/pipeline-quicksearch-utils.ts +++ b/frontend/packages/pipelines-plugin/src/components/quicksearch/pipeline-quicksearch-utils.ts @@ -16,8 +16,11 @@ export const isSelectedVersionInstalled = (item: CatalogItem, selectedVersion: s export const isTaskVersionInstalled = (item: CatalogItem): boolean => !!item.attributes?.installed; export const isOneVersionInstalled = (item: CatalogItem): boolean => { - return !!item.attributes?.versions?.find( - (v) => v.version.toString() === item.attributes?.installed?.toString(), + return !!( + item.attributes?.installed && + item.attributes?.versions?.some( + (v) => v.version?.toString() === item.attributes?.installed?.toString(), + ) ); }; diff --git a/frontend/packages/pipelines-plugin/src/components/repository/RepositoryLinkList.tsx b/frontend/packages/pipelines-plugin/src/components/repository/RepositoryLinkList.tsx index 6aeb6c32539..5bbeba9fa01 100644 --- a/frontend/packages/pipelines-plugin/src/components/repository/RepositoryLinkList.tsx +++ b/frontend/packages/pipelines-plugin/src/components/repository/RepositoryLinkList.tsx @@ -12,10 +12,10 @@ import { referenceForModel } from '@console/internal/module/k8s'; import { RepositoryModel } from '../../models'; import { PipelineRunKind } from '../../types'; import { - RepositoryLabels, - RepositoryFields, RepositoryAnnotations, RepoAnnotationFields, + RepositoryFields, + RepositoryLabels, } from './consts'; import { getGitProviderIcon, getLabelValue, sanitizeBranchName } from './repository-utils'; @@ -31,6 +31,9 @@ const RepositoryLinkList: React.FC = ({ pipelineRun }) const repoName = plrLabels?.[repoLabel]; const repoURL = plrAnnotations?.[RepositoryAnnotations[RepoAnnotationFields.REPO_URL]]; const shaURL = plrAnnotations?.[RepositoryAnnotations[RepoAnnotationFields.SHA_URL]]; + const branchName = + plrLabels?.[RepositoryAnnotations[RepoAnnotationFields.BRANCH]] || + plrAnnotations?.[RepositoryAnnotations[RepoAnnotationFields.BRANCH]]; if (!repoName) return null; @@ -58,12 +61,10 @@ const RepositoryLinkList: React.FC = ({ pipelineRun }) )} - {plrLabels?.[RepositoryLabels[RepositoryFields.BRANCH]] && ( + {branchName && ( <> -
{t(getLabelValue(plrLabels[RepositoryLabels[RepositoryFields.BRANCH]]))}
-
- {sanitizeBranchName(plrLabels[RepositoryLabels[RepositoryFields.BRANCH]])} -
+
{t(getLabelValue(branchName))}
+
{sanitizeBranchName(branchName)}
)} {plrLabels?.[RepositoryLabels[RepositoryFields.SHA]] && ( diff --git a/frontend/packages/pipelines-plugin/src/components/repository/RepositoryPipelineRunHeader.tsx b/frontend/packages/pipelines-plugin/src/components/repository/RepositoryPipelineRunHeader.tsx index 82069504b10..c266220d147 100644 --- a/frontend/packages/pipelines-plugin/src/components/repository/RepositoryPipelineRunHeader.tsx +++ b/frontend/packages/pipelines-plugin/src/components/repository/RepositoryPipelineRunHeader.tsx @@ -1,7 +1,12 @@ import { sortable } from '@patternfly/react-table'; import i18n from 'i18next'; import { Kebab } from '@console/internal/components/utils'; -import { RepositoryLabels, RepositoryFields } from './consts'; +import { + RepoAnnotationFields, + RepositoryAnnotations, + RepositoryFields, + RepositoryLabels, +} from './consts'; export const tableColumnClasses = [ 'pf-u-w-16-on-lg pf-u-w-25-on-md', @@ -62,7 +67,7 @@ const RepositoryPipelineRunHeader = () => { }, { title: i18n.t('pipelines-plugin~Branch/Tag'), - sortField: `metadata.labels.${RepositoryLabels[RepositoryFields.BRANCH]}`, + sortField: `metadata.annotations.${RepositoryAnnotations[RepoAnnotationFields.BRANCH]}`, transforms: [sortable], props: { className: tableColumnClasses[7] }, }, diff --git a/frontend/packages/pipelines-plugin/src/components/repository/RepositoryPipelineRunList.tsx b/frontend/packages/pipelines-plugin/src/components/repository/RepositoryPipelineRunList.tsx index 77a6c872ee9..be3b3bd55fe 100644 --- a/frontend/packages/pipelines-plugin/src/components/repository/RepositoryPipelineRunList.tsx +++ b/frontend/packages/pipelines-plugin/src/components/repository/RepositoryPipelineRunList.tsx @@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next'; import { Table } from '@console/internal/components/factory'; import { PipelineRunModel } from '../../models'; import { usePipelineOperatorVersion } from '../pipelines/utils/pipeline-operator'; +import { useTaskRuns } from '../taskruns/useTaskRuns'; import RepositoryPipelineRunHeader from './RepositoryPipelineRunHeader'; import RepositoryPipelineRunRow from './RepositoryPipelineRunRow'; @@ -14,6 +15,7 @@ type RepositoryPipelineRunListProps = { export const RepositoryPipelineRunList: React.FC = (props) => { const { t } = useTranslation(); const operatorVersion = usePipelineOperatorVersion(props.namespace); + const [taskRuns, taskRunsLoaded] = useTaskRuns(props.namespace); return (
defaultSortOrder={SortByDirection.desc} Header={RepositoryPipelineRunHeader} Row={RepositoryPipelineRunRow} - customData={operatorVersion} + customData={{ operatorVersion, taskRuns: taskRunsLoaded ? taskRuns : [], taskRunsLoaded }} virtualize /> ); diff --git a/frontend/packages/pipelines-plugin/src/components/repository/RepositoryPipelineRunListPage.tsx b/frontend/packages/pipelines-plugin/src/components/repository/RepositoryPipelineRunListPage.tsx index 4c347fc6dcb..7aa07d22db8 100644 --- a/frontend/packages/pipelines-plugin/src/components/repository/RepositoryPipelineRunListPage.tsx +++ b/frontend/packages/pipelines-plugin/src/components/repository/RepositoryPipelineRunListPage.tsx @@ -21,7 +21,9 @@ const RepositoryPipelineRunListPage: React.FC = ({ obj }) => { +const PLRStatus: React.FC = ({ obj, taskRuns, taskRunsLoaded }) => { return ( ); }; @@ -50,7 +55,11 @@ const RepositoryPipelineRunRow: React.FC> = ({ }) => { const plrLabels = obj.metadata.labels; const plrAnnotations = obj.metadata.annotations; - + const { operatorVersion, taskRuns, taskRunsLoaded } = customData; + const PLRTaskRuns = getTaskRunsOfPipelineRun(taskRuns, obj?.metadata?.name); + const branchName = + plrLabels?.[RepositoryAnnotations[RepoAnnotationFields.BRANCH]] || + plrAnnotations?.[RepositoryAnnotations[RepoAnnotationFields.BRANCH]]; return ( <> @@ -74,7 +83,7 @@ const RepositoryPipelineRunRow: React.FC> = ({ - {truncateMiddle(plrLabels[RepositoryLabels[RepositoryFields.SHA]], { + {truncateMiddle(plrLabels?.[RepositoryLabels[RepositoryFields.SHA]], { length: 7, truncateEnd: true, omission: '', @@ -86,21 +95,23 @@ const RepositoryPipelineRunRow: React.FC> = ({ - + - + {pipelineRunDuration(obj)} - - {sanitizeBranchName(plrLabels?.[RepositoryLabels[RepositoryFields.BRANCH]])} - + {sanitizeBranchName(branchName)} diff --git a/frontend/packages/pipelines-plugin/src/components/repository/consts.ts b/frontend/packages/pipelines-plugin/src/components/repository/consts.ts index 795ac95f523..587cc5cd85a 100644 --- a/frontend/packages/pipelines-plugin/src/components/repository/consts.ts +++ b/frontend/packages/pipelines-plugin/src/components/repository/consts.ts @@ -3,7 +3,6 @@ import { RepositoryFormValues } from './types'; export enum RepositoryFields { REPOSITORY = 'Repository', - BRANCH = 'Branch', URL_REPO = 'RepoUrl', URL_ORG = 'RepoOrg', SHA = 'sha', @@ -14,6 +13,7 @@ export enum RepoAnnotationFields { SHA_MESSAGE = 'sha_message', SHA_URL = 'sha_url', REPO_URL = 'repo_url', + BRANCH = 'Branch', } export enum RepositoryRuntimes { @@ -25,7 +25,6 @@ export enum RepositoryRuntimes { export const RepositoryLabels: Record = { [RepositoryFields.REPOSITORY]: 'pipelinesascode.tekton.dev/repository', - [RepositoryFields.BRANCH]: 'pipelinesascode.tekton.dev/branch', [RepositoryFields.URL_REPO]: 'pipelinesascode.tekton.dev/url-repository', [RepositoryFields.URL_ORG]: 'pipelinesascode.tekton.dev/url-org', [RepositoryFields.SHA]: 'pipelinesascode.tekton.dev/sha', @@ -36,6 +35,7 @@ export const RepositoryAnnotations: Record = { [RepoAnnotationFields.SHA_MESSAGE]: 'pipelinesascode.tekton.dev/sha-title', [RepoAnnotationFields.SHA_URL]: 'pipelinesascode.tekton.dev/sha-url', [RepoAnnotationFields.REPO_URL]: 'pipelinesascode.tekton.dev/repo-url', + [RepoAnnotationFields.BRANCH]: 'pipelinesascode.tekton.dev/branch', }; export const baseURL = 'https://github.com'; diff --git a/frontend/packages/pipelines-plugin/src/components/repository/list-page/RepositoryRow.tsx b/frontend/packages/pipelines-plugin/src/components/repository/list-page/RepositoryRow.tsx index c9c098f8f69..3a024b9f6b2 100644 --- a/frontend/packages/pipelines-plugin/src/components/repository/list-page/RepositoryRow.tsx +++ b/frontend/packages/pipelines-plugin/src/components/repository/list-page/RepositoryRow.tsx @@ -22,14 +22,16 @@ import { import { pipelineRunDuration } from '../../../utils/pipeline-utils'; import LinkedPipelineRunTaskStatus from '../../pipelineruns/status/LinkedPipelineRunTaskStatus'; import PipelineRunStatus from '../../pipelineruns/status/PipelineRunStatus'; +import { getTaskRunsOfPipelineRun } from '../../taskruns/useTaskRuns'; import { RepositoryFields, RepositoryLabels } from '../consts'; import { RepositoryKind } from '../types'; import { repositoriesTableColumnClasses } from './RepositoryHeader'; -const RepositoryRow: React.FC> = ({ obj }) => { +const RepositoryRow: React.FC> = ({ obj, customData }) => { const { metadata: { name, namespace }, } = obj; + const { taskRuns, taskRunsLoaded } = customData; const [pipelineRun, loaded] = useK8sWatchResource({ kind: referenceForModel(PipelineRunModel), @@ -42,6 +44,8 @@ const RepositoryRow: React.FC> = ({ obj }) => { const latestPLREventType = latestRun && latestRun?.metadata?.labels[RepositoryLabels[RepositoryFields.EVENT_TYPE]]; + + const PLRTaskRuns = getTaskRunsOfPipelineRun(taskRuns, latestRun?.metadata?.name); return ( <> @@ -79,7 +83,11 @@ const RepositoryRow: React.FC> = ({ obj }) => { {} {loaded ? ( latestRun ? ( - + ) : ( '-' ) @@ -93,6 +101,8 @@ const RepositoryRow: React.FC> = ({ obj }) => { status={pipelineRunFilterReducer(latestRun)} title={pipelineRunTitleFilterReducer(latestRun)} pipelineRun={latestRun} + taskRuns={PLRTaskRuns} + taskRunsLoaded={taskRunsLoaded} /> ) : ( diff --git a/frontend/packages/pipelines-plugin/src/components/repository/list-page/ReppositoryList.tsx b/frontend/packages/pipelines-plugin/src/components/repository/list-page/ReppositoryList.tsx index f90417a1156..7e5cc58d5be 100644 --- a/frontend/packages/pipelines-plugin/src/components/repository/list-page/ReppositoryList.tsx +++ b/frontend/packages/pipelines-plugin/src/components/repository/list-page/ReppositoryList.tsx @@ -1,22 +1,29 @@ import * as React from 'react'; import { Table } from '@console/internal/components/factory'; import { RepositoryModel } from '../../../models'; +import { useTaskRuns } from '../../taskruns/useTaskRuns'; import { RepositoryKind } from '../types'; import RepositoryHeader from './RepositoryHeader'; import RepositoryRow from './RepositoryRow'; export interface RepositoryListProps { data?: RepositoryKind[]; + namespace: string; } -const RepositoryList: React.FC = (props) => ( -
-); +const RepositoryList: React.FC = (props) => { + const [taskRuns, taskRunsLoaded] = useTaskRuns(props.namespace); + + return ( +
+ ); +}; export default RepositoryList; diff --git a/frontend/packages/pipelines-plugin/src/components/repository/repository-utils.tsx b/frontend/packages/pipelines-plugin/src/components/repository/repository-utils.tsx index 3e7292cca08..e0b89bd516a 100644 --- a/frontend/packages/pipelines-plugin/src/components/repository/repository-utils.tsx +++ b/frontend/packages/pipelines-plugin/src/components/repository/repository-utils.tsx @@ -38,30 +38,30 @@ export const getGitProviderIcon = (url: string) => { }; export const getLabelValue = (branchName: string): string => { - if (branchName.startsWith('refs/heads/') || branchName.startsWith('refs-heads-')) { + if (branchName?.startsWith('refs/heads/') || branchName?.startsWith('refs-heads-')) { return 'pipelines-plugin~Branch'; } - if (branchName.startsWith('refs/tags/') || branchName.startsWith('refs-tags-')) { + if (branchName?.startsWith('refs/tags/') || branchName?.startsWith('refs-tags-')) { return 'pipelines-plugin~Tag'; } return 'pipelines-plugin~Branch'; }; export const sanitizeBranchName = (branchName: string): string => { - if (branchName.startsWith('refs/heads/')) { + if (branchName?.startsWith('refs/heads/')) { return branchName.replace('refs/heads/', ''); } - if (branchName.startsWith('refs-heads-')) { + if (branchName?.startsWith('refs-heads-')) { return branchName.replace('refs-heads-', ''); } - if (branchName.startsWith('refs/tags/')) { + if (branchName?.startsWith('refs/tags/')) { return branchName.replace('refs/tags/', ''); } - if (branchName.startsWith('refs-tags-')) { + if (branchName?.startsWith('refs-tags-')) { return branchName.replace('refs-tags-', ''); } return branchName; diff --git a/frontend/packages/pipelines-plugin/src/components/taskruns/useTaskRuns.ts b/frontend/packages/pipelines-plugin/src/components/taskruns/useTaskRuns.ts index a2f69056219..ad22e61ae3c 100644 --- a/frontend/packages/pipelines-plugin/src/components/taskruns/useTaskRuns.ts +++ b/frontend/packages/pipelines-plugin/src/components/taskruns/useTaskRuns.ts @@ -7,30 +7,55 @@ import { TektonResourceLabel } from '../pipelines/const'; export const useTaskRuns = ( namespace: string, - pipelineRunName: string, -): [TaskRunKind[], boolean, unknown] => - useK8sWatchResource({ - kind: referenceForModel(TaskRunModel), - namespace, - selector: { - matchLabels: { - [TektonResourceLabel.pipelinerun]: pipelineRunName, - }, - }, - isList: true, - }); + pipelineRunName?: string, +): [TaskRunKind[], boolean, unknown] => { + const taskRunResource = pipelineRunName + ? { + kind: referenceForModel(TaskRunModel), + namespace, + selector: { + matchLabels: { + [TektonResourceLabel.pipelinerun]: pipelineRunName, + }, + }, + isList: true, + } + : { + kind: referenceForModel(TaskRunModel), + namespace, + isList: true, + }; + return useK8sWatchResource(taskRunResource); +}; -export const getTaskRuns = async (namespace: string, pipelineRunName: string) => { - const taskRuns = await k8sListResource({ - model: TaskRunModel, - queryParams: { - ns: namespace, - labelSelector: { - matchLabels: { - [TektonResourceLabel.pipelinerun]: pipelineRunName, +export const getTaskRuns = async (namespace: string, pipelineRunName?: string) => { + const taskRunResource = pipelineRunName + ? { + model: TaskRunModel, + queryParams: { + ns: namespace, + labelSelector: { + matchLabels: { + [TektonResourceLabel.pipelinerun]: pipelineRunName, + }, + }, + }, + } + : { + model: TaskRunModel, + queryParams: { + ns: namespace, }, - }, - }, - }); + }; + const taskRuns = await k8sListResource(taskRunResource); return taskRuns; }; + +export const getTaskRunsOfPipelineRun = ( + taskRuns: TaskRunKind[], + pipelineRunName: string, +): TaskRunKind[] => { + return taskRuns.filter( + (taskRun) => taskRun.metadata?.labels[TektonResourceLabel.pipelinerun] === pipelineRunName, + ); +}; diff --git a/frontend/packages/pipelines-plugin/src/const.ts b/frontend/packages/pipelines-plugin/src/const.ts index 0f0b0188f0b..6ddf6dc9012 100644 --- a/frontend/packages/pipelines-plugin/src/const.ts +++ b/frontend/packages/pipelines-plugin/src/const.ts @@ -8,3 +8,4 @@ export const PIPELINE_STRATEGY_LABEL = 'pipeline.openshift.io/strategy'; export const PREFERRED_DEV_PIPELINE_PAGE_TAB_USER_SETTING_KEY = 'pipeline.preferredPipelinePageTab'; export const FUNC_PIPELINE_RUNTIME_LABEL = 'function.knative.dev/runtime'; export const ARTIFACTHUB_API_BASE_URL = 'https://artifacthub.io/api/v1'; +export const GITHUB_BASE_URL = 'https://github.com'; diff --git a/frontend/packages/pipelines-plugin/src/models/pipelines.ts b/frontend/packages/pipelines-plugin/src/models/pipelines.ts index 1342ec55db4..a5e3e47028c 100644 --- a/frontend/packages/pipelines-plugin/src/models/pipelines.ts +++ b/frontend/packages/pipelines-plugin/src/models/pipelines.ts @@ -203,7 +203,7 @@ export const ConditionModel: K8sKind = { export const TriggerBindingModel: K8sKind = { apiGroup: 'triggers.tekton.dev', - apiVersion: 'v1alpha1', + apiVersion: 'v1beta1', label: 'TriggerBinding', // t('pipelines-plugin~TriggerBinding') labelKey: 'pipelines-plugin~TriggerBinding', @@ -221,7 +221,7 @@ export const TriggerBindingModel: K8sKind = { export const ClusterTriggerBindingModel: K8sKind = { apiGroup: 'triggers.tekton.dev', - apiVersion: 'v1alpha1', + apiVersion: 'v1beta1', label: 'ClusterTriggerBinding', // t('pipelines-plugin~ClusterTriggerBinding') labelKey: 'pipelines-plugin~ClusterTriggerBinding', @@ -239,7 +239,7 @@ export const ClusterTriggerBindingModel: K8sKind = { export const TriggerTemplateModel: K8sKind = { apiGroup: 'triggers.tekton.dev', - apiVersion: 'v1alpha1', + apiVersion: 'v1beta1', label: 'TriggerTemplate', // t('pipelines-plugin~TriggerTemplate') labelKey: 'pipelines-plugin~TriggerTemplate', @@ -257,7 +257,7 @@ export const TriggerTemplateModel: K8sKind = { export const EventListenerModel: K8sKind = { apiGroup: 'triggers.tekton.dev', - apiVersion: 'v1alpha1', + apiVersion: 'v1beta1', label: 'EventListener', // t('pipelines-plugin~EventListener') labelKey: 'pipelines-plugin~EventListener', diff --git a/frontend/packages/pipelines-plugin/src/test-data/event-listener-data.ts b/frontend/packages/pipelines-plugin/src/test-data/event-listener-data.ts index 9f3a683324c..8ba7d915c96 100644 --- a/frontend/packages/pipelines-plugin/src/test-data/event-listener-data.ts +++ b/frontend/packages/pipelines-plugin/src/test-data/event-listener-data.ts @@ -52,7 +52,7 @@ export const TriggerTestData: TriggerTestData = { }; export const EventlistenerTestData: EventListenerTestData = { [EventlistenerTypes.BINDINGS_TEMPLATE_REF]: { - apiVersion: 'triggers.tekton.dev/v1alpha1', + apiVersion: 'triggers.tekton.dev/v1beta1', kind: 'EventListener', metadata: { name: 'el-listener-ref', @@ -63,7 +63,7 @@ export const EventlistenerTestData: EventListenerTestData = { }, }, [EventlistenerTypes.BINDINGS_TEMPLATE_NAME]: { - apiVersion: 'triggers.tekton.dev/v1alpha1', + apiVersion: 'triggers.tekton.dev/v1beta1', kind: 'EventListener', metadata: { name: 'el-listener-name', @@ -75,7 +75,7 @@ export const EventlistenerTestData: EventListenerTestData = { }, [EventlistenerTypes.TRIGGER_REF]: { - apiVersion: 'triggers.tekton.dev/v1alpha1', + apiVersion: 'triggers.tekton.dev/v1beta1', kind: 'EventListener', metadata: { name: 'vote-app', diff --git a/frontend/packages/pipelines-plugin/src/test-data/pipeline-data.ts b/frontend/packages/pipelines-plugin/src/test-data/pipeline-data.ts index 039b2cbaae9..6f122927f74 100644 --- a/frontend/packages/pipelines-plugin/src/test-data/pipeline-data.ts +++ b/frontend/packages/pipelines-plugin/src/test-data/pipeline-data.ts @@ -3145,9 +3145,11 @@ export const PipeLineRunWithRepoMetadata: Record = { metadata: { name: 'pipeline-with-repo', labels: { - 'pipelinesascode.tekton.dev/branch': 'main', 'pipelinesascode.tekton.dev/url-repository': 'demoapp', }, + annotations: { + 'pipelinesascode.tekton.dev/branch': 'main', + }, namespace: 'test', }, spec: {}, @@ -3171,6 +3173,8 @@ export const PipeLineRunWithRepoMetadata: Record = { name: 'pipeline-with-repo', labels: { 'pipelinesascode.tekton.dev/repository': 'repo1', + }, + annotations: { 'pipelinesascode.tekton.dev/branch': 'main', }, namespace: 'test', diff --git a/frontend/packages/pipelines-plugin/src/test-data/trigger-data.ts b/frontend/packages/pipelines-plugin/src/test-data/trigger-data.ts index a6bba909612..085b55afce1 100644 --- a/frontend/packages/pipelines-plugin/src/test-data/trigger-data.ts +++ b/frontend/packages/pipelines-plugin/src/test-data/trigger-data.ts @@ -51,7 +51,7 @@ export const formValues: AddTriggerFormValues = { name: '2~github-push', resource: { kind: 'ClusterTriggerBinding', - apiVersion: 'triggers.tekton.dev/v1alpha1', + apiVersion: 'triggers.tekton.dev/v1beta1', metadata: { name: 'github-push', }, diff --git a/frontend/packages/pipelines-plugin/src/topology/build-decorators/PipelineRunDecorator.tsx b/frontend/packages/pipelines-plugin/src/topology/build-decorators/PipelineRunDecorator.tsx index b972fccdddc..7ae897a947d 100644 --- a/frontend/packages/pipelines-plugin/src/topology/build-decorators/PipelineRunDecorator.tsx +++ b/frontend/packages/pipelines-plugin/src/topology/build-decorators/PipelineRunDecorator.tsx @@ -11,7 +11,7 @@ import { getLatestPipelineRunStatus } from '@console/pipelines-plugin/src/utils/ import { Status } from '@console/shared'; import { BuildDecoratorBubble } from '@console/topology/src/components/graph-view'; import { startPipelineModal } from '../../components/pipelines/modals'; -import { useTaskRuns } from '../../components/taskruns/useTaskRuns'; +import { getTaskRunsOfPipelineRun, useTaskRuns } from '../../components/taskruns/useTaskRuns'; import { PipelineRunModel } from '../../models'; import { PipelineKind, PipelineRunKind } from '../../types'; @@ -41,10 +41,8 @@ export const ConnectedPipelineRunDecorator: React.FC { const { t } = useTranslation(); const { latestPipelineRun, status } = getLatestPipelineRunStatus(pipelineRuns); - const [taskRuns, taskRunsLoaded] = useTaskRuns( - latestPipelineRun?.metadata?.namespace, - latestPipelineRun?.metadata?.name, - ); + const [taskRuns, taskRunsLoaded] = useTaskRuns(latestPipelineRun?.metadata?.namespace); + const PLRTaskRuns = getTaskRunsOfPipelineRun(taskRuns, latestPipelineRun?.metadata?.namespace); const statusIcon = ; const defaultAccessReview: AccessReviewResourceAttributes = { @@ -64,7 +62,7 @@ export const ConnectedPipelineRunDecorator: React.FC ); const link = `${resourcePathFromModel( diff --git a/frontend/packages/pipelines-plugin/src/utils/__tests__/pipeline-actions.spec.ts b/frontend/packages/pipelines-plugin/src/utils/__tests__/pipeline-actions.spec.ts index 962ed2a1d81..21f47d6eb23 100644 --- a/frontend/packages/pipelines-plugin/src/utils/__tests__/pipeline-actions.spec.ts +++ b/frontend/packages/pipelines-plugin/src/utils/__tests__/pipeline-actions.spec.ts @@ -1,3 +1,4 @@ +import { SemVer } from 'semver'; import { PipelineModel, PipelineRunModel } from '../../models'; import { pipelineTestData, PipelineExampleNames, DataState } from '../../test-data/pipeline-data'; import { @@ -47,24 +48,34 @@ describe('PipelineAction testing startPipeline to check the pipelinerun access r describe('PipelineAction testing stopPipelineRun create correct labels and callbacks', () => { it('expect label to be "Stop" with hidden flag as false when latest Run is running', () => { - const stopAction = stopPipelineRun(PipelineRunModel, { - ...samplePipelineRun, - status: { - conditions: [ - { - ...samplePipelineRun.status.conditions[0], - status: 'Unknown', - }, - ], + const stopAction = stopPipelineRun( + PipelineRunModel, + { + ...samplePipelineRun, + status: { + conditions: [ + { + ...samplePipelineRun.status.conditions[0], + status: 'Unknown', + }, + ], + }, }, - }); + new SemVer('1.9.0'), + [], + ); expect(stopAction.labelKey).toBe(`${i18nNS}~Stop`); expect(stopAction.callback).not.toBeNull(); expect(stopAction.hidden).toBeFalsy(); }); it('expect label to be "Stop" with hidden flag as true when latest Run is not running', () => { - const stopAction = stopPipelineRun(PipelineRunModel, samplePipelineRun); + const stopAction = stopPipelineRun( + PipelineRunModel, + samplePipelineRun, + new SemVer('1.9.0'), + [], + ); expect(stopAction.labelKey).toBe(`${i18nNS}~Stop`); expect(stopAction.callback).not.toBeNull(); expect(stopAction.hidden).not.toBeFalsy(); @@ -75,26 +86,30 @@ describe('PipelineAction testing cancelPipelineRunFinally create correct labels it('expect label to be "Cancel" with hidden flag as false when latest Run is running', () => { const pipelineRun = pipelineTestData[PipelineExampleNames.SIMPLE_PIPELINE].pipelineRuns[DataState.IN_PROGRESS]; - const cancelAction = cancelPipelineRunFinally(PipelineRunModel, pipelineRun); + const cancelAction = cancelPipelineRunFinally(PipelineRunModel, pipelineRun, []); expect(cancelAction.labelKey).toBe(`${i18nNS}~Cancel`); expect(cancelAction.callback).not.toBeNull(); expect(cancelAction.hidden).toBeFalsy(); }); it('expect label to be "Cancel" with hidden flag as true when latest Run is not running', () => { - const cancelAction = cancelPipelineRunFinally(PipelineRunModel, samplePipelineRun); + const cancelAction = cancelPipelineRunFinally(PipelineRunModel, samplePipelineRun, []); expect(cancelAction.labelKey).toBe(`${i18nNS}~Cancel`); expect(cancelAction.callback).not.toBeNull(); expect(cancelAction.hidden).not.toBeFalsy(); }); it('"Cancel" action should be present for the Stopped latest Run', () => { - const cancelAction = cancelPipelineRunFinally(PipelineRunModel, { - ...samplePipelineRun, - spec: { - status: 'StoppedRunFinally', + const cancelAction = cancelPipelineRunFinally( + PipelineRunModel, + { + ...samplePipelineRun, + spec: { + status: 'StoppedRunFinally', + }, }, - }); + [], + ); expect(cancelAction.labelKey).toBe(`${i18nNS}~Cancel`); expect(cancelAction.callback).not.toBeNull(); expect(cancelAction.hidden).not.toBeFalsy(); diff --git a/frontend/packages/pipelines-plugin/src/utils/__tests__/pipeline-augment.spec.ts b/frontend/packages/pipelines-plugin/src/utils/__tests__/pipeline-augment.spec.ts index 2affc9ef899..3564903462a 100644 --- a/frontend/packages/pipelines-plugin/src/utils/__tests__/pipeline-augment.spec.ts +++ b/frontend/packages/pipelines-plugin/src/utils/__tests__/pipeline-augment.spec.ts @@ -184,7 +184,7 @@ describe('PipelineAugment test correct task status state is pulled from pipeline pipelineTestData[PipelineExampleNames.EMBEDDED_TASK_SPEC_MOCK_APP].pipelineRuns[ DataState.IN_PROGRESS ]; - expect(shouldHidePipelineRunStop(pipelineRun)).toEqual(false); + expect(shouldHidePipelineRunStop(pipelineRun, [])).toEqual(false); }); it('should hide the pipelinerun stop action ', () => { @@ -192,7 +192,7 @@ describe('PipelineAugment test correct task status state is pulled from pipeline pipelineTestData[PipelineExampleNames.EMBEDDED_TASK_SPEC_MOCK_APP].pipelineRuns[ DataState.SUCCESS ]; - expect(shouldHidePipelineRunStop(pipelineRun)).toEqual(true); + expect(shouldHidePipelineRunStop(pipelineRun, [])).toEqual(true); }); it('should hide the pipelinerun cancel action ', () => { @@ -200,7 +200,7 @@ describe('PipelineAugment test correct task status state is pulled from pipeline pipelineTestData[PipelineExampleNames.EMBEDDED_TASK_SPEC_MOCK_APP].pipelineRuns[ DataState.SUCCESS ]; - expect(shouldHidePipelineRunCancel(pipelineRun)).toEqual(true); + expect(shouldHidePipelineRunCancel(pipelineRun, [])).toEqual(true); }); it('should not hide the pipelinerun cancel action ', () => { @@ -208,7 +208,7 @@ describe('PipelineAugment test correct task status state is pulled from pipeline pipelineTestData[PipelineExampleNames.EMBEDDED_TASK_SPEC_MOCK_APP].pipelineRuns[ DataState.IN_PROGRESS ]; - expect(shouldHidePipelineRunCancel(pipelineRun)).toEqual(false); + expect(shouldHidePipelineRunCancel(pipelineRun, [])).toEqual(false); }); it('should hide the pipelinerun cancel action ', () => { @@ -216,7 +216,7 @@ describe('PipelineAugment test correct task status state is pulled from pipeline pipelineTestData[PipelineExampleNames.SIMPLE_PIPELINE].pipelineRuns[ DataState.PIPELINE_RUN_STOPPED ]; - expect(shouldHidePipelineRunCancel(pipelineRun)).toEqual(true); + expect(shouldHidePipelineRunCancel(pipelineRun, [])).toEqual(true); }); }); diff --git a/frontend/packages/pipelines-plugin/src/utils/pipeline-actions.tsx b/frontend/packages/pipelines-plugin/src/utils/pipeline-actions.tsx index 8dc25cdf29f..df30b0e8863 100644 --- a/frontend/packages/pipelines-plugin/src/utils/pipeline-actions.tsx +++ b/frontend/packages/pipelines-plugin/src/utils/pipeline-actions.tsx @@ -16,8 +16,9 @@ import { removeTriggerModal, } from '../components/pipelines/modals'; import { getPipelineRunData } from '../components/pipelines/modals/common/utils'; +import { getTaskRunsOfPipelineRun } from '../components/taskruns/useTaskRuns'; import { EventListenerModel, PipelineModel, PipelineRunModel } from '../models'; -import { PipelineKind, PipelineRunKind } from '../types'; +import { PipelineKind, PipelineRunKind, TaskRunKind } from '../types'; import { shouldHidePipelineRunStop, shouldHidePipelineRunCancel } from './pipeline-augment'; export const handlePipelineRunSubmit = (pipelineRun: PipelineRunKind) => { @@ -182,7 +183,9 @@ export const stopPipelineRun: KebabAction = ( kind: K8sKind, pipelineRun: PipelineRunKind, operatorVersion: SemVer, + taskRuns: TaskRunKind[], ) => { + const PLRTasks = getTaskRunsOfPipelineRun(taskRuns, pipelineRun?.metadata?.name); // The returned function will be called using the 'kind' and 'obj' in Kebab Actions return { // t('pipelines-plugin~Stop') @@ -207,7 +210,7 @@ export const stopPipelineRun: KebabAction = ( ], ); }, - hidden: shouldHidePipelineRunStop(pipelineRun), + hidden: shouldHidePipelineRunStop(pipelineRun, PLRTasks), accessReview: { group: kind.apiGroup, resource: kind.plural, @@ -221,7 +224,9 @@ export const stopPipelineRun: KebabAction = ( export const cancelPipelineRunFinally: KebabAction = ( kind: K8sKind, pipelineRun: PipelineRunKind, + taskRuns: TaskRunKind[], ) => { + const PLRTasks = getTaskRunsOfPipelineRun(taskRuns, pipelineRun?.metadata?.name); // The returned function will be called using the 'kind' and 'obj' in Kebab Actions return { // t('pipelines-plugin~Cancel') @@ -244,7 +249,7 @@ export const cancelPipelineRunFinally: KebabAction = ( ], ); }, - hidden: shouldHidePipelineRunCancel(pipelineRun), + hidden: shouldHidePipelineRunCancel(pipelineRun, PLRTasks), accessReview: { group: kind.apiGroup, resource: kind.plural, @@ -307,13 +312,14 @@ export const getPipelineKebabActions = ( export const getPipelineRunKebabActions = ( operatorVersion: SemVer, + taskRuns: TaskRunKind[], redirectReRun?: boolean, ): KebabAction[] => [ redirectReRun ? (model, pipelineRun) => rerunPipelineRunAndRedirect(model, pipelineRun) : (model, pipelineRun) => reRunPipelineRun(model, pipelineRun), - (model, pipelineRun) => stopPipelineRun(model, pipelineRun, operatorVersion), - (model, pipelineRun) => cancelPipelineRunFinally(model, pipelineRun), + (model, pipelineRun) => stopPipelineRun(model, pipelineRun, operatorVersion, taskRuns), + (model, pipelineRun) => cancelPipelineRunFinally(model, pipelineRun, taskRuns), Kebab.factory.Delete, ]; diff --git a/frontend/packages/pipelines-plugin/src/utils/pipeline-augment.ts b/frontend/packages/pipelines-plugin/src/utils/pipeline-augment.ts index 2710d23b34b..ce3316cea54 100644 --- a/frontend/packages/pipelines-plugin/src/utils/pipeline-augment.ts +++ b/frontend/packages/pipelines-plugin/src/utils/pipeline-augment.ts @@ -12,7 +12,6 @@ import { apiVersionForModel, } from '@console/internal/module/k8s'; import { TektonResourceLabel } from '../components/pipelines/const'; -import { getTaskRuns } from '../components/taskruns/useTaskRuns'; import { ClusterTaskModel, ClusterTriggerBindingModel, @@ -274,27 +273,30 @@ export const getModelReferenceFromTaskKind = (kind: string): GroupVersionKind => return referenceForModel(model); }; -export const countRunningTasks = (pipelineRun: PipelineRunKind): number => { - let taskRuns: TaskRunKind[] = []; - getTaskRuns(pipelineRun.metadata.namespace, pipelineRun.metadata.name) - .then((response: TaskRunKind[]) => { - taskRuns = response; - }) - .catch(() => {}); +export const countRunningTasks = ( + pipelineRun: PipelineRunKind, + taskRuns: TaskRunKind[], +): number => { const taskStatuses = taskRuns && getTaskStatus(pipelineRun, undefined, taskRuns); return taskStatuses?.Running; }; -export const shouldHidePipelineRunStop = (pipelineRun: PipelineRunKind): boolean => +export const shouldHidePipelineRunStop = ( + pipelineRun: PipelineRunKind, + taskRuns: TaskRunKind[], +): boolean => !( pipelineRun && - (countRunningTasks(pipelineRun) > 0 || + (countRunningTasks(pipelineRun, taskRuns) > 0 || pipelineRunFilterReducer(pipelineRun) === ComputedStatus.Running) ); -export const shouldHidePipelineRunCancel = (pipelineRun: PipelineRunKind): boolean => +export const shouldHidePipelineRunCancel = ( + pipelineRun: PipelineRunKind, + taskRuns: TaskRunKind[], +): boolean => !( pipelineRun && - countRunningTasks(pipelineRun) > 0 && + countRunningTasks(pipelineRun, taskRuns) > 0 && pipelineRunFilterReducer(pipelineRun) !== ComputedStatus.Cancelled ); diff --git a/frontend/packages/shipwright-plugin/console-extensions.json b/frontend/packages/shipwright-plugin/console-extensions.json index 0c709668a36..e873707a6bc 100644 --- a/frontend/packages/shipwright-plugin/console-extensions.json +++ b/frontend/packages/shipwright-plugin/console-extensions.json @@ -22,6 +22,21 @@ "abbr": "B" } }, + { + "type": "console.model-metadata", + "properties": { + "model": { + "group": "shipwright.io", + "version": "v1beta1", + "kind": "Build" + }, + "color": "#7c8fa4", + "label": "%shipwright-plugin~Build%", + "labelPlural": "%shipwright-plugin~Builds%", + "abbr": "B" + } + }, + { "type": "console.model-metadata", "properties": { @@ -36,6 +51,20 @@ "abbr": "BR" } }, + { + "type": "console.model-metadata", + "properties": { + "model": { + "group": "shipwright.io", + "version": "v1beta1", + "kind": "BuildRun" + }, + "color": "#7c8fa4", + "label": "%shipwright-plugin~BuildRun%", + "labelPlural": "%shipwright-plugin~BuildRuns%", + "abbr": "BR" + } + }, { "type": "console.flag/model", "properties": { @@ -44,6 +73,17 @@ "version": "v1alpha1", "kind": "Build" }, + "flag": "SHIPWRIGHT_BUILD_V1ALPHA1" + } + }, + { + "type": "console.flag/model", + "properties": { + "model": { + "group": "shipwright.io", + "version": "v1beta1", + "kind": "Build" + }, "flag": "SHIPWRIGHT_BUILD" } }, @@ -55,6 +95,17 @@ "version": "v1alpha1", "kind": "BuildRun" }, + "flag": "SHIPWRIGHT_BUILDRUN_V1ALPHA1" + } + }, + { + "type": "console.flag/model", + "properties": { + "model": { + "group": "shipwright.io", + "version": "v1beta1", + "kind": "BuildRun" + }, "flag": "SHIPWRIGHT_BUILDRUN" } }, @@ -72,7 +123,8 @@ } }, "flags": { - "required": ["SHIPWRIGHT_BUILD"] + "required": ["SHIPWRIGHT_BUILD_V1ALPHA1"], + "disallowed": ["SHIPWRIGHT_BUILD"] } }, { @@ -89,6 +141,42 @@ "kind": "BuildRun" } }, + "flags": { + "required": ["SHIPWRIGHT_BUILDRUN_V1ALPHA1"], + "disallowed": ["SHIPWRIGHT_BUILDRUN"] + } + }, + { + "type": "console.navigation/resource-ns", + "properties": { + "perspective": "admin", + "section": "builds", + "id": "shipwright-builds", + "name": "%shipwright-plugin~Shipwright Builds%", + "model": { + "group": "shipwright.io", + "version": "v1beta1", + "kind": "Build" + } + }, + "flags": { + "required": ["SHIPWRIGHT_BUILD"] + } + }, + { + "type": "console.navigation/resource-ns", + "properties": { + "perspective": "admin", + "section": "builds", + "id": "shipwright-buildruns", + "insertAfter": "shipwright-buildruns", + "name": "%shipwright-plugin~Shipwright BuildRuns%", + "model": { + "group": "shipwright.io", + "version": "v1beta1", + "kind": "BuildRun" + } + }, "flags": { "required": ["SHIPWRIGHT_BUILDRUN"] } @@ -102,9 +190,17 @@ "kind": "Build" }, "component": { "$codeRef": "pages.BuildListPage" } - }, - "flags": { - "required": ["SHIPWRIGHT_BUILD"] + } + }, + { + "type": "console.page/resource/list", + "properties": { + "model": { + "group": "shipwright.io", + "version": "v1beta1", + "kind": "Build" + }, + "component": { "$codeRef": "pages.BuildListPage" } } }, { @@ -116,9 +212,32 @@ "kind": "Build" }, "component": { "$codeRef": "pages.BuildDetailsPage" } + } + }, + { + "type": "console.page/resource/details", + "properties": { + "model": { + "group": "shipwright.io", + "version": "v1beta1", + "kind": "Build" + }, + "component": { "$codeRef": "pages.BuildDetailsPage" } + } + }, + { + "type": "console.page/resource/list", + "properties": { + "model": { + "group": "shipwright.io", + "version": "v1alpha1", + "kind": "BuildRun" + }, + "component": { "$codeRef": "pages.BuildRunListPage" } }, "flags": { - "required": ["SHIPWRIGHT_BUILD"] + "required": ["SHIPWRIGHT_BUILDRUN_V1ALPHA1"], + "disallowed": ["SHIPWRIGHT_BUILDRUN"] } }, { @@ -126,7 +245,7 @@ "properties": { "model": { "group": "shipwright.io", - "version": "v1alpha1", + "version": "v1beta1", "kind": "BuildRun" }, "component": { "$codeRef": "pages.BuildRunListPage" } @@ -145,6 +264,21 @@ }, "component": { "$codeRef": "pages.BuildRunDetailsPage" } }, + "flags": { + "required": ["SHIPWRIGHT_BUILDRUN_V1ALPHA1"], + "disallowed": ["SHIPWRIGHT_BUILDRUN"] + } + }, + { + "type": "console.page/resource/details", + "properties": { + "model": { + "group": "shipwright.io", + "version": "v1beta1", + "kind": "BuildRun" + }, + "component": { "$codeRef": "pages.BuildRunDetailsPage" } + }, "flags": { "required": ["SHIPWRIGHT_BUILDRUN"] } @@ -160,7 +294,22 @@ "provider": { "$codeRef": "actions.useBuildActions" } }, "flags": { - "required": ["SHIPWRIGHT_BUILD"] + "required": ["SHIPWRIGHT_BUILD_V1ALPHA1"], + "disallowed": ["SHIPWRIGHT_BUILDRUN"] + } + }, + { + "type": "console.action/resource-provider", + "properties": { + "model": { + "group": "shipwright.io", + "version": "v1beta1", + "kind": "Build" + }, + "provider": { "$codeRef": "actions.useBuildActions" } + }, + "flags": { + "required": ["SHIPWRIGHT_BUILDRUN"] } }, { @@ -173,10 +322,38 @@ }, "provider": { "$codeRef": "actions.useBuildRunActions" } }, + "flags": { + "required": ["SHIPWRIGHT_BUILDRUN_V1ALPHA1"], + "disallowed": ["SHIPWRIGHT_BUILDRUN"] + } + }, + { + "type": "console.action/resource-provider", + "properties": { + "model": { + "group": "shipwright.io", + "version": "v1beta1", + "kind": "BuildRun" + }, + "provider": { "$codeRef": "actions.useBuildRunActions" } + }, "flags": { "required": ["SHIPWRIGHT_BUILDRUN"] } }, + { + "type": "console.topology/decorator/provider", + "properties": { + "id": "shipwright-buildruns-decorator", + "priority": 100, + "quadrant": "lowerLeft", + "decorator": { "$codeRef": "topology.getShipWrightBuildDecorator" } + }, + "flags": { + "required": ["SHIPWRIGHT_BUILDRUN_V1ALPHA1"], + "disallowed": ["SHIPWRIGHT_BUILDRUN"] + } + }, { "type": "console.topology/decorator/provider", "properties": { @@ -189,6 +366,21 @@ "required": ["SHIPWRIGHT_BUILDRUN"] } }, + { + "type": "console.topology/details/tab-section", + "properties": { + "id": "topology-tab-section-shipwright-overview", + "tab": "topology-side-bar-tab-resource", + "insertAfter": "topology-tab-section-pods-overview", + "provider": { + "$codeRef": "topology.useBuildSideBarTabSection" + } + }, + "flags": { + "required": ["SHIPWRIGHT_BUILDRUN_V1ALPHA1"], + "disallowed": ["SHIPWRIGHT_BUILDRUN"] + } + }, { "type": "console.topology/details/tab-section", "properties": { @@ -220,6 +412,28 @@ }, "getDataModelReconciler": { "$codeRef": "topology.getShipwrightDataModelReconcilor" } }, + "flags": { + "required": ["SHIPWRIGHT_BUILDRUN_V1ALPHA1"], + "disallowed": ["SHIPWRIGHT_BUILDRUN"] + } + }, + { + "type": "console.topology/data/factory", + "properties": { + "id": "shipwright-topology-model-factory", + "priority": 800, + "resources": { + "builds": { + "model": { "kind": "Build", "group": "shipwright.io", "version": "v1beta1" }, + "opts": { "isList": true, "optional": true, "namespaced": true } + }, + "buildRuns": { + "model": { "kind": "BuildRun", "group": "shipwright.io", "version": "v1beta1" }, + "opts": { "isList": true, "optional": true, "namespaced": true } + } + }, + "getDataModelReconciler": { "$codeRef": "topology.getShipwrightDataModelReconcilor" } + }, "flags": { "required": ["SHIPWRIGHT_BUILDRUN"] } diff --git a/frontend/packages/shipwright-plugin/integration-tests/testData/builds/shipwrightBuild.yaml b/frontend/packages/shipwright-plugin/integration-tests/testData/builds/shipwrightBuild.yaml index b4aabd94719..1978876d9b8 100644 --- a/frontend/packages/shipwright-plugin/integration-tests/testData/builds/shipwrightBuild.yaml +++ b/frontend/packages/shipwright-plugin/integration-tests/testData/builds/shipwrightBuild.yaml @@ -1,10 +1,12 @@ -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: Build metadata: name: buildpack-nodejs-build-heroku spec: source: - url: https://github.com/shipwright-io/sample-nodejs + type: Git + git: + url: https://github.com/shipwright-io/sample-nodejs contextDir: source-build strategy: name: buildpacks-v3-heroku @@ -12,18 +14,18 @@ spec: output: image: image-registry.openshift-image-registry.svc:5000/build-examples/buildpack-nodejs-build-heroku --- -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: BuildRun metadata: name: buildpack-nodejs-build-heroku-1 spec: - buildRef: + build: name: buildpack-nodejs-build-heroku --- -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: BuildRun metadata: name: buildpack-nodejs-build-heroku-2 spec: - buildRef: + build: name: buildpack-nodejs-build-heroku diff --git a/frontend/packages/shipwright-plugin/integration-tests/testData/builds/shipwrightBuildRun.yaml b/frontend/packages/shipwright-plugin/integration-tests/testData/builds/shipwrightBuildRun.yaml index 919bb011078..c0438dd2456 100644 --- a/frontend/packages/shipwright-plugin/integration-tests/testData/builds/shipwrightBuildRun.yaml +++ b/frontend/packages/shipwright-plugin/integration-tests/testData/builds/shipwrightBuildRun.yaml @@ -1,14 +1,17 @@ -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: BuildRun metadata: name: buildpack-nodejs-buildrun-123 spec: - buildSpec: - source: - url: https://github.com/shipwright-io/sample-nodejs - contextDir: source-build - strategy: - kind: BuildStrategy - name: buildpacks-v3 - output: - image: image-registry.openshift-image-registry.svc:5000/christoph/buildpack-nodejs-build:latest + build: + spec: + source: + type: Git + git: + url: https://github.com/shipwright-io/sample-nodejs + contextDir: source-build + strategy: + kind: BuildStrategy + name: buildpacks-v3 + output: + image: image-registry.openshift-image-registry.svc:5000/christoph/buildpack-nodejs-build:latest diff --git a/frontend/packages/shipwright-plugin/integration-tests/testData/builds/shipwrightBuildStrategies.yaml b/frontend/packages/shipwright-plugin/integration-tests/testData/builds/shipwrightBuildStrategies.yaml index 69d6bf2c365..922ad1f3830 100644 --- a/frontend/packages/shipwright-plugin/integration-tests/testData/builds/shipwrightBuildStrategies.yaml +++ b/frontend/packages/shipwright-plugin/integration-tests/testData/builds/shipwrightBuildStrategies.yaml @@ -11,7 +11,7 @@ # --- -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: BuildStrategy metadata: name: buildah @@ -166,14 +166,14 @@ spec: - $(params.registries-search[*]) resources: limits: - cpu: "1" + cpu: '1' memory: 2Gi requests: cpu: 250m memory: 65Mi parameters: - name: build-args - description: "The values for the args in the Dockerfile. Values must be in the format KEY=VALUE." + description: 'The values for the args in the Dockerfile. Values must be in the format KEY=VALUE.' type: array defaults: [] - name: registries-block @@ -192,14 +192,14 @@ spec: - quay.io --- -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: BuildStrategy metadata: name: buildkit spec: parameters: - name: build-args - description: "The values for the ARGs in the Dockerfile. Values must be in the format KEY=VALUE." + description: 'The values for the ARGs in the Dockerfile. Values must be in the format KEY=VALUE.' type: array defaults: [] - name: cache @@ -208,14 +208,14 @@ spec: default: registry - name: insecure-registry type: string - description: "Enables the push to an insecure registry" - default: "false" + description: 'Enables the push to an insecure registry' + default: 'false' - name: platforms description: "Build the image for different platforms. By default, the image is built for the platform used by the FROM image. If that is present for multiple platforms, then it is built for the environment's platform." type: array defaults: [] - name: secrets - description: "The secrets to pass to the build. Values must be in the format ID=FILE_CONTENT." + description: 'The secrets to pass to the build. Values must be in the format ID=FILE_CONTENT.' type: array defaults: [] buildSteps: @@ -340,7 +340,7 @@ spec: - $(params.secrets[*]) --- -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: BuildStrategy metadata: name: buildpacks-v3-heroku @@ -348,7 +348,7 @@ spec: parameters: - name: platform-api-version description: The referenced version is the minimum version that all relevant buildpack implementations support. - default: "0.4" + default: '0.4' buildSteps: - name: build-and-push image: heroku/buildpacks:18 @@ -376,7 +376,7 @@ spec: memory: 65Mi --- -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: BuildStrategy metadata: name: buildpacks-v3 @@ -384,7 +384,7 @@ spec: parameters: - name: platform-api-version description: The referenced version is the minimum version that all relevant buildpack implementations support. - default: "0.4" + default: '0.4' buildSteps: - name: build-and-push image: docker.io/paketobuildpacks/builder:full @@ -416,7 +416,7 @@ spec: # This Build Strategy will intentionally fail if the image has any # critical CVEs. It will not be pushed into the destination registry # if any critical vulnerabilities are found. -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: BuildStrategy metadata: name: kaniko-trivy @@ -517,7 +517,7 @@ spec: mountPath: /kaniko/oci-image-layout --- -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: BuildStrategy metadata: name: kaniko @@ -579,24 +579,24 @@ spec: mountPath: /kaniko/oci-image-layout --- -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: BuildStrategy metadata: name: ko spec: parameters: - name: go-flags - description: "Value for the GOFLAGS environment variable." - default: "" + description: 'Value for the GOFLAGS environment variable.' + default: '' - name: go-version - description: "Version of Go, must match a tag from https://hub.docker.com/_/golang?tab=tags" - default: "1.17" + description: 'Version of Go, must match a tag from https://hub.docker.com/_/golang?tab=tags' + default: '1.17' - name: ko-version description: "Version of ko, must be either 'latest', or a release name from https://github.com/google/ko/releases" default: latest - name: package-directory - description: "The directory inside the context directory containing the main package." - default: "." + description: 'The directory inside the context directory containing the main package.' + default: '.' - name: target-platform description: "Target platform to be built. For example: 'linux/arm64'. Multiple platforms can be provided separated by comma, for example: 'linux/arm64,linux/amd64'. The value 'all' will build all platforms supported by the base image. The value 'current' will build the platform on which the build runs." default: current @@ -694,7 +694,7 @@ spec: memory: 65Mi --- -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: BuildStrategy metadata: name: source-to-image-redhat @@ -826,7 +826,7 @@ spec: - quay.io --- -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: BuildStrategy metadata: name: source-to-image diff --git a/frontend/packages/shipwright-plugin/package.json b/frontend/packages/shipwright-plugin/package.json index 8c98bc20b21..9bf3579ceff 100644 --- a/frontend/packages/shipwright-plugin/package.json +++ b/frontend/packages/shipwright-plugin/package.json @@ -8,7 +8,9 @@ "lint": "yarn --cwd ../.. eslint packages/shipwright-plugin", "test": "yarn --cwd ../.. test packages/shipwright-plugin" }, - "dependencies": {}, + "dependencies": { + "@kubernetes-models/shipwright": "^0.2.0" + }, "consolePlugin": { "entry": "src/plugin.ts", "exposedModules": { diff --git a/frontend/packages/shipwright-plugin/samples-v1alpha1/0-namespace.yaml b/frontend/packages/shipwright-plugin/samples-v1alpha1/0-namespace.yaml new file mode 100644 index 00000000000..63c750a4211 --- /dev/null +++ b/frontend/packages/shipwright-plugin/samples-v1alpha1/0-namespace.yaml @@ -0,0 +1,4 @@ +kind: Namespace +apiVersion: v1 +metadata: + name: build-examples-alpha diff --git a/frontend/packages/shipwright-plugin/samples/1-buildstrategies.yaml b/frontend/packages/shipwright-plugin/samples-v1alpha1/1-buildstrategies.yaml similarity index 96% rename from frontend/packages/shipwright-plugin/samples/1-buildstrategies.yaml rename to frontend/packages/shipwright-plugin/samples-v1alpha1/1-buildstrategies.yaml index 50f3fdc0df8..21d83ae6d2a 100644 --- a/frontend/packages/shipwright-plugin/samples/1-buildstrategies.yaml +++ b/frontend/packages/shipwright-plugin/samples-v1alpha1/1-buildstrategies.yaml @@ -1,6 +1,6 @@ # Based on https://github.com/shipwright-io/build/releases/download/v0.9.0/sample-strategies.yaml # -# - Changed ClusterBuildStrategy to BuildStrategy, add namespace `build-examples` +# - Changed ClusterBuildStrategy to BuildStrategy, add namespace `build-examples-alpha` # - Removed all securityContext sections # - Removed `prepare` (chown -R 1000:1000 /tekton/home) from # buildpacks-v3, @@ -14,7 +14,7 @@ apiVersion: shipwright.io/v1alpha1 kind: BuildStrategy metadata: - namespace: build-examples + namespace: build-examples-alpha name: buildah spec: buildSteps: @@ -178,14 +178,14 @@ spec: - $(params.registries-search[*]) resources: limits: - cpu: "1" + cpu: '1' memory: 2Gi requests: cpu: 250m memory: 65Mi parameters: - name: build-args - description: "The values for the args in the Dockerfile. Values must be in the format KEY=VALUE." + description: 'The values for the args in the Dockerfile. Values must be in the format KEY=VALUE.' type: array defaults: [] - name: registries-block @@ -207,12 +207,12 @@ spec: apiVersion: shipwright.io/v1alpha1 kind: BuildStrategy metadata: - namespace: build-examples + namespace: build-examples-alpha name: buildkit spec: parameters: - name: build-args - description: "The values for the ARGs in the Dockerfile. Values must be in the format KEY=VALUE." + description: 'The values for the ARGs in the Dockerfile. Values must be in the format KEY=VALUE.' type: array defaults: [] - name: cache @@ -221,14 +221,14 @@ spec: default: registry - name: insecure-registry type: string - description: "Enables the push to an insecure registry" - default: "false" + description: 'Enables the push to an insecure registry' + default: 'false' - name: platforms description: "Build the image for different platforms. By default, the image is built for the platform used by the FROM image. If that is present for multiple platforms, then it is built for the environment's platform." type: array defaults: [] - name: secrets - description: "The secrets to pass to the build. Values must be in the format ID=FILE_CONTENT." + description: 'The secrets to pass to the build. Values must be in the format ID=FILE_CONTENT.' type: array defaults: [] buildSteps: @@ -370,13 +370,13 @@ spec: apiVersion: shipwright.io/v1alpha1 kind: BuildStrategy metadata: - namespace: build-examples + namespace: build-examples-alpha name: buildpacks-v3-heroku spec: parameters: - name: platform-api-version description: The referenced version is the minimum version that all relevant buildpack implementations support. - default: "0.4" + default: '0.4' buildSteps: - name: build-and-push image: heroku/buildpacks:18 @@ -407,13 +407,13 @@ spec: apiVersion: shipwright.io/v1alpha1 kind: BuildStrategy metadata: - namespace: build-examples + namespace: build-examples-alpha name: buildpacks-v3 spec: parameters: - name: platform-api-version description: The referenced version is the minimum version that all relevant buildpack implementations support. - default: "0.4" + default: '0.4' buildSteps: - name: build-and-push image: docker.io/paketobuildpacks/builder:full @@ -448,7 +448,7 @@ spec: apiVersion: shipwright.io/v1alpha1 kind: BuildStrategy metadata: - namespace: build-examples + namespace: build-examples-alpha name: kaniko-trivy spec: buildSteps: @@ -552,7 +552,7 @@ spec: apiVersion: shipwright.io/v1alpha1 kind: BuildStrategy metadata: - namespace: build-examples + namespace: build-examples-alpha name: kaniko spec: buildSteps: @@ -617,22 +617,22 @@ spec: apiVersion: shipwright.io/v1alpha1 kind: BuildStrategy metadata: - namespace: build-examples + namespace: build-examples-alpha name: ko spec: parameters: - name: go-flags - description: "Value for the GOFLAGS environment variable." - default: "" + description: 'Value for the GOFLAGS environment variable.' + default: '' - name: go-version - description: "Version of Go, must match a tag from https://hub.docker.com/_/golang?tab=tags" - default: "1.17" + description: 'Version of Go, must match a tag from https://hub.docker.com/_/golang?tab=tags' + default: '1.17' - name: ko-version description: "Version of ko, must be either 'latest', or a release name from https://github.com/google/ko/releases" default: latest - name: package-directory - description: "The directory inside the context directory containing the main package." - default: "." + description: 'The directory inside the context directory containing the main package.' + default: '.' - name: target-platform description: "Target platform to be built. For example: 'linux/arm64'. Multiple platforms can be provided separated by comma, for example: 'linux/arm64,linux/amd64'. The value 'all' will build all platforms supported by the base image. The value 'current' will build the platform on which the build runs." default: current @@ -746,7 +746,7 @@ spec: apiVersion: shipwright.io/v1alpha1 kind: BuildStrategy metadata: - namespace: build-examples + namespace: build-examples-alpha name: source-to-image-redhat spec: buildSteps: @@ -888,7 +888,7 @@ spec: apiVersion: shipwright.io/v1alpha1 kind: BuildStrategy metadata: - namespace: build-examples + namespace: build-examples-alpha name: source-to-image spec: buildSteps: diff --git a/frontend/packages/shipwright-plugin/samples/2-buildpack-nodejs-build.yaml b/frontend/packages/shipwright-plugin/samples-v1alpha1/2-buildpack-nodejs-build.yaml similarity index 81% rename from frontend/packages/shipwright-plugin/samples/2-buildpack-nodejs-build.yaml rename to frontend/packages/shipwright-plugin/samples-v1alpha1/2-buildpack-nodejs-build.yaml index 72ce88e889a..6e3fd334ee4 100644 --- a/frontend/packages/shipwright-plugin/samples/2-buildpack-nodejs-build.yaml +++ b/frontend/packages/shipwright-plugin/samples-v1alpha1/2-buildpack-nodejs-build.yaml @@ -1,7 +1,7 @@ apiVersion: shipwright.io/v1alpha1 kind: Build metadata: - namespace: build-examples + namespace: build-examples-alpha name: buildpack-nodejs-build spec: source: @@ -11,12 +11,12 @@ spec: name: buildpacks-v3 kind: BuildStrategy output: - image: image-registry.openshift-image-registry.svc:5000/build-examples/buildpack-nodejs-build + image: image-registry.openshift-image-registry.svc:5000/build-examples-alpha/buildpack-nodejs-build --- apiVersion: shipwright.io/v1alpha1 kind: BuildRun metadata: - namespace: build-examples + namespace: build-examples-alpha generateName: buildpack-nodejs-build- spec: buildRef: @@ -25,7 +25,7 @@ spec: apiVersion: shipwright.io/v1alpha1 kind: BuildRun metadata: - namespace: build-examples + namespace: build-examples-alpha generateName: buildpack-nodejs-build- spec: buildRef: diff --git a/frontend/packages/shipwright-plugin/samples-v1alpha1/201-full-openshift-deployment-example.yaml b/frontend/packages/shipwright-plugin/samples-v1alpha1/201-full-openshift-deployment-example.yaml new file mode 100644 index 00000000000..10ce6c76c0b --- /dev/null +++ b/frontend/packages/shipwright-plugin/samples-v1alpha1/201-full-openshift-deployment-example.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: sw-deployment-example + namespace: build-examples-alpha + labels: + app.kubernetes.io/part-of: sw-deployment-app +spec: + selector: + matchLabels: + app: sw-deployment-example + replicas: 1 + template: + metadata: + labels: + app: sw-deployment-example + spec: + containers: + - name: container + image: >- + image-registry.openshift-image-registry.svc:5000/build-examples-alpha/sw-deployment-example:latest +--- +apiVersion: shipwright.io/v1alpha1 +kind: Build +metadata: + name: sw-deployment-example-build + namespace: build-examples-alpha + labels: + app.kubernetes.io/part-of: sw-deployment-app +spec: + source: + url: https://github.com/shipwright-io/sample-nodejs + contextDir: source-build + strategy: + name: buildpacks-v3 + kind: BuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples-alpha/sw-deployment-example-build +--- +apiVersion: shipwright.io/v1alpha1 +kind: BuildRun +metadata: + namespace: build-examples-alpha + generateName: sw-deployment-example-build- + labels: + app.kubernetes.io/part-of: sw-deployment-app +spec: + buildRef: + name: sw-deployment-example-build +--- +apiVersion: v1 +kind: Service +metadata: + name: sw-deployment-example-service + labels: + app.kubernetes.io/component: sw-deployment-example +spec: + ports: + - name: 8080-tcp + protocol: TCP + port: 8080 + targetPort: 8080 + selector: + app: sw-deployment-example +--- +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + name: sw-deployment-example-route + labels: + app.kubernetes.io/component: sw-deployment-example +spec: + to: + kind: Service + name: sw-deployment-example-service + weight: 100 + port: + targetPort: 8080-tcp diff --git a/frontend/packages/shipwright-plugin/samples-v1alpha1/202-full-openshift-deploymentconfig-example.yaml b/frontend/packages/shipwright-plugin/samples-v1alpha1/202-full-openshift-deploymentconfig-example.yaml new file mode 100644 index 00000000000..efc000659e0 --- /dev/null +++ b/frontend/packages/shipwright-plugin/samples-v1alpha1/202-full-openshift-deploymentconfig-example.yaml @@ -0,0 +1,88 @@ +apiVersion: apps.openshift.io/v1 +kind: DeploymentConfig +metadata: + namespace: build-examples-alpha + name: sw-deploymentconfig-example + labels: + app.kubernetes.io/part-of: sw-deploymentconfig-app +spec: + selector: + app: sw-deploymentconfig-example + replicas: 1 + template: + metadata: + labels: + app: sw-deploymentconfig-example + spec: + containers: + - name: container + image: >- + image-registry.openshift-image-registry.svc:5000/build-examples-alpha/sw-deploymentconfig-example:latest + triggers: + - type: ConfigChange + - type: ImageChange + imageChangeParams: + automatic: true + containerNames: + - container + from: + kind: ImageStreamTag + namespace: build-examples-alpha + name: 'sw-deploymentconfig-example:latest' +--- +apiVersion: shipwright.io/v1alpha1 +kind: Build +metadata: + namespace: build-examples-alpha + name: sw-deploymentconfig-example-build + labels: + app.kubernetes.io/part-of: sw-deploymentconfig-app +spec: + source: + url: https://github.com/shipwright-io/sample-nodejs + contextDir: source-build + strategy: + name: buildpacks-v3 + kind: BuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples-alpha/sw-deploymentconfig-example +--- +apiVersion: shipwright.io/v1alpha1 +kind: BuildRun +metadata: + namespace: build-examples-alpha + generateName: sw-deploymentconfig-example-buildrun- + labels: + app.kubernetes.io/part-of: sw-deploymentconfig-app +spec: + buildRef: + name: sw-deploymentconfig-example-build +--- +apiVersion: v1 +kind: Service +metadata: + name: sw-deploymentconfig-example-service + labels: + app.kubernetes.io/component: sw-deploymentconfig-example +spec: + ports: + - name: 8080-tcp + protocol: TCP + port: 8080 + targetPort: 8080 + selector: + app: sw-deploymentconfig-example +--- +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + name: sw-deploymentconfig-example-route + labels: + app.kubernetes.io/component: sw-deploymentconfig-example +spec: + to: + kind: Service + name: sw-deploymentconfig-example-service + weight: 100 + port: + targetPort: 8080-tcp diff --git a/frontend/packages/shipwright-plugin/samples-v1alpha1/203-full-openshift-knative-service-example.yaml b/frontend/packages/shipwright-plugin/samples-v1alpha1/203-full-openshift-knative-service-example.yaml new file mode 100644 index 00000000000..e6206c4646b --- /dev/null +++ b/frontend/packages/shipwright-plugin/samples-v1alpha1/203-full-openshift-knative-service-example.yaml @@ -0,0 +1,45 @@ +apiVersion: serving.knative.dev/v1 +kind: Service +metadata: + namespace: build-examples-alpha + name: sw-knative-service-example + labels: + app.kubernetes.io/part-of: sw-knative-service-example +spec: + template: + metadata: + labels: + app.kubernetes.io/component: sw-knative-service-example + spec: + containers: + - name: container + image: >- + image-registry.openshift-image-registry.svc:5000/build-examples-alpha/sw-knative-service-example:latest +--- +apiVersion: shipwright.io/v1alpha1 +kind: Build +metadata: + namespace: build-examples-alpha + name: sw-knative-service-example-build + labels: + app.kubernetes.io/part-of: sw-knative-service-example +spec: + source: + url: https://github.com/shipwright-io/sample-nodejs + contextDir: source-build + strategy: + name: buildpacks-v3 + kind: BuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples-alpha/sw-knative-service-example +--- +apiVersion: shipwright.io/v1alpha1 +kind: BuildRun +metadata: + namespace: build-examples-alpha + generateName: sw-knative-service-example-buildrun- + labels: + app.kubernetes.io/part-of: sw-knative-service-example +spec: + buildRef: + name: sw-knative-service-example-build diff --git a/frontend/packages/shipwright-plugin/samples/3-buildpack-nodejs-build-heroku.yaml b/frontend/packages/shipwright-plugin/samples-v1alpha1/3-buildpack-nodejs-build-heroku.yaml similarity index 81% rename from frontend/packages/shipwright-plugin/samples/3-buildpack-nodejs-build-heroku.yaml rename to frontend/packages/shipwright-plugin/samples-v1alpha1/3-buildpack-nodejs-build-heroku.yaml index 70e45bb383a..40afd0215ad 100644 --- a/frontend/packages/shipwright-plugin/samples/3-buildpack-nodejs-build-heroku.yaml +++ b/frontend/packages/shipwright-plugin/samples-v1alpha1/3-buildpack-nodejs-build-heroku.yaml @@ -1,7 +1,7 @@ apiVersion: shipwright.io/v1alpha1 kind: Build metadata: - namespace: build-examples + namespace: build-examples-alpha name: buildpack-nodejs-build-heroku spec: source: @@ -11,12 +11,12 @@ spec: name: buildpacks-v3-heroku kind: BuildStrategy output: - image: image-registry.openshift-image-registry.svc:5000/build-examples/buildpack-nodejs-build-heroku + image: image-registry.openshift-image-registry.svc:5000/build-examples-alpha/buildpack-nodejs-build-heroku --- apiVersion: shipwright.io/v1alpha1 kind: BuildRun metadata: - namespace: build-examples + namespace: build-examples-alpha generateName: buildpack-nodejs-build-heroku- spec: buildRef: @@ -25,7 +25,7 @@ spec: apiVersion: shipwright.io/v1alpha1 kind: BuildRun metadata: - namespace: build-examples + namespace: build-examples-alpha generateName: buildpack-nodejs-build-heroku- spec: buildRef: diff --git a/frontend/packages/shipwright-plugin/samples/4-kaniko-golang-build.yaml b/frontend/packages/shipwright-plugin/samples-v1alpha1/4-kaniko-golang-build.yaml similarity index 81% rename from frontend/packages/shipwright-plugin/samples/4-kaniko-golang-build.yaml rename to frontend/packages/shipwright-plugin/samples-v1alpha1/4-kaniko-golang-build.yaml index fb6d52b91be..b9c95ae842e 100644 --- a/frontend/packages/shipwright-plugin/samples/4-kaniko-golang-build.yaml +++ b/frontend/packages/shipwright-plugin/samples-v1alpha1/4-kaniko-golang-build.yaml @@ -1,7 +1,7 @@ apiVersion: shipwright.io/v1alpha1 kind: Build metadata: - namespace: build-examples + namespace: build-examples-alpha name: kaniko-golang-build spec: source: @@ -12,12 +12,12 @@ spec: kind: BuildStrategy dockerfile: Dockerfile output: - image: image-registry.openshift-image-registry.svc:5000/build-examples/kaniko-golang-build + image: image-registry.openshift-image-registry.svc:5000/build-examples-alpha/kaniko-golang-build --- apiVersion: shipwright.io/v1alpha1 kind: BuildRun metadata: - namespace: build-examples + namespace: build-examples-alpha generateName: kaniko-golang-build- spec: buildRef: @@ -26,7 +26,7 @@ spec: apiVersion: shipwright.io/v1alpha1 kind: BuildRun metadata: - namespace: build-examples + namespace: build-examples-alpha generateName: kaniko-golang-build- spec: buildRef: diff --git a/frontend/packages/shipwright-plugin/samples/5-ko-build.yaml b/frontend/packages/shipwright-plugin/samples-v1alpha1/5-ko-build.yaml similarity index 77% rename from frontend/packages/shipwright-plugin/samples/5-ko-build.yaml rename to frontend/packages/shipwright-plugin/samples-v1alpha1/5-ko-build.yaml index 5690e78314b..505afef60d7 100644 --- a/frontend/packages/shipwright-plugin/samples/5-ko-build.yaml +++ b/frontend/packages/shipwright-plugin/samples-v1alpha1/5-ko-build.yaml @@ -1,14 +1,14 @@ apiVersion: shipwright.io/v1alpha1 kind: Build metadata: - namespace: build-examples + namespace: build-examples-alpha name: ko-build spec: paramValues: - name: go-flags - value: "-v -mod=vendor -ldflags=-w" + value: '-v -mod=vendor -ldflags=-w' - name: go-version - value: "1.17" + value: '1.17' - name: package-directory value: ./cmd/shipwright-build-controller source: @@ -17,12 +17,12 @@ spec: name: ko kind: BuildStrategy output: - image: image-registry.openshift-image-registry.svc:5000/build-examples/ko-build + image: image-registry.openshift-image-registry.svc:5000/build-examples-alpha/ko-build --- apiVersion: shipwright.io/v1alpha1 kind: BuildRun metadata: - namespace: build-examples + namespace: build-examples-alpha generateName: ko-build- spec: buildRef: @@ -31,7 +31,7 @@ spec: apiVersion: shipwright.io/v1alpha1 kind: BuildRun metadata: - namespace: build-examples + namespace: build-examples-alpha generateName: ko-build- spec: buildRef: diff --git a/frontend/packages/shipwright-plugin/samples/6-standalone-buildrun.yaml b/frontend/packages/shipwright-plugin/samples-v1alpha1/6-standalone-buildrun.yaml similarity index 100% rename from frontend/packages/shipwright-plugin/samples/6-standalone-buildrun.yaml rename to frontend/packages/shipwright-plugin/samples-v1alpha1/6-standalone-buildrun.yaml diff --git a/frontend/packages/shipwright-plugin/samples/7-donothing-fastrun-build.yaml b/frontend/packages/shipwright-plugin/samples-v1alpha1/7-donothing-fastrun-build.yaml similarity index 83% rename from frontend/packages/shipwright-plugin/samples/7-donothing-fastrun-build.yaml rename to frontend/packages/shipwright-plugin/samples-v1alpha1/7-donothing-fastrun-build.yaml index e82ab49de46..99cce1c2cf0 100644 --- a/frontend/packages/shipwright-plugin/samples/7-donothing-fastrun-build.yaml +++ b/frontend/packages/shipwright-plugin/samples-v1alpha1/7-donothing-fastrun-build.yaml @@ -1,7 +1,7 @@ apiVersion: shipwright.io/v1alpha1 kind: BuildStrategy metadata: - namespace: build-examples + namespace: build-examples-alpha name: donothing-build-strategy spec: buildSteps: @@ -14,7 +14,7 @@ spec: apiVersion: shipwright.io/v1alpha1 kind: Build metadata: - namespace: build-examples + namespace: build-examples-alpha name: donothing-build spec: source: @@ -24,12 +24,12 @@ spec: name: donothing-build-strategy kind: BuildStrategy output: - image: image-registry.openshift-image-registry.svc:5000/build-examples/donothing-build + image: image-registry.openshift-image-registry.svc:5000/build-examples-alpha/donothing-build --- apiVersion: shipwright.io/v1alpha1 kind: BuildRun metadata: - namespace: build-examples + namespace: build-examples-alpha generateName: donothing-build- spec: buildRef: @@ -38,7 +38,7 @@ spec: apiVersion: shipwright.io/v1alpha1 kind: BuildRun metadata: - namespace: build-examples + namespace: build-examples-alpha generateName: donothing-build- spec: buildRef: diff --git a/frontend/packages/shipwright-plugin/samples/8-invalid-builds.yaml b/frontend/packages/shipwright-plugin/samples-v1alpha1/8-invalid-builds.yaml similarity index 100% rename from frontend/packages/shipwright-plugin/samples/8-invalid-builds.yaml rename to frontend/packages/shipwright-plugin/samples-v1alpha1/8-invalid-builds.yaml diff --git a/frontend/packages/shipwright-plugin/samples/9-invalid-buildruns.yaml b/frontend/packages/shipwright-plugin/samples-v1alpha1/9-invalid-buildruns.yaml similarity index 100% rename from frontend/packages/shipwright-plugin/samples/9-invalid-buildruns.yaml rename to frontend/packages/shipwright-plugin/samples-v1alpha1/9-invalid-buildruns.yaml diff --git a/frontend/packages/shipwright-plugin/samples/0-namespace.yaml b/frontend/packages/shipwright-plugin/samples-v1beta1/0-namespace.yaml similarity index 100% rename from frontend/packages/shipwright-plugin/samples/0-namespace.yaml rename to frontend/packages/shipwright-plugin/samples-v1beta1/0-namespace.yaml diff --git a/frontend/packages/shipwright-plugin/samples-v1beta1/1-buildstrategies.yaml b/frontend/packages/shipwright-plugin/samples-v1beta1/1-buildstrategies.yaml new file mode 100644 index 00000000000..2d64c617422 --- /dev/null +++ b/frontend/packages/shipwright-plugin/samples-v1beta1/1-buildstrategies.yaml @@ -0,0 +1,972 @@ +# Based on https://github.com/shipwright-io/build/releases/download/v0.9.0/sample-strategies.yaml +# +# - Changed ClusterBuildStrategy to BuildStrategy, add namespace `build-examples` +# - Removed all securityContext sections +# - Removed `prepare` (chown -R 1000:1000 /tekton/home) from +# buildpacks-v3, +# buildpacks-v3-heroku, +# buildkit +# ko +# - Removed `seccomp` annotation in `buildkit-build` +# + +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildStrategy +metadata: + namespace: build-examples + name: buildah +spec: + steps: + - name: build-and-push + image: quay.io/containers/buildah:v1.23.1 + workingDir: $(params.shp-source-root) + command: + - /bin/bash + args: + - -c + - | + set -euo pipefail + + # Parse parameters + context= + dockerfile= + image= + buildArgs=() + inBuildArgs=false + registriesBlock="" + inRegistriesBlock=false + registriesInsecure="" + inRegistriesInsecure=false + registriesSearch="" + inRegistriesSearch=false + tlsVerify=true + while [[ $# -gt 0 ]]; do + arg="$1" + shift + + if [ "${arg}" == "--context" ]; then + inBuildArgs=false + inRegistriesBlock=false + inRegistriesInsecure=false + inRegistriesSearch=false + context="$1" + shift + elif [ "${arg}" == "--dockerfile" ]; then + inBuildArgs=false + inRegistriesBlock=false + inRegistriesInsecure=false + inRegistriesSearch=false + dockerfile="$1" + shift + elif [ "${arg}" == "--image" ]; then + inBuildArgs=false + inRegistriesBlock=false + inRegistriesInsecure=false + inRegistriesSearch=false + image="$1" + shift + elif [ "${arg}" == "--build-args" ]; then + inBuildArgs=true + inRegistriesBlock=false + inRegistriesInsecure=false + inRegistriesSearch=false + elif [ "${arg}" == "--registries-block" ]; then + inRegistriesBlock=true + inBuildArgs=false + inRegistriesInsecure=false + inRegistriesSearch=false + elif [ "${arg}" == "--registries-insecure" ]; then + inRegistriesInsecure=true + inBuildArgs=false + inRegistriesBlock=false + inRegistriesSearch=false + elif [ "${arg}" == "--registries-search" ]; then + inRegistriesSearch=true + inBuildArgs=false + inRegistriesBlock=false + inRegistriesInsecure=false + elif [ "${inBuildArgs}" == "true" ]; then + buildArgs+=("--build-arg" "${arg}") + elif [ "${inRegistriesBlock}" == "true" ]; then + registriesBlock="${registriesBlock}'${arg}', " + elif [ "${inRegistriesInsecure}" == "true" ]; then + registriesInsecure="${registriesInsecure}'${arg}', " + + # This assumes that the image is passed before the insecure registries which is fair in this context + if [[ ${image} == ${arg}/* ]]; then + tlsVerify=false + fi + elif [ "${inRegistriesSearch}" == "true" ]; then + registriesSearch="${registriesSearch}'${arg}', " + else + echo "Invalid usage" + exit 1 + fi + done + + # Verify the existence of the context directory + if [ ! -d "${context}" ]; then + echo -e "The context directory '${context}' does not exist." + echo -n "ContextDirNotFound" > '$(results.shp-error-reason.path)' + echo -n "The context directory '${context}' does not exist." > '$(results.shp-error-message.path)' + exit 1 + fi + cd "${context}" + + # Verify the existence of the Dockerfile + if [ ! -f "${dockerfile}" ]; then + echo -e "The Dockerfile '${dockerfile}' does not exist." + echo -n "DockerfileNotFound" > '$(results.shp-error-reason.path)' + echo -n "The Dockerfile '${dockerfile}' does not exist." > '$(results.shp-error-message.path)' + exit 1 + fi + + echo "[INFO] Creating registries config file..." + if [ "${registriesSearch}" != "" ]; then + cat <>/tmp/registries.conf + [registries.search] + registries = [${registriesSearch::-2}] + + EOF + fi + if [ "${registriesInsecure}" != "" ]; then + cat <>/tmp/registries.conf + [registries.insecure] + registries = [${registriesInsecure::-2}] + + EOF + fi + if [ "${registriesBlock}" != "" ]; then + cat <>/tmp/registries.conf + [registries.block] + registries = [${registriesBlock::-2}] + + EOF + fi + + # Building the image + echo "[INFO] Building image ${image}" + buildah bud "${buildArgs[@]}" \ + --registries-conf=/tmp/registries.conf \ + --tag="${image}" \ + --file="${dockerfile}" \ + . + + # Push the image + echo "[INFO] Pushing image ${image}" + buildah push \ + --digestfile='$(results.shp-image-digest.path)' \ + --tls-verify="${tlsVerify}" \ + "${image}" \ + "docker://${image}" + # That's the separator between the shell script and its args + - -- + - --context + - $(params.shp-source-context) + - --dockerfile + - $(build.dockerfile) + - --image + - $(params.shp-output-image) + - --build-args + - $(params.build-args[*]) + - --registries-block + - $(params.registries-block[*]) + - --registries-insecure + - $(params.registries-insecure[*]) + - --registries-search + - $(params.registries-search[*]) + resources: + limits: + cpu: '1' + memory: 2Gi + requests: + cpu: 250m + memory: 65Mi + parameters: + - name: build-args + description: 'The values for the args in the Dockerfile. Values must be in the format KEY=VALUE.' + type: array + defaults: [] + - name: registries-block + description: The registries that need to block pull access. + type: array + defaults: [] + - name: registries-insecure + description: The fully-qualified name of insecure registries. An insecure registry is one that does not have a valid SSL certificate or only supports HTTP. + type: array + defaults: [] + - name: registries-search + description: The registries for searching short name images such as `golang:latest`. + type: array + defaults: + - docker.io + - quay.io + +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildStrategy +metadata: + namespace: build-examples + name: buildkit +spec: + parameters: + - name: build-args + description: 'The values for the ARGs in the Dockerfile. Values must be in the format KEY=VALUE.' + type: array + defaults: [] + - name: cache + description: "Configure BuildKit's cache usage. Allowed values are 'disabled' and 'registry'. The default is 'registry'." + type: string + default: registry + - name: insecure-registry + type: string + description: 'Enables the push to an insecure registry' + default: 'false' + - name: platforms + description: "Build the image for different platforms. By default, the image is built for the platform used by the FROM image. If that is present for multiple platforms, then it is built for the environment's platform." + type: array + defaults: [] + - name: secrets + description: 'The secrets to pass to the build. Values must be in the format ID=FILE_CONTENT.' + type: array + defaults: [] + - name: dockerfile + description: The path to the Dockerfile to be used for building the image. + type: string + default: 'Dockerfile' + steps: + - name: build-and-push + image: moby/buildkit:nightly-rootless + imagePullPolicy: Always + workingDir: $(params.shp-source-root) + env: + - name: DOCKER_CONFIG + value: /tekton/home/.docker + - name: HOME + value: /tekton/home + # See https://github.com/moby/buildkit/blob/master/docs/rootless.md#about---oci-worker-no-process-sandbox for more information + - name: BUILDKITD_FLAGS + value: --oci-worker-no-process-sandbox + - name: PARAM_SOURCE_CONTEXT + value: $(params.shp-source-context) + - name: PARAM_DOCKERFILE + value: $(params.DOCKERFILE) + - name: PARAM_OUTPUT_IMAGE + value: $(params.shp-output-image) + - name: PARAM_INSECURE_REGISTRY + value: $(params.insecure-registry) + - name: PARAM_CACHE + value: $(params.cache) + command: + - /bin/ash + args: + - -c + - | + set -euo pipefail + + # Verify the existence of the context directory + if [ ! -d "${PARAM_SOURCE_CONTEXT}" ]; then + echo -e "The context directory '${PARAM_SOURCE_CONTEXT}' does not exist." + echo -n "ContextDirNotFound" > '$(results.shp-error-reason.path)' + echo -n "The context directory '${PARAM_SOURCE_CONTEXT}' does not exist." > '$(results.shp-error-message.path)' + exit 1 + fi + + # Prepare the file arguments + DOCKERFILE_PATH="${PARAM_SOURCE_CONTEXT}/${PARAM_DOCKERFILE}" + DOCKERFILE_DIR="$(dirname "${DOCKERFILE_PATH}")" + DOCKERFILE_NAME="$(basename "${DOCKERFILE_PATH}")" + + # Verify the existence of the Dockerfile + if [ ! -f "${DOCKERFILE_PATH}" ]; then + echo -e "The Dockerfile '${DOCKERFILE_PATH}' does not exist." + echo -n "DockerfileNotFound" > '$(results.shp-error-reason.path)' + echo -n "The Dockerfile '${DOCKERFILE_PATH}' does not exist." > '$(results.shp-error-message.path)' + exit 1 + fi + + # We only have ash here and therefore no bash arrays to help add dynamic arguments (the build-args) to the build command. + + echo "#!/bin/ash" > /tmp/run.sh + echo "set -euo pipefail" >> /tmp/run.sh + echo "buildctl-daemonless.sh \\" >> /tmp/run.sh + echo "build \\" >> /tmp/run.sh + echo "--progress=plain \\" >> /tmp/run.sh + echo "--frontend=dockerfile.v0 \\" >> /tmp/run.sh + echo "--opt=filename=\"${DOCKERFILE_NAME}\" \\" >> /tmp/run.sh + echo "--local=context=\"${PARAM_SOURCE_CONTEXT}\" \\" >> /tmp/run.sh + echo "--local=dockerfile=\"${DOCKERFILE_DIR}\" \\" >> /tmp/run.sh + echo "--output=type=image,name=\"${PARAM_OUTPUT_IMAGE}\",push=true,registry.insecure=\"${PARAM_INSECURE_REGISTRY}\" \\" >> /tmp/run.sh + if [ "${PARAM_CACHE}" == "registry" ]; then + echo "--export-cache=type=inline \\" >> /tmp/run.sh + echo "--import-cache=type=registry,ref=\"${PARAM_OUTPUT_IMAGE}\" \\" >> /tmp/run.sh + elif [ "${PARAM_CACHE}" == "disabled" ]; then + echo "--no-cache \\" >> /tmp/run.sh + else + echo -e "An invalid value for the parameter 'cache' has been provided: '${PARAM_CACHE}'. Allowed values are 'disabled' and 'registry'." + echo -n "InvalidParameterValue" > '$(results.shp-error-reason.path)' + echo -n "An invalid value for the parameter 'cache' has been provided: '${PARAM_CACHE}'. Allowed values are 'disabled' and 'registry'." > '$(results.shp-error-message.path)' + exit 1 + fi + + stage="" + platforms="" + for a in "$@" + do + if [ "${a}" == "--build-args" ]; then + stage=build-args + elif [ "${a}" == "--platforms" ]; then + stage=platforms + elif [ "${a}" == "--secrets" ]; then + stage=secrets + elif [ "${stage}" == "build-args" ]; then + echo "--opt=\"build-arg:${a}\" \\" >> /tmp/run.sh + elif [ "${stage}" == "platforms" ]; then + if [ "${platforms}" == "" ]; then + platforms="${a}" + else + platforms="${platforms},${a}" + fi + elif [ "${stage}" == "secrets" ]; then + # Split ID=FILE_CONTENT into variables id and data + + # using head because the data could be multiline + id="$(echo "${a}" | head -1 | sed 's/=.*//')" + + # This is hacky, we remove the suffix ${id}= from all lines of the data. + # If the data would be multiple lines and a line would start with ${id}= + # then we would remove it. We could force users to give us the secret + # base64 encoded. But ultimately, the best solution might be if the user + # mounts the secret and just gives us the path here. + data="$(echo "${a}" | sed "s/^${id}=//")" + + # Write the secret data into a temporary file, once we have volume support + # in the build strategy, we should use a memory based emptyDir for this. + echo -n "${data}" > "/tmp/secret_${id}" + + # Add the secret argument + echo "--secret id=${id},src="/tmp/secret_${id}" \\" >> /tmp/run.sh + fi + done + + if [ "${platforms}" != "" ]; then + echo "--opt=\"platform=${platforms}\" \\" >> /tmp/run.sh + fi + + echo "--metadata-file /tmp/image-metadata.json" >> /tmp/run.sh + + chmod +x /tmp/run.sh + /tmp/run.sh + + # Store the image digest + grep containerimage.digest /tmp/image-metadata.json | sed -E 's/.*containerimage.digest":\s*"([^"]*).*/\1/' | tr -d '\n' > '$(results.shp-image-digest.path)' + # That's the separator between the shell script and its args + - -- + - --build-args + - $(params.build-args[*]) + - --platforms + - $(params.platforms[*]) + - --secrets + - $(params.secrets[*]) + +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildStrategy +metadata: + namespace: build-examples + name: buildpacks-v3-heroku +spec: + parameters: + - name: platform-api-version + description: The referenced version is the minimum version that all relevant buildpack implementations support. + default: '0.4' + steps: + - name: build-and-push + image: heroku/buildpacks:18 + env: + - name: CNB_PLATFORM_API + value: $(params.platform-api-version) + - name: PARAM_SOURCE_CONTEXT + value: $(params.shp-source-context) + - name: PARAM_OUTPUT_IMAGE + value: $(params.shp-output-image) + command: + - /bin/bash + args: + - -c + - "set -euo pipefail\n\necho \"> Processing environment variables...\"\nENV_DIR=\"/platform/env\"\n\nenvs=($(env))\n\n# Denying the creation of non required files from system environments.\n# The creation of a file named PATH (corresponding to PATH system environment)\n# caused failure for python source during pip install (https://github.com/Azure-Samples/python-docs-hello-world)\nblock_list=(\"PATH\" \"HOSTNAME\" \"PWD\" \"_\" \"SHLVL\" \"HOME\" \"\")\n\nfor env in \"${envs[@]}\"; do\n blocked=false\n\n IFS='=' read -r key value string <<< \"$env\"\n\n for str in \"${block_list[@]}\"; do\n if [[ \"$key\" == \"$str\" ]]; then\n blocked=true\n break\n fi\n done\n\n if [ \"$blocked\" == \"false\" ]; then\n path=\"${ENV_DIR}/${key}\"\n echo -n \"$value\" > \"$path\"\n fi\ndone\n\nLAYERS_DIR=/tmp/layers\nCACHE_DIR=/tmp/cache\n\nmkdir \"$CACHE_DIR\" \"$LAYERS_DIR\"\n\nfunction anounce_phase {\n printf \"===> %s\\n\" \"$1\" \n}\n\nanounce_phase \"DETECTING\"\n/cnb/lifecycle/detector -app=\"${PARAM_SOURCE_CONTEXT}\" -layers=\"$LAYERS_DIR\"\n\nanounce_phase \"ANALYZING\"\n/cnb/lifecycle/analyzer -layers=\"$LAYERS_DIR\" -cache-dir=\"$CACHE_DIR\" \"${PARAM_OUTPUT_IMAGE}\"\n\nanounce_phase \"RESTORING\"\n/cnb/lifecycle/restorer -cache-dir=\"$CACHE_DIR\"\n\nanounce_phase \"BUILDING\"\n/cnb/lifecycle/builder -app=\"${PARAM_SOURCE_CONTEXT}\" -layers=\"$LAYERS_DIR\"\n\nexporter_args=( -layers=\"$LAYERS_DIR\" -report=/tmp/report.toml -cache-dir=\"$CACHE_DIR\" -app=\"${PARAM_SOURCE_CONTEXT}\")\ngrep -q \"buildpack-default-process-type\" \"$LAYERS_DIR/config/metadata.toml\" || exporter_args+=( -process-type web ) \n\nanounce_phase \"EXPORTING\"\n/cnb/lifecycle/exporter \"${exporter_args[@]}\" \"${PARAM_OUTPUT_IMAGE}\"\n\n# Store the image digest\ngrep digest /tmp/report.toml | tr -d ' \\\"\\n' | sed s/digest=// > \"$(results.shp-image-digest.path)\"\n" + volumeMounts: + - mountPath: /platform/env + name: platform-env + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 250m + memory: 65Mi + +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildStrategy +metadata: + namespace: build-examples + name: buildpacks-v3 +spec: + parameters: + - name: platform-api-version + description: The referenced version is the minimum version that all relevant buildpack implementations support. + default: '0.4' + steps: + - name: build-and-push + image: docker.io/paketobuildpacks/builder:full + imagePullPolicy: Always + env: + - name: CNB_PLATFORM_API + value: $(params.platform-api-version) + - name: PARAM_SOURCE_CONTEXT + value: $(params.shp-source-context) + - name: PARAM_OUTPUT_IMAGE + value: $(params.shp-output-image) + command: + - /bin/bash + args: + - -c + - "set -euo pipefail\n\necho \"> Processing environment variables...\"\nENV_DIR=\"/platform/env\"\n\nenvs=($(env))\n\n# Denying the creation of non required files from system environments.\n# The creation of a file named PATH (corresponding to PATH system environment)\n# caused failure for python source during pip install (https://github.com/Azure-Samples/python-docs-hello-world)\nblock_list=(\"PATH\" \"HOSTNAME\" \"PWD\" \"_\" \"SHLVL\" \"HOME\" \"\")\n\nfor env in \"${envs[@]}\"; do\n blocked=false\n\n IFS='=' read -r key value string <<< \"$env\"\n\n for str in \"${block_list[@]}\"; do\n if [[ \"$key\" == \"$str\" ]]; then\n blocked=true\n break\n fi\n done\n\n if [ \"$blocked\" == \"false\" ]; then\n path=\"${ENV_DIR}/${key}\"\n echo -n \"$value\" > \"$path\"\n fi\ndone\n\nLAYERS_DIR=/tmp/layers\nCACHE_DIR=/tmp/cache\n\nmkdir \"$CACHE_DIR\" \"$LAYERS_DIR\"\n\nfunction anounce_phase {\n printf \"===> %s\\n\" \"$1\" \n}\n\nanounce_phase \"DETECTING\"\n/cnb/lifecycle/detector -app=\"${PARAM_SOURCE_CONTEXT}\" -layers=\"$LAYERS_DIR\"\n\nanounce_phase \"ANALYZING\"\n/cnb/lifecycle/analyzer -layers=\"$LAYERS_DIR\" -cache-dir=\"$CACHE_DIR\" \"${PARAM_OUTPUT_IMAGE}\"\n\nanounce_phase \"RESTORING\"\n/cnb/lifecycle/restorer -cache-dir=\"$CACHE_DIR\"\n\nanounce_phase \"BUILDING\"\n/cnb/lifecycle/builder -app=\"${PARAM_SOURCE_CONTEXT}\" -layers=\"$LAYERS_DIR\"\n\nexporter_args=( -layers=\"$LAYERS_DIR\" -report=/tmp/report.toml -cache-dir=\"$CACHE_DIR\" -app=\"${PARAM_SOURCE_CONTEXT}\")\ngrep -q \"buildpack-default-process-type\" \"$LAYERS_DIR/config/metadata.toml\" || exporter_args+=( -process-type web ) \n\nanounce_phase \"EXPORTING\"\n/cnb/lifecycle/exporter \"${exporter_args[@]}\" \"${PARAM_OUTPUT_IMAGE}\"\n\n# Store the image digest\ngrep digest /tmp/report.toml | tr -d ' \\\"\\n' | sed s/digest=// > \"$(results.shp-image-digest.path)\"\n" + volumeMounts: + - mountPath: /platform/env + name: platform-env + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 250m + memory: 65Mi + +--- +# This Build Strategy will intentionally fail if the image has any +# critical CVEs. It will not be pushed into the destination registry +# if any critical vulnerabilities are found. +apiVersion: shipwright.io/v1beta1 +kind: BuildStrategy +metadata: + namespace: build-examples + name: kaniko-trivy +spec: + steps: + - name: kaniko-build + image: gcr.io/kaniko-project/executor:v1.8.1 + workingDir: $(params.shp-source-root) + env: + - name: HOME + value: /tekton/home + - name: AWS_ACCESS_KEY_ID + value: NOT_SET + - name: AWS_SECRET_KEY + value: NOT_SET + command: + - /kaniko/executor + args: + - --skip-tls-verify=true + - --dockerfile=$(params.dockerfile) + - --context=$(params.shp-source-context) + - --destination=$(params.shp-output-image) + - --oci-layout-path=/kaniko/oci-image-layout + - --snapshotMode=redo + - --no-push + - --tarPath + - /kaniko/tar-image/image.tar + volumeMounts: + - name: layout + mountPath: /kaniko/oci-image-layout + - name: tar + mountPath: /kaniko/tar-image + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 250m + memory: 65Mi + - name: trivy-scan + image: docker.io/aquasec/trivy:0.25.3 + volumeMounts: + - mountPath: /image/ + name: tar + command: + - trivy + args: + - image + - --exit-code=1 + - --severity=CRITICAL + - --input + - /image/image.tar + env: + - name: HOME + value: /tekton/home + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 250m + memory: 65Mi + - name: crane-push + image: gcr.io/go-containerregistry/crane:v0.8.0 + volumeMounts: + - mountPath: /image/ + name: tar + command: + - crane + args: + - push + - /image/image.tar + - $(params.shp-output-image) + env: + - name: HOME + value: /tekton/home + - name: results + image: registry.access.redhat.com/ubi8/ubi-minimal + command: + - /bin/bash + args: + - -c + - | + set -euo pipefail + + # Store the image digest + grep digest /kaniko/oci-image-layout/index.json | sed -E 's/.*sha256([^"]*).*/sha256\1/' | tr -d '\n' > "$(results.shp-image-digest.path)" + + # Store the image size + du -b -c /kaniko/oci-image-layout/blobs/sha256/* | tail -1 | sed 's/\s*total//' | tr -d '\n' > "$(results.shp-image-size.path)" + resources: + limits: + cpu: 100m + memory: 128Mi + requests: + cpu: 100m + memory: 128Mi + volumeMounts: + - name: layout + mountPath: /kaniko/oci-image-layout + parameters: + - name: dockerfile + description: The path to the Dockerfile to be used for building the image. + type: string + default: 'Dockerfile' +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildStrategy +metadata: + namespace: build-examples + name: kaniko +spec: + steps: + - name: build-and-push + image: gcr.io/kaniko-project/executor:v1.8.1 + workingDir: $(params.shp-source-root) + env: + - name: HOME + value: /tekton/home + - name: DOCKER_CONFIG + value: /tekton/home/.docker + - name: AWS_ACCESS_KEY_ID + value: NOT_SET + - name: AWS_SECRET_KEY + value: NOT_SET + command: + - /kaniko/executor + args: + - --skip-tls-verify=true + - --dockerfile=$(params.dockerfile) + - --context=$(params.shp-source-context) + - --destination=$(params.shp-output-image) + - --oci-layout-path=/kaniko/oci-image-layout + - --snapshotMode=redo + - --push-retry=3 + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 250m + memory: 65Mi + volumeMounts: + - name: layout + mountPath: /kaniko/oci-image-layout + - name: results + image: registry.access.redhat.com/ubi8/ubi-minimal + command: + - /bin/bash + args: + - -c + - | + set -euo pipefail + + # Store the image digest + grep digest /kaniko/oci-image-layout/index.json | sed -E 's/.*sha256([^"]*).*/sha256\1/' | tr -d '\n' > "$(results.shp-image-digest.path)" + + # Store the image size + du -b -c /kaniko/oci-image-layout/blobs/sha256/* | tail -1 | sed 's/\s*total//' | tr -d '\n' > "$(results.shp-image-size.path)" + resources: + limits: + cpu: 100m + memory: 128Mi + requests: + cpu: 100m + memory: 128Mi + volumeMounts: + - name: layout + mountPath: /kaniko/oci-image-layout + parameters: + - name: dockerfile + description: The path to the Dockerfile to be used for building the image. + type: string + default: 'Dockerfile' + +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildStrategy +metadata: + namespace: build-examples + name: ko +spec: + parameters: + - name: go-flags + description: 'Value for the GOFLAGS environment variable.' + default: '' + - name: go-version + description: 'Version of Go, must match a tag from https://hub.docker.com/_/golang?tab=tags' + default: '1.17' + - name: ko-version + description: "Version of ko, must be either 'latest', or a release name from https://github.com/google/ko/releases" + default: latest + - name: package-directory + description: 'The directory inside the context directory containing the main package.' + default: '.' + - name: target-platform + description: "Target platform to be built. For example: 'linux/arm64'. Multiple platforms can be provided separated by comma, for example: 'linux/arm64,linux/amd64'. The value 'all' will build all platforms supported by the base image. The value 'current' will build the platform on which the build runs." + default: current + steps: + - name: build-and-push + image: golang:$(params.go-version) + imagePullPolicy: Always + workingDir: $(params.shp-source-root) + env: + - name: DOCKER_CONFIG + value: /tekton/home/.docker + - name: HOME + value: /tekton/home + - name: GOFLAGS + value: $(params.go-flags) + - name: PARAM_OUTPUT_IMAGE + value: $(params.shp-output-image) + - name: PARAM_SOURCE_CONTEXT + value: $(params.shp-source-context) + - name: PARAM_TARGET_PLATFORM + value: $(params.target-platform) + - name: PARAM_PACKAGE_DIRECTORY + value: $(params.package-directory) + - name: PARAM_KO_VERSION + value: $(params.ko-version) + command: + - /bin/bash + args: + - -c + - | + set -euo pipefail + + # Parse image URL to extract repository and tag, must work with + # - a URL without a tag and a port: registry/image + # - a URL without a tag but a port: registry:port/image + # - a URL with a tag but without a port: registry/image:tag + # - a URL with both a tag and a port: registry:port/image:tag + + REPO= + TAG= + + IFS=':' read -ra PARTS <<< "${PARAM_OUTPUT_IMAGE}" + for PART in "${PARTS[@]}"; do + if [ "${REPO}" == "" ]; then + REPO="${PART}" + elif [[ "${PART}" == *"/"* ]]; then + REPO="${REPO}:${PART}" + elif [ "${TAG}" == "" ]; then + TAG="${PART}" + else + REPO="${REPO}:${TAG}" + TAG="${PART}" + fi + done + + # Determine the ko version + KO_VERSION="${PARAM_KO_VERSION}" + if [ "${KO_VERSION}" == "latest" ]; then + KO_VERSION=$(curl --silent "https://api.github.com/repos/google/ko/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') + fi + + # Create one variable with v-suffix and one without as we need both for the download URL + if [[ ${KO_VERSION} = v* ]]; then + KO_VERSION_WITH_V=${KO_VERSION} + KO_VERSION_WITHOUT_V=${KO_VERSION:1} + else + KO_VERSION_WITH_V=v${KO_VERSION} + KO_VERSION_WITHOUT_V=${KO_VERSION} + fi + + # Download ko to the temp directory + curl -f -s -L "https://github.com/google/ko/releases/download/${KO_VERSION_WITH_V}/ko_${KO_VERSION_WITHOUT_V}_$(uname)_$(uname -m | sed 's/aarch64/arm64/').tar.gz" | tar xzf - -C /tmp ko + + # Determine the platform + PLATFORM="${PARAM_TARGET_PLATFORM}" + if [ "${PLATFORM}" == "current" ]; then + PLATFORM="$(uname | tr '[:upper:]' '[:lower:]')/$(uname -m | sed -e 's/x86_64/amd64/' -e 's/aarch64/arm64/')" + fi + + # Print version information + go version + echo "ko version $(/tmp/ko version)" + + # Run ko + + export GOROOT="$(go env GOROOT)" + export KO_DOCKER_REPO="${REPO}" + + pushd "${PARAM_SOURCE_CONTEXT}" > /dev/null + if [ "${TAG}" == "" ]; then + /tmp/ko publish "${PARAM_PACKAGE_DIRECTORY}" --bare --oci-layout-path=/tmp/layout --platform="${PLATFORM}" + else + /tmp/ko publish "${PARAM_PACKAGE_DIRECTORY}" --bare --oci-layout-path=/tmp/layout --platform="${PLATFORM}" --tags="${TAG}" + fi + popd > /dev/null + + # Store the image digest + grep digest /tmp/layout/index.json | sed -E 's/.*sha256([^"]*).*/sha256\1/' | tr -d '\n' > '$(results.shp-image-digest.path)' + + # Store the image size + du -b -c /tmp/layout/blobs/sha256/* | tail -1 | sed 's/\s*total//' | tr -d '\n' > '$(results.shp-image-size.path)' + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 250m + memory: 65Mi + +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildStrategy +metadata: + namespace: build-examples + name: source-to-image-redhat +spec: + steps: + - name: s2i-generate + image: registry.redhat.io/ocp-tools-43-tech-preview/source-to-image-rhel8:latest + workingDir: $(params.shp-source-root) + args: + - build + - $(params.shp-source-context) + - $(build.builder.image) + - $(params.shp-output-image) + - --as-dockerfile=/s2i/Dockerfile + volumeMounts: + - name: s2i + mountPath: /s2i + - name: buildah + image: quay.io/containers/buildah:v1.23.1 + workingDir: /s2i + command: + - /bin/bash + args: + - -c + - | + set -euo pipefail + + # Parse parameters + image= + registriesBlock="" + inRegistriesBlock=false + registriesInsecure="" + inRegistriesInsecure=false + registriesSearch="" + inRegistriesSearch=false + tlsVerify=true + while [[ $# -gt 0 ]]; do + arg="$1" + shift + + if [ "${arg}" == "--image" ]; then + inRegistriesBlock=false + inRegistriesInsecure=false + inRegistriesSearch=false + image="$1" + shift + elif [ "${arg}" == "--registries-block" ]; then + inRegistriesBlock=true + inRegistriesInsecure=false + inRegistriesSearch=false + elif [ "${arg}" == "--registries-insecure" ]; then + inRegistriesInsecure=true + inRegistriesBlock=false + inRegistriesSearch=false + elif [ "${arg}" == "--registries-search" ]; then + inRegistriesSearch=true + inRegistriesBlock=false + inRegistriesInsecure=false + elif [ "${inRegistriesBlock}" == "true" ]; then + registriesBlock="${registriesBlock}'${arg}', " + elif [ "${inRegistriesInsecure}" == "true" ]; then + registriesInsecure="${registriesInsecure}'${arg}', " + + # This assumes that the image is passed before the insecure registries which is fair in this context + if [[ ${image} == ${arg}/* ]]; then + tlsVerify=false + fi + elif [ "${inRegistriesSearch}" == "true" ]; then + registriesSearch="${registriesSearch}'${arg}', " + else + echo "Invalid usage" + exit 1 + fi + done + + echo "[INFO] Creating registries config file..." + if [ "${registriesSearch}" != "" ]; then + cat <>/tmp/registries.conf + [registries.search] + registries = [${registriesSearch::-2}] + + EOF + fi + if [ "${registriesInsecure}" != "" ]; then + cat <>/tmp/registries.conf + [registries.insecure] + registries = [${registriesInsecure::-2}] + + EOF + fi + if [ "${registriesBlock}" != "" ]; then + cat <>/tmp/registries.conf + [registries.block] + registries = [${registriesBlock::-2}] + + EOF + fi + + # Building the image + echo "[INFO] Building image ${image}" + buildah bud \ + --registries-conf=/tmp/registries.conf \ + --tag="${image}" + + # Push the image + echo "[INFO] Pushing image ${image}" + buildah push \ + --digestfile='$(results.shp-image-digest.path)' \ + --tls-verify="${tlsVerify}" \ + "docker://${image}" + # That's the separator between the shell script and its args + - -- + - --image + - $(params.shp-output-image) + - --registries-block + - $(params.registries-block[*]) + - --registries-insecure + - $(params.registries-insecure[*]) + - --registries-search + - $(params.registries-search[*]) + volumeMounts: + - name: s2i + mountPath: /s2i + parameters: + - name: registries-block + description: The registries that need to block pull access. + type: array + defaults: [] + - name: registries-insecure + description: The fully-qualified name of insecure registries. An insecure registry is one that does not have a valid SSL certificate or only supports HTTP. + type: array + defaults: [] + - name: registries-search + description: The registries for searching short name images such as `golang:latest`. + type: array + defaults: + - docker.io + - quay.io + +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildStrategy +metadata: + namespace: build-examples + name: source-to-image +spec: + steps: + - command: + - /usr/local/bin/s2i + - build + - $(params.shp-source-context) + - $(build.builder.image) + - '--as-dockerfile' + - /gen-source/Dockerfile.gen + image: quay.io/openshift-pipeline/s2i:nightly + imagePullPolicy: Always + name: s2i-build-as-dockerfile + volumeMounts: + - mountPath: /gen-source + name: gen-source + workingDir: $(params.shp-source-root) + - args: + - '--skip-tls-verify=true' + - '--dockerfile=/gen-source/Dockerfile.gen' + - '--context=/gen-source' + - '--destination=$(params.shp-output-image)' + - '--oci-layout-path=/kaniko/oci-image-layout' + - '--snapshotMode=redo' + - '--push-retry=3' + command: + - /kaniko/executor + env: + - name: DOCKER_CONFIG + value: /tekton/home/.docker + - name: HOME + value: /tekton/home + - name: AWS_ACCESS_KEY_ID + value: NOT_SET + - name: AWS_SECRET_KEY + value: NOT_SET + image: gcr.io/kaniko-project/executor:v1.8.1 + name: build-and-push + volumeMounts: + - mountPath: /gen-source + name: gen-source + - name: layout + mountPath: /kaniko/oci-image-layout + workingDir: /gen-source + - name: results + image: registry.access.redhat.com/ubi8/ubi-minimal + command: + - /bin/bash + args: + - -c + - | + set -euo pipefail + + # Store the image digest + grep digest /kaniko/oci-image-layout/index.json | sed -E 's/.*sha256([^"]*).*/sha256\1/' | tr -d '\n' > "$(results.shp-image-digest.path)" + + # Store the image size + du -b -c /kaniko/oci-image-layout/blobs/sha256/* | tail -1 | sed 's/\s*total//' | tr -d '\n' > "$(results.shp-image-size.path)" + resources: + limits: + cpu: 100m + memory: 128Mi + requests: + cpu: 100m + memory: 128Mi + volumeMounts: + - name: layout + mountPath: /kaniko/oci-image-layout diff --git a/frontend/packages/shipwright-plugin/samples-v1beta1/2-buildpack-nodejs-build.yaml b/frontend/packages/shipwright-plugin/samples-v1beta1/2-buildpack-nodejs-build.yaml new file mode 100644 index 00000000000..353e3c7d04c --- /dev/null +++ b/frontend/packages/shipwright-plugin/samples-v1beta1/2-buildpack-nodejs-build.yaml @@ -0,0 +1,34 @@ +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + namespace: build-examples + name: buildpack-nodejs-build +spec: + source: + type: Git + git: + url: https://github.com/shipwright-io/sample-nodejs + contextDir: source-build + strategy: + name: buildpacks-v3 + kind: BuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/buildpack-nodejs-build +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + namespace: build-examples + generateName: buildpack-nodejs-build- +spec: + build: + name: buildpack-nodejs-build +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + namespace: build-examples + generateName: buildpack-nodejs-build- +spec: + build: + name: buildpack-nodejs-build diff --git a/frontend/packages/shipwright-plugin/samples/201-full-openshift-deployment-example.yaml b/frontend/packages/shipwright-plugin/samples-v1beta1/201-full-openshift-deployment-example.yaml similarity index 91% rename from frontend/packages/shipwright-plugin/samples/201-full-openshift-deployment-example.yaml rename to frontend/packages/shipwright-plugin/samples-v1beta1/201-full-openshift-deployment-example.yaml index 15a2b7e1ce5..e8bbff13087 100644 --- a/frontend/packages/shipwright-plugin/samples/201-full-openshift-deployment-example.yaml +++ b/frontend/packages/shipwright-plugin/samples-v1beta1/201-full-openshift-deployment-example.yaml @@ -20,7 +20,7 @@ spec: image: >- image-registry.openshift-image-registry.svc:5000/build-examples/sw-deployment-example:latest --- -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: Build metadata: name: sw-deployment-example-build @@ -29,7 +29,9 @@ metadata: app.kubernetes.io/part-of: sw-deployment-app spec: source: - url: https://github.com/shipwright-io/sample-nodejs + type: Git + git: + url: https://github.com/shipwright-io/sample-nodejs contextDir: source-build strategy: name: buildpacks-v3 @@ -37,7 +39,7 @@ spec: output: image: image-registry.openshift-image-registry.svc:5000/build-examples/sw-deployment-example-build --- -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: BuildRun metadata: namespace: build-examples @@ -45,7 +47,7 @@ metadata: labels: app.kubernetes.io/part-of: sw-deployment-app spec: - buildRef: + build: name: sw-deployment-example-build --- apiVersion: v1 diff --git a/frontend/packages/shipwright-plugin/samples/202-full-openshift-deploymentconfig-example.yaml b/frontend/packages/shipwright-plugin/samples-v1beta1/202-full-openshift-deploymentconfig-example.yaml similarity index 92% rename from frontend/packages/shipwright-plugin/samples/202-full-openshift-deploymentconfig-example.yaml rename to frontend/packages/shipwright-plugin/samples-v1beta1/202-full-openshift-deploymentconfig-example.yaml index ed0f7665336..146d015bbed 100644 --- a/frontend/packages/shipwright-plugin/samples/202-full-openshift-deploymentconfig-example.yaml +++ b/frontend/packages/shipwright-plugin/samples-v1beta1/202-full-openshift-deploymentconfig-example.yaml @@ -30,7 +30,7 @@ spec: namespace: build-examples name: 'sw-deploymentconfig-example:latest' --- -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: Build metadata: namespace: build-examples @@ -39,7 +39,9 @@ metadata: app.kubernetes.io/part-of: sw-deploymentconfig-app spec: source: - url: https://github.com/shipwright-io/sample-nodejs + type: Git + git: + url: https://github.com/shipwright-io/sample-nodejs contextDir: source-build strategy: name: buildpacks-v3 @@ -47,7 +49,7 @@ spec: output: image: image-registry.openshift-image-registry.svc:5000/build-examples/sw-deploymentconfig-example --- -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: BuildRun metadata: namespace: build-examples @@ -55,7 +57,7 @@ metadata: labels: app.kubernetes.io/part-of: sw-deploymentconfig-app spec: - buildRef: + build: name: sw-deploymentconfig-example-build --- apiVersion: v1 diff --git a/frontend/packages/shipwright-plugin/samples/203-full-openshift-knative-service-example.yaml b/frontend/packages/shipwright-plugin/samples-v1beta1/203-full-openshift-knative-service-example.yaml similarity index 83% rename from frontend/packages/shipwright-plugin/samples/203-full-openshift-knative-service-example.yaml rename to frontend/packages/shipwright-plugin/samples-v1beta1/203-full-openshift-knative-service-example.yaml index eb008375264..3928d89c625 100644 --- a/frontend/packages/shipwright-plugin/samples/203-full-openshift-knative-service-example.yaml +++ b/frontend/packages/shipwright-plugin/samples-v1beta1/203-full-openshift-knative-service-example.yaml @@ -16,7 +16,7 @@ spec: image: >- image-registry.openshift-image-registry.svc:5000/build-examples/sw-knative-service-example:latest --- -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: Build metadata: namespace: build-examples @@ -25,7 +25,9 @@ metadata: app.kubernetes.io/part-of: sw-knative-service-example spec: source: - url: https://github.com/shipwright-io/sample-nodejs + type: Git + git: + url: https://github.com/shipwright-io/sample-nodejs contextDir: source-build strategy: name: buildpacks-v3 @@ -33,7 +35,7 @@ spec: output: image: image-registry.openshift-image-registry.svc:5000/build-examples/sw-knative-service-example --- -apiVersion: shipwright.io/v1alpha1 +apiVersion: shipwright.io/v1beta1 kind: BuildRun metadata: namespace: build-examples @@ -41,5 +43,5 @@ metadata: labels: app.kubernetes.io/part-of: sw-knative-service-example spec: - buildRef: - name: sw-knative-service-example-build \ No newline at end of file + build: + name: sw-knative-service-example-build diff --git a/frontend/packages/shipwright-plugin/samples-v1beta1/3-buildpack-nodejs-build-heroku.yaml b/frontend/packages/shipwright-plugin/samples-v1beta1/3-buildpack-nodejs-build-heroku.yaml new file mode 100644 index 00000000000..5bc1009640f --- /dev/null +++ b/frontend/packages/shipwright-plugin/samples-v1beta1/3-buildpack-nodejs-build-heroku.yaml @@ -0,0 +1,34 @@ +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + namespace: build-examples + name: buildpack-nodejs-build-heroku +spec: + source: + type: Git + git: + url: https://github.com/shipwright-io/sample-nodejs + contextDir: source-build + strategy: + name: buildpacks-v3-heroku + kind: BuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/buildpack-nodejs-build-heroku +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + namespace: build-examples + generateName: buildpack-nodejs-build-heroku- +spec: + build: + name: buildpack-nodejs-build-heroku +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + namespace: build-examples + generateName: buildpack-nodejs-build-heroku- +spec: + build: + name: buildpack-nodejs-build-heroku diff --git a/frontend/packages/shipwright-plugin/samples-v1beta1/4-kaniko-golang-build.yaml b/frontend/packages/shipwright-plugin/samples-v1beta1/4-kaniko-golang-build.yaml new file mode 100644 index 00000000000..b199077976a --- /dev/null +++ b/frontend/packages/shipwright-plugin/samples-v1beta1/4-kaniko-golang-build.yaml @@ -0,0 +1,35 @@ +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + namespace: build-examples + name: kaniko-golang-build +spec: + source: + type: Git + git: + url: https://github.com/shipwright-io/sample-go + contextDir: docker-build + strategy: + name: kaniko + kind: BuildStrategy + dockerfile: Dockerfile + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/kaniko-golang-build +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + namespace: build-examples + generateName: kaniko-golang-build- +spec: + build: + name: kaniko-golang-build +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + namespace: build-examples + generateName: kaniko-golang-build- +spec: + build: + name: kaniko-golang-build diff --git a/frontend/packages/shipwright-plugin/samples-v1beta1/5-ko-build.yaml b/frontend/packages/shipwright-plugin/samples-v1beta1/5-ko-build.yaml new file mode 100644 index 00000000000..d790834db8a --- /dev/null +++ b/frontend/packages/shipwright-plugin/samples-v1beta1/5-ko-build.yaml @@ -0,0 +1,40 @@ +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + namespace: build-examples + name: ko-build +spec: + source: + type: Git + git: + url: https://github.com/shipwright-io/build + strategy: + name: ko + kind: BuildStrategy + paramValues: + - name: go-flags + value: '-v -mod=vendor -ldflags=-w' + - name: go-version + value: '1.17' + - name: package-directory + value: ./cmd/shipwright-build-controller + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/ko-build +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + namespace: build-examples + generateName: ko-build- +spec: + build: + name: ko-build +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + namespace: build-examples + generateName: ko-build- +spec: + build: + name: ko-build diff --git a/frontend/packages/shipwright-plugin/samples-v1beta1/6-standalone-buildrun.yaml b/frontend/packages/shipwright-plugin/samples-v1beta1/6-standalone-buildrun.yaml new file mode 100644 index 00000000000..baf28d72d2a --- /dev/null +++ b/frontend/packages/shipwright-plugin/samples-v1beta1/6-standalone-buildrun.yaml @@ -0,0 +1,17 @@ +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + generateName: buildpack-nodejs-buildrun- +spec: + build: + spec: + source: + type: Git + git: + url: https://github.com/shipwright-io/sample-nodejs + contextDir: source-build + strategy: + kind: BuildStrategy + name: buildpacks-v3 + output: + image: image-registry.openshift-image-registry.svc:5000/christoph/buildpack-nodejs-build:latest diff --git a/frontend/packages/shipwright-plugin/samples-v1beta1/7-donothing-fastrun-build.yaml b/frontend/packages/shipwright-plugin/samples-v1beta1/7-donothing-fastrun-build.yaml new file mode 100644 index 00000000000..3d6b587d6f7 --- /dev/null +++ b/frontend/packages/shipwright-plugin/samples-v1beta1/7-donothing-fastrun-build.yaml @@ -0,0 +1,47 @@ +apiVersion: shipwright.io/v1beta1 +kind: BuildStrategy +metadata: + namespace: build-examples + name: donothing-build-strategy +spec: + buildSteps: + - name: donothing + command: + - echo + - donothing-build-strategy + image: ubuntu +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + namespace: build-examples + name: donothing-build +spec: + source: + type: Git + git: + url: https://github.com/shipwright-io/sample-nodejs + contextDir: source-build + strategy: + name: donothing-build-strategy + kind: BuildStrategy + output: + image: image-registry.openshift-image-registry.svc:5000/build-examples/donothing-build +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + namespace: build-examples + generateName: donothing-build- +spec: + build: + name: donothing-build +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + namespace: build-examples + generateName: donothing-build- +spec: + build: + name: donothing-build diff --git a/frontend/packages/shipwright-plugin/samples-v1beta1/8-invalid-builds.yaml b/frontend/packages/shipwright-plugin/samples-v1beta1/8-invalid-builds.yaml new file mode 100644 index 00000000000..dfb6709e6bc --- /dev/null +++ b/frontend/packages/shipwright-plugin/samples-v1beta1/8-invalid-builds.yaml @@ -0,0 +1,31 @@ +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: clusterbuildstrategy-not-found +spec: + source: + type: Git + git: + url: https://github.com/shipwright-io/sample-go.git + contextDir: source-build + strategy: + kind: ClusterBuildStrategy + name: not-found + output: + image: foo/bar:latest +--- +apiVersion: shipwright.io/v1beta1 +kind: Build +metadata: + name: buildstrategy-not-found +spec: + source: + type: Git + git: + url: https://github.com/shipwright-io/sample-go.git + contextDir: source-build + strategy: + kind: BuildStrategy + name: not-found + output: + image: foo/bar:latest diff --git a/frontend/packages/shipwright-plugin/samples-v1beta1/9-invalid-buildruns.yaml b/frontend/packages/shipwright-plugin/samples-v1beta1/9-invalid-buildruns.yaml new file mode 100644 index 00000000000..da3d95b2103 --- /dev/null +++ b/frontend/packages/shipwright-plugin/samples-v1beta1/9-invalid-buildruns.yaml @@ -0,0 +1,14 @@ +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: build-not-found +spec: + build: + name: build-not-found +--- +apiVersion: shipwright.io/v1beta1 +kind: BuildRun +metadata: + name: no-buildref-nor-buildspec +spec: + build: {} diff --git a/frontend/packages/shipwright-plugin/src/__tests__/api.spec.ts b/frontend/packages/shipwright-plugin/src/__tests__/api-v1alpha1.spec.ts similarity index 90% rename from frontend/packages/shipwright-plugin/src/__tests__/api.spec.ts rename to frontend/packages/shipwright-plugin/src/__tests__/api-v1alpha1.spec.ts index bbb806b1cee..08ee5b4a363 100644 --- a/frontend/packages/shipwright-plugin/src/__tests__/api.spec.ts +++ b/frontend/packages/shipwright-plugin/src/__tests__/api-v1alpha1.spec.ts @@ -1,6 +1,6 @@ import { k8sCreateResource } from '@console/dynamic-plugin-sdk/src/utils/k8s'; import { startBuild, canRerunBuildRun, rerunBuildRun } from '../api'; -import { BuildRunModel } from '../models'; +import { BuildRunModelV1Alpha1 } from '../models'; import { Build, BuildRun } from '../types'; import { incompleteBuild, @@ -11,7 +11,7 @@ import { buildRunContainsIncompleteBuildSpecWithGenerateName, buildRunWithLabels, buildWithLabels, -} from './mock-data'; +} from './mock-data-v1alpha1'; jest.mock('@console/dynamic-plugin-sdk/src/utils/k8s', () => ({ k8sCreateResource: jest.fn(), @@ -44,7 +44,7 @@ describe('startBuild', () => { await startBuild(build); expect(k8sCreateResourceMock).toHaveBeenCalledTimes(1); expect(k8sCreateResourceMock).toHaveBeenCalledWith({ - model: BuildRunModel, + model: BuildRunModelV1Alpha1, data: expectedBuildRun, }); }); @@ -72,7 +72,7 @@ describe('startBuild', () => { await startBuild(build); expect(k8sCreateResourceMock).toHaveBeenCalledTimes(1); expect(k8sCreateResourceMock).toHaveBeenCalledWith({ - model: BuildRunModel, + model: BuildRunModelV1Alpha1, data: expectedBuildRun, }); }); @@ -117,7 +117,7 @@ describe('rerunBuildRun', () => { await rerunBuildRun(buildRun); expect(k8sCreateResourceMock).toHaveBeenCalledTimes(1); expect(k8sCreateResourceMock).toHaveBeenCalledWith({ - model: BuildRunModel, + model: BuildRunModelV1Alpha1, data: expectedBuildRun, }); }); @@ -144,7 +144,7 @@ describe('rerunBuildRun', () => { await rerunBuildRun(buildRun); expect(k8sCreateResourceMock).toHaveBeenCalledTimes(1); expect(k8sCreateResourceMock).toHaveBeenCalledWith({ - model: BuildRunModel, + model: BuildRunModelV1Alpha1, data: expectedBuildRun, }); }); @@ -159,14 +159,24 @@ describe('rerunBuildRun', () => { generateName: 'buildrun3-33333-', }, spec: { - buildSpec: {}, + buildSpec: { + output: { + image: '', + }, + source: { + url: '', + }, + strategy: { + name: '', + }, + }, }, }; await rerunBuildRun(buildRun); expect(k8sCreateResourceMock).toHaveBeenCalledTimes(1); expect(k8sCreateResourceMock).toHaveBeenCalledWith({ - model: BuildRunModel, + model: BuildRunModelV1Alpha1, data: expectedBuildRun, }); }); @@ -181,20 +191,30 @@ describe('rerunBuildRun', () => { generateName: 'buildrun4-', }, spec: { - buildSpec: {}, + buildSpec: { + output: { + image: '', + }, + source: { + url: '', + }, + strategy: { + name: '', + }, + }, }, }; await rerunBuildRun(buildRun); expect(k8sCreateResourceMock).toHaveBeenCalledTimes(1); expect(k8sCreateResourceMock).toHaveBeenCalledWith({ - model: BuildRunModel, + model: BuildRunModelV1Alpha1, data: expectedBuildRun, }); }); it('should fail when try to rerun an incomplete BuildRun without buildRef and without buildSpec', async () => { - const buildRun: BuildRun = incompleteBuildRun; + const buildRun = incompleteBuildRun; await expect(rerunBuildRun(buildRun)).rejects.toEqual( new Error('Could not rerun BuildRun without buildRef.name or inline buildSpec.'), @@ -225,7 +245,7 @@ describe('rerunBuildRun', () => { await rerunBuildRun(buildRun); expect(k8sCreateResourceMock).toHaveBeenCalledTimes(1); expect(k8sCreateResourceMock).toHaveBeenCalledWith({ - model: BuildRunModel, + model: BuildRunModelV1Alpha1, data: expectedBuildRun, }); }); diff --git a/frontend/packages/shipwright-plugin/src/__tests__/api-v1beta1.spec.ts b/frontend/packages/shipwright-plugin/src/__tests__/api-v1beta1.spec.ts new file mode 100644 index 00000000000..36c6060b13a --- /dev/null +++ b/frontend/packages/shipwright-plugin/src/__tests__/api-v1beta1.spec.ts @@ -0,0 +1,260 @@ +import { k8sCreateResource } from '@console/dynamic-plugin-sdk/src/utils/k8s'; +import { startBuild, canRerunBuildRun, rerunBuildRun } from '../api'; +import { BuildRunModel } from '../models'; +import { Build, BuildRun } from '../types'; +import { + incompleteBuild, + incompleteBuildRun, + buildRunReferenceIncompleteBuildWithoutGenerateName, + buildRunReferenceIncompleteBuildWithGenerateName, + buildRunContainsIncompleteBuildSpecWithoutGenerateName, + buildRunContainsIncompleteBuildSpecWithGenerateName, + buildRunWithLabels, + buildWithLabels, +} from './mock-data-v1beta1'; + +jest.mock('@console/dynamic-plugin-sdk/src/utils/k8s', () => ({ + k8sCreateResource: jest.fn(), +})); + +const k8sCreateResourceMock = k8sCreateResource as jest.Mock; + +beforeEach(() => jest.resetAllMocks()); + +describe('startBuild', () => { + it('should create a new BuildRun referencing a Build', async () => { + const build: Build = incompleteBuild; + const expectedBuildRun: BuildRun = { + apiVersion: 'shipwright.io/v1beta1', + kind: 'BuildRun', + metadata: { + namespace: 'a-namespace', + generateName: 'incomplete-build-', + labels: { + 'build.shipwright.io/name': 'incomplete-build', + }, + }, + spec: { + build: { + name: 'incomplete-build', + }, + }, + }; + + await startBuild(build); + expect(k8sCreateResourceMock).toHaveBeenCalledTimes(1); + expect(k8sCreateResourceMock).toHaveBeenCalledWith({ + model: BuildRunModel, + data: expectedBuildRun, + }); + }); + + it('should create a new BuildRun with Labels present in Build', async () => { + const build: Build = buildWithLabels; + const expectedBuildRun: BuildRun = { + apiVersion: 'shipwright.io/v1beta1', + kind: 'BuildRun', + metadata: { + namespace: 'a-namespace', + generateName: 'build-with-labels-', + labels: { + 'build.shipwright.io/name': 'build-with-labels', + 'app.kubernetes.io/part-of': 'buildpack-nodejs-build', + }, + }, + spec: { + build: { + name: 'build-with-labels', + }, + }, + }; + + await startBuild(build); + expect(k8sCreateResourceMock).toHaveBeenCalledTimes(1); + expect(k8sCreateResourceMock).toHaveBeenCalledWith({ + model: BuildRunModel, + data: expectedBuildRun, + }); + }); +}); + +describe('canRerunBuildRun', () => { + it('should allow user to rerun BuildRun that referencing a Build', () => { + expect(canRerunBuildRun(buildRunReferenceIncompleteBuildWithoutGenerateName)).toBe(true); + expect(canRerunBuildRun(buildRunReferenceIncompleteBuildWithGenerateName)).toBe(true); + }); + + it('should allow user to rerun BuildRun that contains a inline BuildSpec', () => { + expect(canRerunBuildRun(buildRunContainsIncompleteBuildSpecWithoutGenerateName)).toBe(true); + expect(canRerunBuildRun(buildRunContainsIncompleteBuildSpecWithGenerateName)).toBe(true); + }); + + it('should not allow user to rerun an incomplete BuildRun', () => { + expect(canRerunBuildRun(incompleteBuildRun)).toBe(false); + }); +}); + +describe('rerunBuildRun', () => { + it('should create another BuildRun when rerun a BuildRun that referencing a Build without generateName', async () => { + const buildRun: BuildRun = buildRunReferenceIncompleteBuildWithoutGenerateName; + const expectedBuildRun: BuildRun = { + apiVersion: 'shipwright.io/v1beta1', + kind: 'BuildRun', + metadata: { + namespace: 'a-namespace', + generateName: 'incomplete-build-', + labels: { + 'build.shipwright.io/name': 'incomplete-build', + }, + }, + spec: { + build: { + name: 'incomplete-build', + }, + }, + }; + + await rerunBuildRun(buildRun); + expect(k8sCreateResourceMock).toHaveBeenCalledTimes(1); + expect(k8sCreateResourceMock).toHaveBeenCalledWith({ + model: BuildRunModel, + data: expectedBuildRun, + }); + }); + + it('should create another BuildRun when rerun a BuildRun that referencing a Build with generateName', async () => { + const buildRun: BuildRun = buildRunReferenceIncompleteBuildWithGenerateName; + const expectedBuildRun: BuildRun = { + apiVersion: 'shipwright.io/v1beta1', + kind: 'BuildRun', + metadata: { + namespace: 'a-namespace', + generateName: 'buildrun2-', + labels: { + 'build.shipwright.io/name': 'incomplete-build', + }, + }, + spec: { + build: { + name: 'incomplete-build', + }, + }, + }; + + await rerunBuildRun(buildRun); + expect(k8sCreateResourceMock).toHaveBeenCalledTimes(1); + expect(k8sCreateResourceMock).toHaveBeenCalledWith({ + model: BuildRunModel, + data: expectedBuildRun, + }); + }); + + it('should create another BuildRun when rerun a BuildRun that contains an inline BuildSpec without generateName', async () => { + const buildRun: BuildRun = buildRunContainsIncompleteBuildSpecWithoutGenerateName; + const expectedBuildRun: BuildRun = { + apiVersion: 'shipwright.io/v1beta1', + kind: 'BuildRun', + metadata: { + namespace: 'a-namespace', + generateName: 'buildrun3-33333-', + }, + spec: { + build: { + spec: { + output: { + image: '', + }, + source: { + git: { + url: '', + }, + }, + strategy: { + name: '', + }, + }, + }, + }, + }; + + await rerunBuildRun(buildRun); + expect(k8sCreateResourceMock).toHaveBeenCalledTimes(1); + expect(k8sCreateResourceMock).toHaveBeenCalledWith({ + model: BuildRunModel, + data: expectedBuildRun, + }); + }); + + it('should create another BuildRun when rerun a BuildRun that contains an inline BuildSpec with generateName', async () => { + const buildRun: BuildRun = buildRunContainsIncompleteBuildSpecWithGenerateName; + const expectedBuildRun: BuildRun = { + apiVersion: 'shipwright.io/v1beta1', + kind: 'BuildRun', + metadata: { + namespace: 'a-namespace', + generateName: 'buildrun4-', + }, + spec: { + build: { + spec: { + output: { + image: '', + }, + source: { + git: { + url: '', + }, + }, + strategy: { + name: '', + }, + }, + }, + }, + }; + + await rerunBuildRun(buildRun); + expect(k8sCreateResourceMock).toHaveBeenCalledTimes(1); + expect(k8sCreateResourceMock).toHaveBeenCalledWith({ + model: BuildRunModel, + data: expectedBuildRun, + }); + }); + + it('should fail when try to rerun an incomplete BuildRun without build and without buildSpec', async () => { + const buildRun = incompleteBuildRun; + + await expect(rerunBuildRun(buildRun)).rejects.toEqual( + new Error('Could not rerun BuildRun without build.name or inline buildSpec.'), + ); + expect(k8sCreateResourceMock).toHaveBeenCalledTimes(0); + }); + + it('should rerun a BuildRun with the labels from the existing BuildRun', async () => { + const buildRun: BuildRun = buildRunWithLabels; + const expectedBuildRun: BuildRun = { + apiVersion: 'shipwright.io/v1beta1', + kind: 'BuildRun', + metadata: { + namespace: 'a-namespace', + generateName: 'buildrun-with-labels-', + labels: { + 'build.shipwright.io/name': 'build-with-labels', + 'app.kubernetes.io/part-of': 'buildpack-nodejs-build', + }, + }, + spec: { + build: { + name: 'build-with-labels', + }, + }, + }; + + await rerunBuildRun(buildRun); + expect(k8sCreateResourceMock).toHaveBeenCalledTimes(1); + expect(k8sCreateResourceMock).toHaveBeenCalledWith({ + model: BuildRunModel, + data: expectedBuildRun, + }); + }); +}); diff --git a/frontend/packages/shipwright-plugin/src/__tests__/mock-data.ts b/frontend/packages/shipwright-plugin/src/__tests__/mock-data-v1alpha1.ts similarity index 86% rename from frontend/packages/shipwright-plugin/src/__tests__/mock-data.ts rename to frontend/packages/shipwright-plugin/src/__tests__/mock-data-v1alpha1.ts index 5f3bbf1f2b2..a2c37fb33fc 100644 --- a/frontend/packages/shipwright-plugin/src/__tests__/mock-data.ts +++ b/frontend/packages/shipwright-plugin/src/__tests__/mock-data-v1alpha1.ts @@ -1,13 +1,13 @@ import { Build, BuildRun } from '../types'; -export const incompleteBuild: Build = { +export const incompleteBuild = { apiVersion: 'shipwright.io/v1alpha1', kind: 'Build', metadata: { namespace: 'a-namespace', name: 'incomplete-build', }, -}; +} as Build; export const buildWithLabels: Build = { apiVersion: 'shipwright.io/v1alpha1', @@ -35,14 +35,14 @@ export const buildWithLabels: Build = { }, }; -export const incompleteBuildRun: BuildRun = { +export const incompleteBuildRun = { apiVersion: 'shipwright.io/v1alpha1', kind: 'BuildRun', metadata: { namespace: 'a-namespace', name: 'incomplete-buildrun', }, -}; +} as BuildRun; export const buildRunWithLabels: BuildRun = { apiVersion: 'shipwright.io/v1alpha1', @@ -100,7 +100,17 @@ export const buildRunContainsIncompleteBuildSpecWithoutGenerateName: BuildRun = name: 'buildrun3-33333', }, spec: { - buildSpec: {}, + buildSpec: { + output: { + image: '', + }, + source: { + url: '', + }, + strategy: { + name: '', + }, + }, }, }; @@ -113,7 +123,17 @@ export const buildRunContainsIncompleteBuildSpecWithGenerateName: BuildRun = { name: 'buildrun4-44444', }, spec: { - buildSpec: {}, + buildSpec: { + output: { + image: '', + }, + source: { + url: '', + }, + strategy: { + name: '', + }, + }, }, }; @@ -124,6 +144,7 @@ export const pendingBuildRun: BuildRun = { namespace: 'a-namespace', name: 'pending-buildrun', }, + spec: {}, status: { conditions: [ { @@ -131,6 +152,7 @@ export const pendingBuildRun: BuildRun = { status: 'Unknown', reason: 'Pending', message: '', + lastTransitionTime: '', }, ], }, @@ -143,6 +165,7 @@ export const runningBuildRun: BuildRun = { namespace: 'a-namespace', name: 'running-buildrun', }, + spec: {}, status: { conditions: [ { @@ -150,6 +173,7 @@ export const runningBuildRun: BuildRun = { status: 'Unknown', reason: 'Running', message: '', + lastTransitionTime: '', }, ], }, @@ -162,6 +186,7 @@ export const succeededBuildRun: BuildRun = { namespace: 'a-namespace', name: 'Succeeded-buildrun', }, + spec: {}, status: { conditions: [ { @@ -169,6 +194,7 @@ export const succeededBuildRun: BuildRun = { status: 'True', reason: '', message: '', + lastTransitionTime: '', }, ], }, @@ -181,6 +207,7 @@ export const failedBuildRun: BuildRun = { namespace: 'a-namespace', name: 'failed-buildrun', }, + spec: {}, status: { conditions: [ { @@ -188,6 +215,7 @@ export const failedBuildRun: BuildRun = { status: 'False', reason: '', message: '', + lastTransitionTime: '', }, ], }, diff --git a/frontend/packages/shipwright-plugin/src/__tests__/mock-data-v1beta1.ts b/frontend/packages/shipwright-plugin/src/__tests__/mock-data-v1beta1.ts new file mode 100644 index 00000000000..679f67936d2 --- /dev/null +++ b/frontend/packages/shipwright-plugin/src/__tests__/mock-data-v1beta1.ts @@ -0,0 +1,248 @@ +import { Build, BuildRun } from '../types'; + +export const incompleteBuild = { + apiVersion: 'shipwright.io/v1beta1', + kind: 'Build', + metadata: { + namespace: 'a-namespace', + name: 'incomplete-build', + }, +} as Build; + +export const buildWithLabels: Build = { + apiVersion: 'shipwright.io/v1beta1', + kind: 'Build', + metadata: { + namespace: 'a-namespace', + name: 'build-with-labels', + labels: { + 'app.kubernetes.io/part-of': 'buildpack-nodejs-build', + }, + }, + spec: { + source: { + git: { + url: 'https://github.com/shipwright-io/sample-nodejs', + }, + contextDir: 'source-build', + }, + strategy: { + name: 'buildpacks-v3', + kind: 'BuildStrategy', + }, + output: { + image: + 'image-registry.openshift-image-registry.svc:5000/build-examples/buildpack-nodejs-build', + }, + }, +}; + +export const incompleteBuildRun = { + apiVersion: 'shipwright.io/v1beta1', + kind: 'BuildRun', + metadata: { + namespace: 'a-namespace', + name: 'incomplete-buildrun', + }, +} as BuildRun; + +export const buildRunWithLabels: BuildRun = { + apiVersion: 'shipwright.io/v1beta1', + kind: 'BuildRun', + metadata: { + namespace: 'a-namespace', + generateName: 'buildrun-with-labels-', + name: 'buildrun-with-labels-1234', + labels: { + 'build.shipwright.io/name': 'build-with-labels', + 'app.kubernetes.io/part-of': 'buildpack-nodejs-build', + }, + }, + spec: { + build: { + name: 'build-with-labels', + }, + }, +}; + +export const buildRunReferenceIncompleteBuildWithoutGenerateName: BuildRun = { + apiVersion: 'shipwright.io/v1beta1', + kind: 'BuildRun', + metadata: { + namespace: 'a-namespace', + name: 'buildrun1-11111', + }, + spec: { + build: { + name: 'incomplete-build', + }, + }, +}; + +export const buildRunReferenceIncompleteBuildWithGenerateName: BuildRun = { + apiVersion: 'shipwright.io/v1beta1', + kind: 'BuildRun', + metadata: { + namespace: 'a-namespace', + generateName: 'buildrun2-', + name: 'buildrun2-22222', + }, + spec: { + build: { + name: 'incomplete-build', + }, + }, +}; + +export const buildRunContainsIncompleteBuildSpecWithoutGenerateName: BuildRun = { + apiVersion: 'shipwright.io/v1beta1', + kind: 'BuildRun', + metadata: { + namespace: 'a-namespace', + name: 'buildrun3-33333', + }, + spec: { + build: { + spec: { + output: { + image: '', + }, + source: { + git: { + url: '', + }, + }, + strategy: { + name: '', + }, + }, + }, + }, +}; + +export const buildRunContainsIncompleteBuildSpecWithGenerateName: BuildRun = { + apiVersion: 'shipwright.io/v1beta1', + kind: 'BuildRun', + metadata: { + namespace: 'a-namespace', + generateName: 'buildrun4-', + name: 'buildrun4-44444', + }, + spec: { + build: { + spec: { + output: { + image: '', + }, + source: { + git: { + url: '', + }, + }, + strategy: { + name: '', + }, + }, + }, + }, +}; + +export const pendingBuildRun: BuildRun = { + apiVersion: 'shipwright.io/v1beta1', + kind: 'BuildRun', + metadata: { + namespace: 'a-namespace', + name: 'pending-buildrun', + }, + spec: { + build: { + name: 'pending-build', + }, + }, + status: { + conditions: [ + { + type: 'Succeeded', + status: 'Unknown', + reason: 'Pending', + message: '', + lastTransitionTime: '', + }, + ], + }, +}; + +export const runningBuildRun: BuildRun = { + apiVersion: 'shipwright.io/v1beta1', + kind: 'BuildRun', + metadata: { + namespace: 'a-namespace', + name: 'running-buildrun', + }, + spec: { + build: { + name: 'running-build', + }, + }, + status: { + conditions: [ + { + type: 'Succeeded', + status: 'Unknown', + reason: 'Running', + message: '', + lastTransitionTime: '', + }, + ], + }, +}; + +export const succeededBuildRun: BuildRun = { + apiVersion: 'shipwright.io/v1beta1', + kind: 'BuildRun', + metadata: { + namespace: 'a-namespace', + name: 'Succeeded-buildrun', + }, + spec: { + build: { + name: 'Succeeded-build', + }, + }, + status: { + conditions: [ + { + type: 'Succeeded', + status: 'True', + reason: '', + message: '', + lastTransitionTime: '', + }, + ], + }, +}; + +export const failedBuildRun: BuildRun = { + apiVersion: 'shipwright.io/v1beta1', + kind: 'BuildRun', + metadata: { + namespace: 'a-namespace', + name: 'failed-buildrun', + }, + spec: { + build: { + name: 'failed-build', + }, + }, + status: { + conditions: [ + { + type: 'Succeeded', + status: 'False', + reason: '', + message: '', + lastTransitionTime: '', + }, + ], + }, +}; diff --git a/frontend/packages/shipwright-plugin/src/api.ts b/frontend/packages/shipwright-plugin/src/api.ts index 83d223cc09e..c90134598fd 100644 --- a/frontend/packages/shipwright-plugin/src/api.ts +++ b/frontend/packages/shipwright-plugin/src/api.ts @@ -1,30 +1,53 @@ import { k8sCreateResource } from '@console/dynamic-plugin-sdk/src/utils/k8s'; import { BUILDRUN_TO_BUILD_REFERENCE_LABEL, BUILDRUN_TO_RESOURCE_MAP_LABEL } from './const'; -import { BuildRunModel } from './models'; +import { BuildRunModel, BuildRunModelV1Alpha1 } from './models'; import { Build, BuildRun } from './types'; +import { isV1Alpha1Resource } from './utils'; /** Create a new BuildRun for a given Build to start it. */ export const startBuild = async (build: Build): Promise => { const resourceMapLabel = build.metadata?.labels?.[BUILDRUN_TO_RESOURCE_MAP_LABEL] || null; - const newBuildRunData: BuildRun = { - apiVersion: 'shipwright.io/v1alpha1', - kind: 'BuildRun', - metadata: { - namespace: build.metadata.namespace, - generateName: `${build.metadata.name}-`, - labels: { - [BUILDRUN_TO_BUILD_REFERENCE_LABEL]: build.metadata.name, - ...(resourceMapLabel ? { [BUILDRUN_TO_RESOURCE_MAP_LABEL]: resourceMapLabel } : {}), + let newBuildRunData: BuildRun; + + if (isV1Alpha1Resource(build)) { + newBuildRunData = { + apiVersion: 'shipwright.io/v1alpha1', + kind: 'BuildRun', + metadata: { + namespace: build.metadata.namespace, + generateName: `${build.metadata.name}-`, + labels: { + [BUILDRUN_TO_BUILD_REFERENCE_LABEL]: build.metadata.name, + ...(resourceMapLabel ? { [BUILDRUN_TO_RESOURCE_MAP_LABEL]: resourceMapLabel } : {}), + }, }, - }, - spec: { - buildRef: { - name: build.metadata.name, + spec: { + buildRef: { + name: build.metadata.name, + }, }, - }, - }; + }; + } else { + newBuildRunData = { + apiVersion: 'shipwright.io/v1beta1', + kind: 'BuildRun', + metadata: { + namespace: build.metadata.namespace, + generateName: `${build.metadata.name}-`, + labels: { + [BUILDRUN_TO_BUILD_REFERENCE_LABEL]: build.metadata.name, + ...(resourceMapLabel ? { [BUILDRUN_TO_RESOURCE_MAP_LABEL]: resourceMapLabel } : {}), + }, + }, + spec: { + build: { + name: build.metadata.name, + }, + }, + }; + } return k8sCreateResource({ - model: BuildRunModel, + model: isV1Alpha1Resource(newBuildRunData) ? BuildRunModelV1Alpha1 : BuildRunModel, data: newBuildRunData, }); }; @@ -35,8 +58,13 @@ export const startBuild = async (build: Build): Promise => { * Checks that BuildRun has a buildRef or buildSpec. Doesn't check any permissions! */ export const canRerunBuildRun = (buildRun: BuildRun): boolean => { - const hasBuildRef = !!buildRun.spec?.buildRef?.name; - const hasBuildSpec = !!buildRun.spec?.buildSpec; + const hasBuildRef = isV1Alpha1Resource(buildRun) + ? !!buildRun.spec?.buildRef?.name + : !!buildRun.spec?.build?.name; + const hasBuildSpec = isV1Alpha1Resource(buildRun) + ? !!buildRun.spec?.buildSpec + : !!buildRun.spec?.build?.spec; + return hasBuildRef || hasBuildSpec; }; @@ -46,57 +74,108 @@ export const canRerunBuildRun = (buildRun: BuildRun): boolean => { * Will fail for BuildRuns without buildRef or buildSpec. See `canRerunBuildRun` */ export const rerunBuildRun = async (buildRun: BuildRun): Promise => { - const buildRefName = buildRun.spec?.buildRef?.name; - const buildSpec = buildRun.spec?.buildSpec; + const buildRefName = isV1Alpha1Resource(buildRun) + ? buildRun.spec?.buildRef?.name + : buildRun.spec?.build?.name; + const buildSpec = isV1Alpha1Resource(buildRun) + ? buildRun.spec?.buildSpec + : buildRun.spec?.build?.spec; const resourceMapLabel = buildRun.metadata?.labels?.[BUILDRUN_TO_RESOURCE_MAP_LABEL] || null; if (buildRefName) { const generateName = buildRun.metadata.generateName || `${buildRefName}-`; - const newBuildRunData: BuildRun = { - apiVersion: 'shipwright.io/v1alpha1', - kind: 'BuildRun', - metadata: { - namespace: buildRun.metadata.namespace, - generateName, - labels: { - [BUILDRUN_TO_BUILD_REFERENCE_LABEL]: buildRefName, - ...(resourceMapLabel ? { [BUILDRUN_TO_RESOURCE_MAP_LABEL]: resourceMapLabel } : {}), + let newBuildRunData: BuildRun; + + if (isV1Alpha1Resource(buildRun)) { + newBuildRunData = { + apiVersion: 'shipwright.io/v1alpha1', + kind: 'BuildRun', + metadata: { + namespace: buildRun.metadata.namespace, + generateName, + labels: { + [BUILDRUN_TO_BUILD_REFERENCE_LABEL]: buildRefName, + ...(resourceMapLabel ? { [BUILDRUN_TO_RESOURCE_MAP_LABEL]: resourceMapLabel } : {}), + }, }, - }, - spec: { - buildRef: { - name: buildRefName, + spec: { + buildRef: { + name: buildRefName, + }, }, - }, - }; + }; + } else { + newBuildRunData = { + apiVersion: 'shipwright.io/v1beta1', + kind: 'BuildRun', + metadata: { + namespace: buildRun.metadata.namespace, + generateName, + labels: { + [BUILDRUN_TO_BUILD_REFERENCE_LABEL]: buildRefName, + ...(resourceMapLabel ? { [BUILDRUN_TO_RESOURCE_MAP_LABEL]: resourceMapLabel } : {}), + }, + }, + spec: { + build: { + name: buildRefName, + }, + }, + }; + } + return k8sCreateResource({ - model: BuildRunModel, + model: isV1Alpha1Resource(newBuildRunData) ? BuildRunModelV1Alpha1 : BuildRunModel, data: newBuildRunData, }); } if (buildSpec) { const generateName = buildRun.metadata.generateName || `${buildRun.metadata.name}-`; + let newBuildRunData: BuildRun; - const newBuildRunData: BuildRun = { - apiVersion: 'shipwright.io/v1alpha1', - kind: 'BuildRun', - metadata: { - namespace: buildRun.metadata.namespace, - generateName, - ...(resourceMapLabel - ? { labels: { [BUILDRUN_TO_RESOURCE_MAP_LABEL]: resourceMapLabel } } - : {}), - }, - spec: { - buildSpec, - }, - }; + if (isV1Alpha1Resource(buildRun)) { + newBuildRunData = { + apiVersion: 'shipwright.io/v1alpha1', + kind: 'BuildRun', + metadata: { + namespace: buildRun.metadata.namespace, + generateName, + ...(resourceMapLabel + ? { labels: { [BUILDRUN_TO_RESOURCE_MAP_LABEL]: resourceMapLabel } } + : {}), + }, + spec: { + buildSpec, + }, + }; + } else { + newBuildRunData = { + apiVersion: 'shipwright.io/v1beta1', + kind: 'BuildRun', + metadata: { + namespace: buildRun.metadata.namespace, + generateName, + ...(resourceMapLabel + ? { labels: { [BUILDRUN_TO_RESOURCE_MAP_LABEL]: resourceMapLabel } } + : {}), + }, + spec: { + build: { + spec: buildSpec, + }, + }, + }; + } return k8sCreateResource({ - model: BuildRunModel, + model: isV1Alpha1Resource(newBuildRunData) ? BuildRunModelV1Alpha1 : BuildRunModel, data: newBuildRunData, }); } - throw new Error('Could not rerun BuildRun without buildRef.name or inline buildSpec.'); + if (isV1Alpha1Resource(buildRun)) { + throw new Error('Could not rerun BuildRun without buildRef.name or inline buildSpec.'); + } else { + throw new Error('Could not rerun BuildRun without build.name or inline buildSpec.'); + } }; diff --git a/frontend/packages/shipwright-plugin/src/components/build-decorators/ShipwrightBuildDecorator.tsx b/frontend/packages/shipwright-plugin/src/components/build-decorators/ShipwrightBuildDecorator.tsx index 1e6a5d34c9a..3898c17f9c8 100644 --- a/frontend/packages/shipwright-plugin/src/components/build-decorators/ShipwrightBuildDecorator.tsx +++ b/frontend/packages/shipwright-plugin/src/components/build-decorators/ShipwrightBuildDecorator.tsx @@ -9,9 +9,9 @@ import { K8sResourceKind } from '@console/internal/module/k8s'; import { Status } from '@console/shared'; import { BuildDecoratorBubble } from '@console/topology/src/components/graph-view'; import { BUILDRUN_TO_RESOURCE_MAP_LABEL } from '../../const'; -import { BuildRunModel } from '../../models'; +import { BuildRunModel, BuildRunModelV1Alpha1 } from '../../models'; import { Build, BuildRun } from '../../types'; -import { getLatestBuildRunStatusforDeployment } from '../../utils'; +import { getLatestBuildRunStatusforDeployment, isV1Alpha1Resource } from '../../utils'; type BuildRunDecoratorProps = { buildRuns: BuildRun[]; @@ -50,8 +50,12 @@ export const ConnectedBuildRunDecorator: React.FC = (props) => { const pages: Page[] = [ navFactory.details(BuildDetailsTab), - navFactory.editYaml(viewYamlComponent), + navFactory.editYaml(), { href: 'buildruns', // t('shipwright-plugin~BuildRuns') diff --git a/frontend/packages/shipwright-plugin/src/components/build-details/BuildSpecSection.tsx b/frontend/packages/shipwright-plugin/src/components/build-details/BuildSpecSection.tsx index 032c77b3ee1..09fefc6b38f 100644 --- a/frontend/packages/shipwright-plugin/src/components/build-details/BuildSpecSection.tsx +++ b/frontend/packages/shipwright-plugin/src/components/build-details/BuildSpecSection.tsx @@ -4,8 +4,14 @@ import { useTranslation } from 'react-i18next'; import { getGroupVersionKindForModel } from '@console/dynamic-plugin-sdk/src/utils/k8s/k8s-ref'; import { ResourceLink, DetailsItem, ExternalLink } from '@console/internal/components/utils'; import { SecretModel } from '@console/internal/models'; -import { ClusterBuildStrategyModel, BuildStrategyModel } from '../../models'; +import { + ClusterBuildStrategyModel, + BuildStrategyModel, + ClusterBuildStrategyModelV1Alpha1, + BuildStrategyModelV1Alpha1, +} from '../../models'; import { Build, BuildRun, BuildSpec } from '../../types'; +import { isV1Alpha1Resource } from '../../utils'; import BuildOutput from '../build-list/BuildOutput'; type BuildSpecSectionProps = { @@ -23,18 +29,39 @@ const BuildSpecSection: React.FC = ({ obj, buildSpec, pat const namespace = obj?.metadata?.namespace; + const url = isV1Alpha1Resource(obj) ? buildSpec.source?.url : buildSpec.source?.git?.url; + const contextDir = buildSpec.source?.contextDir; + const credentials = isV1Alpha1Resource(obj) + ? buildSpec.source?.credentials?.name + : buildSpec.source?.git?.cloneSecret; + const outputCredentials = isV1Alpha1Resource(obj) + ? buildSpec.output?.credentials?.name + : buildSpec.output?.pushSecret; + const dockerFile = isV1Alpha1Resource(obj) + ? buildSpec?.dockerfile + : buildSpec?.paramValues?.find((param) => param?.name === 'dockerfile')?.value; + const builderImage = isV1Alpha1Resource(obj) + ? buildSpec?.builder?.image + : buildSpec?.paramValues?.find((param) => param?.name === 'builder-image')?.value; + return (
{buildSpec.strategy ? ( {buildSpec.strategy.kind === 'ClusterBuildStrategy' ? ( ) : ( @@ -42,45 +69,47 @@ const BuildSpecSection: React.FC = ({ obj, buildSpec, pat ) : null} - {buildSpec.source?.url ? ( + {url ? ( - + ) : null} - {buildSpec.source?.contextDir ? ( + {contextDir ? ( - - {buildSpec.source.contextDir} - + {contextDir} ) : null} - {buildSpec.source?.credentials ? ( + {credentials ? ( ) : null} - {buildSpec.sources?.length ? ( + {isV1Alpha1Resource(obj) && buildSpec?.sources?.length ? ( {buildSpec.sources?.map((source) => ( @@ -94,27 +123,31 @@ const BuildSpecSection: React.FC = ({ obj, buildSpec, pat ) : null} - {buildSpec.dockerfile ? ( + {dockerFile ? ( - - {buildSpec.dockerfile} - + {dockerFile} ) : null} - {buildSpec.builder?.image ? ( + {builderImage ? ( - - {buildSpec.builder.image} - + {builderImage} ) : null} @@ -128,16 +161,20 @@ const BuildSpecSection: React.FC = ({ obj, buildSpec, pat ) : null} - {buildSpec.output?.credentials ? ( + {outputCredentials ? ( ) : null} diff --git a/frontend/packages/shipwright-plugin/src/components/build-list/BuildListPage.tsx b/frontend/packages/shipwright-plugin/src/components/build-list/BuildListPage.tsx index 94326c3a285..128164b6f7e 100644 --- a/frontend/packages/shipwright-plugin/src/components/build-list/BuildListPage.tsx +++ b/frontend/packages/shipwright-plugin/src/components/build-list/BuildListPage.tsx @@ -1,9 +1,10 @@ import * as React from 'react'; import { useTranslation } from 'react-i18next'; +import { useFlag } from '@console/dynamic-plugin-sdk/src/lib-core'; import { ListPage, ListPageProps } from '@console/internal/components/factory'; import { RowFilter } from '@console/internal/components/filter-toolbar'; import { referenceForModel } from '@console/internal/module/k8s'; -import { BuildModel } from '../../models'; +import { BuildModel, BuildModelV1Alpha1 } from '../../models'; import { Build } from '../../types'; import { getBuildRunStatus } from '../buildrun-status/BuildRunStatus'; import { BuildTable } from './BuildTable'; @@ -45,7 +46,11 @@ const BuildListPage: React.FC = (props) => { return ( { export const BuildRow: React.FC> = ({ obj: build }) => { const kindReference = referenceFor(build); const context = { [kindReference]: build }; - const buildRunKindReference = referenceForModel(BuildRunModel); + const buildRunKindReference = isV1Alpha1Resource(build) + ? referenceForModel(BuildRunModelV1Alpha1) + : referenceForModel(BuildRunModel); return ( <> @@ -149,7 +152,10 @@ type BuildTableProps = TableProps & { export const BuildTable: React.FC = (props) => { const { t } = useTranslation(); - const buildRunModel = referenceForModel(BuildRunModel); + const buildRunModel = useFlag('SHIPWRIGHT_BUILDRUN') + ? referenceForModel(BuildRunModel) + : referenceForModel(BuildRunModelV1Alpha1); + const [buildRuns, buildRunsLoaded, buildRunsLoadError] = useK8sWatchResource({ kind: buildRunModel, namespace: props.namespace, diff --git a/frontend/packages/shipwright-plugin/src/components/build-tabsection/BuildOverview.tsx b/frontend/packages/shipwright-plugin/src/components/build-tabsection/BuildOverview.tsx index 8d1de76a5e7..8d65e4458db 100644 --- a/frontend/packages/shipwright-plugin/src/components/build-tabsection/BuildOverview.tsx +++ b/frontend/packages/shipwright-plugin/src/components/build-tabsection/BuildOverview.tsx @@ -8,11 +8,11 @@ import { SidebarSectionHeading, } from '@console/internal/components/utils'; import { referenceForModel } from '@console/internal/module/k8s/k8s'; -import { OverviewItem } from '@console/shared'; +import { OverviewItem, useFlag } from '@console/shared'; import { BUILDRUN_TO_RESOURCE_MAP_LABEL } from '../../const'; -import { BuildModel, BuildRunModel } from '../../models'; +import { BuildModel, BuildModelV1Alpha1, BuildRunModel, BuildRunModelV1Alpha1 } from '../../models'; import { Build, BuildRun } from '../../types'; -import { byCreationTime } from '../../utils'; +import { byCreationTime, isV1Alpha1Resource } from '../../utils'; import BuildRunItem from './BuildRunItem'; import StartBuildButton from './StartBuildButton'; import TriggerLastBuildButton from './TriggerLastBuildButton'; @@ -29,6 +29,9 @@ type BuildsOverviewProps = { const BuildsOverview: React.FC = ({ item: { builds, buildRuns, obj } }) => { const { t } = useTranslation(); const resourceLabel = obj.metadata?.labels?.[BUILDRUN_TO_RESOURCE_MAP_LABEL]; + const buildRunModel = useFlag('SHIPWRIGHT_BUILDRUN') + ? referenceForModel(BuildRunModel) + : referenceForModel(BuildRunModelV1Alpha1); const buildRunsforResource = resourceLabel ? buildRuns.filter((buildRun) => { return resourceLabel === buildRun.metadata?.labels?.[BUILDRUN_TO_RESOURCE_MAP_LABEL]; @@ -53,7 +56,7 @@ const BuildsOverview: React.FC = ({ item: { builds, buildRu = ({ item: { builds, buildRu {buildsForResource.map((build) => { const buildRunsforBuild = buildRuns - .filter( - (buildRun) => - buildRun.spec.buildRef?.name === build.metadata.name && - buildRun.metadata?.labels?.[BUILDRUN_TO_RESOURCE_MAP_LABEL] === - obj.metadata?.labels?.[BUILDRUN_TO_RESOURCE_MAP_LABEL], + .filter((buildRun) => + isV1Alpha1Resource(buildRun) + ? buildRun.spec.buildRef?.name === build.metadata.name + : buildRun.spec.build?.name === build.metadata.name && + buildRun.metadata?.labels?.[BUILDRUN_TO_RESOURCE_MAP_LABEL] === + obj.metadata?.labels?.[BUILDRUN_TO_RESOURCE_MAP_LABEL], ) .sort(byCreationTime); return ( @@ -81,7 +85,11 @@ const BuildsOverview: React.FC = ({ item: { builds, buildRu diff --git a/frontend/packages/shipwright-plugin/src/components/build-tabsection/BuildRunItem.tsx b/frontend/packages/shipwright-plugin/src/components/build-tabsection/BuildRunItem.tsx index ebdfff25b27..e217c24d586 100644 --- a/frontend/packages/shipwright-plugin/src/components/build-tabsection/BuildRunItem.tsx +++ b/frontend/packages/shipwright-plugin/src/components/build-tabsection/BuildRunItem.tsx @@ -5,8 +5,9 @@ import { Link } from 'react-router-dom'; import { resourcePath } from '@console/internal/components/utils'; import { fromNow } from '@console/internal/components/utils/datetime'; import { referenceForModel } from '@console/internal/module/k8s'; -import { BuildRunModel } from '../../models'; +import { BuildRunModel, BuildRunModelV1Alpha1 } from '../../models'; import { BuildRun } from '../../types'; +import { isV1Alpha1Resource } from '../../utils'; import BuildRunStatus from '../buildrun-status/BuildRunStatus'; import './BuildRunItem.scss'; @@ -21,7 +22,8 @@ const BuildRunItem: React.FC = ({ buildRun }) => { metadata: { name, namespace, creationTimestamp }, status, } = buildRun; - const path = resourcePath(referenceForModel(BuildRunModel), name, namespace); + const buildRunModel = isV1Alpha1Resource(buildRun) ? BuildRunModelV1Alpha1 : BuildRunModel; + const path = resourcePath(referenceForModel(buildRunModel), name, namespace); const lastUpdated = status ? status.completionTime || status.startTime || creationTimestamp : creationTimestamp; diff --git a/frontend/packages/shipwright-plugin/src/components/buildrun-details/BuildRunDetailsPage.tsx b/frontend/packages/shipwright-plugin/src/components/buildrun-details/BuildRunDetailsPage.tsx index 41c46f29782..c886b868a7b 100644 --- a/frontend/packages/shipwright-plugin/src/components/buildrun-details/BuildRunDetailsPage.tsx +++ b/frontend/packages/shipwright-plugin/src/components/buildrun-details/BuildRunDetailsPage.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { DetailsPage, DetailsPageProps } from '@console/internal/components/factory'; -import { Page, navFactory, viewYamlComponent } from '@console/internal/components/utils'; +import { Page, navFactory } from '@console/internal/components/utils'; import { referenceFor } from '@console/internal/module/k8s'; import { ActionMenu, @@ -29,7 +29,7 @@ const BuildRunDetailsPage: React.FC = (props) => { const pages: Page[] = [ navFactory.details(BuildRunDetailsTab), - navFactory.editYaml(viewYamlComponent), + navFactory.editYaml(), navFactory.logs(BuildRunLogsTab), navFactory.events(BuildRunEventsTab), ]; diff --git a/frontend/packages/shipwright-plugin/src/components/buildrun-details/BuildRunDetailsTab.tsx b/frontend/packages/shipwright-plugin/src/components/buildrun-details/BuildRunDetailsTab.tsx index 9515c587a8b..f9f43e5254f 100644 --- a/frontend/packages/shipwright-plugin/src/components/buildrun-details/BuildRunDetailsTab.tsx +++ b/frontend/packages/shipwright-plugin/src/components/buildrun-details/BuildRunDetailsTab.tsx @@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next'; import { Conditions } from '@console/internal/components/conditions'; import { SectionHeading, ResourceSummary } from '@console/internal/components/utils'; import { BuildRun } from '../../types'; +import { isV1Alpha1Resource } from '../../utils'; import BuildSpecSection from '../build-details/BuildSpecSection'; import BuildRunSection from './BuildRunSection'; @@ -32,8 +33,19 @@ const BuildRunDetailsTab: React.FC = ({ obj: buildRun } {t('shipwright-plugin~BuildSpec details')} diff --git a/frontend/packages/shipwright-plugin/src/components/buildrun-details/BuildRunLogsTab.tsx b/frontend/packages/shipwright-plugin/src/components/buildrun-details/BuildRunLogsTab.tsx index df19117f966..3ace80c4fc1 100644 --- a/frontend/packages/shipwright-plugin/src/components/buildrun-details/BuildRunLogsTab.tsx +++ b/frontend/packages/shipwright-plugin/src/components/buildrun-details/BuildRunLogsTab.tsx @@ -9,6 +9,7 @@ import TaskRunLog from '@console/pipelines-plugin/src/components/taskruns/TaskRu import { TaskRunModel } from '@console/pipelines-plugin/src/models/pipelines'; import { TaskRunKind } from '@console/pipelines-plugin/src/types'; import { BuildRun } from '../../types'; +import { isV1Alpha1Resource } from '../../utils'; type BuildRunLogsTabProps = { obj: BuildRun; @@ -25,7 +26,9 @@ const BuildRunLogsTab: React.FC = ({ }) => { const { t } = useTranslation(); - const taskRunRef = buildRun.status?.latestTaskRunRef; + const taskRunRef = isV1Alpha1Resource(buildRun) + ? buildRun.status?.latestTaskRunRef + : buildRun.status?.taskRunName; const [taskRun, taskRunLoaded, taskRunLoadError] = useK8sWatchResource( taskRunRef ? { diff --git a/frontend/packages/shipwright-plugin/src/components/buildrun-details/BuildRunSection.tsx b/frontend/packages/shipwright-plugin/src/components/buildrun-details/BuildRunSection.tsx index d5f0e4901bc..2a6643a9e23 100644 --- a/frontend/packages/shipwright-plugin/src/components/buildrun-details/BuildRunSection.tsx +++ b/frontend/packages/shipwright-plugin/src/components/buildrun-details/BuildRunSection.tsx @@ -2,8 +2,9 @@ import * as React from 'react'; import { useTranslation } from 'react-i18next'; import { getGroupVersionKindForModel } from '@console/dynamic-plugin-sdk/src/utils/k8s/k8s-ref'; import { ResourceLink, DetailsItem, Timestamp } from '@console/internal/components/utils'; -import { BuildModel } from '../../models'; +import { BuildModel, BuildModelV1Alpha1 } from '../../models'; import { BuildRun } from '../../types'; +import { getBuildNameFromBuildRun, isV1Alpha1Resource } from '../../utils'; import BuildRunDuration from '../buildrun-duration/BuildRunDuration'; import BuildRunStatus from '../buildrun-status/BuildRunStatus'; @@ -13,6 +14,7 @@ type BuildRunSectionProps = { const BuildRunSection: React.FC = ({ buildRun }) => { const { t } = useTranslation(); + const buildModel = isV1Alpha1Resource(buildRun) ? BuildModelV1Alpha1 : BuildModel; return (
@@ -21,12 +23,16 @@ const BuildRunSection: React.FC = ({ buildRun }) => { - - {buildRun.spec.buildRef?.name ? ( + + {getBuildNameFromBuildRun(buildRun) ? ( ) : ( '-' diff --git a/frontend/packages/shipwright-plugin/src/components/buildrun-duration/__tests__/BuildRunDuration.spec.tsx b/frontend/packages/shipwright-plugin/src/components/buildrun-duration/__tests__/BuildRunDuration.spec.tsx index 077d195faa8..eac7491998e 100644 --- a/frontend/packages/shipwright-plugin/src/components/buildrun-duration/__tests__/BuildRunDuration.spec.tsx +++ b/frontend/packages/shipwright-plugin/src/components/buildrun-duration/__tests__/BuildRunDuration.spec.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { render } from '@testing-library/react'; -import { incompleteBuildRun } from '../../../__tests__/mock-data'; +import { incompleteBuildRun } from '../../../__tests__/mock-data-v1beta1'; import { BuildRun } from '../../../types'; import BuildRunDuration, { getDuration } from '../BuildRunDuration'; @@ -46,6 +46,7 @@ describe('BuildRunDuration', () => { namespace: 'a-namespace', name: 'incomplete-buildrun', }, + spec: {}, status: { startTime: '2022-06-06T13:52:34Z', completionTime: '2022-06-06T13:53:26Z', @@ -63,6 +64,7 @@ describe('BuildRunDuration', () => { namespace: 'a-namespace', name: 'incomplete-buildrun', }, + spec: {}, status: { startTime: '2022-06-06T13:52:34Z', }, diff --git a/frontend/packages/shipwright-plugin/src/components/buildrun-list/BuildRunListPage.tsx b/frontend/packages/shipwright-plugin/src/components/buildrun-list/BuildRunListPage.tsx index 5731fe6ab46..e4158fe334b 100644 --- a/frontend/packages/shipwright-plugin/src/components/buildrun-list/BuildRunListPage.tsx +++ b/frontend/packages/shipwright-plugin/src/components/buildrun-list/BuildRunListPage.tsx @@ -1,9 +1,10 @@ import * as React from 'react'; import { useTranslation } from 'react-i18next'; +import { useFlag } from '@console/dynamic-plugin-sdk/src/lib-core'; import { ListPage } from '@console/internal/components/factory'; import { RowFilter } from '@console/internal/components/filter-toolbar'; import { referenceForModel } from '@console/internal/module/k8s'; -import { BuildRunModel } from '../../models'; +import { BuildRunModel, BuildRunModelV1Alpha1 } from '../../models'; import { BuildRun, ComputedBuildRunStatus } from '../../types'; import { getBuildRunStatus } from '../buildrun-status/BuildRunStatus'; import { BuildRunTable } from './BuildRunTable'; @@ -37,7 +38,11 @@ const BuildRunListPage: React.FC = (props) => { return ( { + return resource.apiVersion === 'shipwright.io/v1alpha1'; +}; + +export const getBuildNameFromBuildRun = (buildRun: BuildRun) => { + if (isV1Alpha1Resource(buildRun)) { + return buildRun.spec?.buildRef?.name; + } + return buildRun.spec?.build?.name; +}; diff --git a/frontend/packages/topology/locales/zh/topology.json b/frontend/packages/topology/locales/zh/topology.json index 616f7995d63..66aec75d10a 100644 --- a/frontend/packages/topology/locales/zh/topology.json +++ b/frontend/packages/topology/locales/zh/topology.json @@ -150,7 +150,7 @@ "Find by label...": "按标签查找......", "Find by name...": "按名称查找......", "Find by name": "按名称查找", - "Search results may appear outside of the visible area. <2>Click here to fit to the screen.": "搜索结果可能会出现在可见区域之外,点这里 <2>来匹配屏幕。", + "Search results may appear outside of the visible area. <2>Click here to fit to the screen.": "搜索结果可能会出现在可见区域之外,<2>点这里来匹配屏幕。", "Kiali": "Kiali", "Create Service Binding": "创建服务绑定", "Operator groupings": "Operator 组", @@ -169,4 +169,4 @@ "Delete": "删除", "Unable to update application, invalid resource type: {{kind}}": "无法更新应用程序,无效资源类型: {{kind}}", "Can not create a connection from a node to itself.": "无法从节点到其自身创建连接。" -} \ No newline at end of file +} diff --git a/frontend/packages/topology/src/behavior/withCreateConnector.tsx b/frontend/packages/topology/src/behavior/withCreateConnector.tsx index 053dc87e71d..8c77b70f896 100644 --- a/frontend/packages/topology/src/behavior/withCreateConnector.tsx +++ b/frontend/packages/topology/src/behavior/withCreateConnector.tsx @@ -1,6 +1,5 @@ import * as React from 'react'; import { css } from '@patternfly/react-styles'; -import styles from '@patternfly/react-styles/css/components/Topology/topology-components'; import { hullPath, DefaultCreateConnector, @@ -26,6 +25,7 @@ import { useCombineRefs, useHover, } from '@patternfly/react-topology'; +import styles from '@patternfly/react-topology/src/css/topology-components'; import { observer } from 'mobx-react'; // diff --git a/frontend/packages/topology/src/components/graph-view/components/edges/ServiceBinding.tsx b/frontend/packages/topology/src/components/graph-view/components/edges/ServiceBinding.tsx index da80e6333bf..5d7aa1454d1 100644 --- a/frontend/packages/topology/src/components/graph-view/components/edges/ServiceBinding.tsx +++ b/frontend/packages/topology/src/components/graph-view/components/edges/ServiceBinding.tsx @@ -1,6 +1,5 @@ import * as React from 'react'; import { css } from '@patternfly/react-styles'; -import styles from '@patternfly/react-styles/css/components/Topology/topology-components'; import { Edge, EdgeTerminalType, @@ -11,6 +10,7 @@ import { WithTargetDragProps, WithContextMenuProps, } from '@patternfly/react-topology'; +import styles from '@patternfly/react-topology/src/css/topology-components'; import { ComputedServiceBindingStatus } from '@console/service-binding-plugin/src/types'; import { getComputedServiceBindingStatus } from '@console/service-binding-plugin/src/utils'; import BaseEdge from './BaseEdge'; diff --git a/frontend/packages/topology/src/components/graph-view/components/groups/ApplicationGroupExpanded.tsx b/frontend/packages/topology/src/components/graph-view/components/groups/ApplicationGroupExpanded.tsx index 89d592d5d3c..41637bfc01a 100644 --- a/frontend/packages/topology/src/components/graph-view/components/groups/ApplicationGroupExpanded.tsx +++ b/frontend/packages/topology/src/components/graph-view/components/groups/ApplicationGroupExpanded.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import CollapseIcon from '@patternfly/react-icons/dist/esm/icons/compress-alt-icon'; import { css } from '@patternfly/react-styles'; -import styles from '@patternfly/react-styles/css/components/Topology/topology-components'; import { NodeLabel, Layer, @@ -24,6 +23,7 @@ import { WithSelectionProps, CollapsibleGroupProps, } from '@patternfly/react-topology'; +import styles from '@patternfly/react-topology/src/css/topology-components'; import { polygonHull } from 'd3-polygon'; import * as _ from 'lodash'; import { observer } from 'mobx-react'; diff --git a/frontend/packages/topology/src/components/page/TopologyView.tsx b/frontend/packages/topology/src/components/page/TopologyView.tsx index 35b5127f0c3..817f2428660 100644 --- a/frontend/packages/topology/src/components/page/TopologyView.tsx +++ b/frontend/packages/topology/src/components/page/TopologyView.tsx @@ -75,7 +75,7 @@ import TopologyEmptyState from './TopologyEmptyState'; import './TopologyView.scss'; const FILTER_ACTIVE_CLASS = 'odc-m-filter-active'; -const MAX_NODES_LIMIT = 100; +const MAX_NODES_LIMIT = 200; interface StateProps { application?: string; diff --git a/frontend/packages/vsphere-plugin/__tests__/__snapshots__/utils.spec.ts.snap b/frontend/packages/vsphere-plugin/__tests__/__snapshots__/utils.spec.ts.snap index 61b85a27e99..688fb14a8c6 100644 --- a/frontend/packages/vsphere-plugin/__tests__/__snapshots__/utils.spec.ts.snap +++ b/frontend/packages/vsphere-plugin/__tests__/__snapshots__/utils.spec.ts.snap @@ -11,7 +11,8 @@ foofoo=barbar server=https://1.2.3.4/something datacenter=my-datacenter default-datastore=my-default-ds -folder=/my/folder +folder="/CLUSTER/vm/SomeGeo/Infra\\ \\(Dev\\ Env\\)/OpenShift" +resourcepool-path=/my-datacenter/host/foo-cluster/Resources [VirtualCenter "https://1.2.3.4/something"] datacenters=my-datacenter @@ -27,7 +28,8 @@ foofoo=barbar server=https://1.2.3.4/something datacenter=my-datacenter default-datastore=my-default-ds -folder=/my/folder +folder=\\"/CLUSTER/vm/SomeGeo/Infra\\\\ \\\\(Dev\\\\ Env\\\\)/OpenShift\\" +resourcepool-path=/my-datacenter/host/foo-cluster/Resources [VirtualCenter \\"https://1.2.3.4/something\\"] datacenters=my-datacenter @@ -44,7 +46,8 @@ insecure-flag=1 server=https://1.2.3.4/something datacenter=my-datacenter default-datastore=my-default-ds -folder=/my/folder +folder=\\"/CLUSTER/vm/SomeGeo/Infra\\\\ \\\\(Dev\\\\ Env\\\\)/OpenShift\\" +resourcepool-path=/my-datacenter/host/foo-cluster/Resources [VirtualCenter \\"https://1.2.3.4/something\\"] datacenters=my-datacenter diff --git a/frontend/packages/vsphere-plugin/__tests__/utils.spec.ts b/frontend/packages/vsphere-plugin/__tests__/utils.spec.ts index 5dc17789cbd..ce5955f291d 100644 --- a/frontend/packages/vsphere-plugin/__tests__/utils.spec.ts +++ b/frontend/packages/vsphere-plugin/__tests__/utils.spec.ts @@ -8,7 +8,7 @@ const config: ConnectionFormContextValues = { vcenter: 'https://1.2.3.4/something', datacenter: 'my-datacenter', defaultDatastore: 'my-default-ds', - folder: '/my/folder', + folder: '/CLUSTER/vm/SomeGeo/Infra\\ \\(Dev\\ Env\\)/OpenShift', vCenterCluster: 'foo-cluster', }; diff --git a/frontend/packages/vsphere-plugin/package.json b/frontend/packages/vsphere-plugin/package.json index 00ed9a4a540..95f3dfa5164 100644 --- a/frontend/packages/vsphere-plugin/package.json +++ b/frontend/packages/vsphere-plugin/package.json @@ -7,7 +7,8 @@ "lint": "yarn --cwd ../.. eslint packages/vsphere-plugin --fix" }, "dependencies": { - "@console/dynamic-plugin-sdk": "0.0.0-fixed" + "@console/dynamic-plugin-sdk": "0.0.0-fixed", + "ini": "4.1.1" }, "consolePlugin": { "entry": "src/plugin.ts", diff --git a/frontend/packages/vsphere-plugin/src/components/persist.ts b/frontend/packages/vsphere-plugin/src/components/persist.ts index 072be8c0403..6cde3f01cc9 100644 --- a/frontend/packages/vsphere-plugin/src/components/persist.ts +++ b/frontend/packages/vsphere-plugin/src/components/persist.ts @@ -130,7 +130,7 @@ const persistProviderConfigMap = async ( config: ConnectionFormContextValues, cloudProviderConfig?: ConfigMap, ): Promise => { - const { vcenter, datacenter, defaultDatastore, folder } = config; + const { vcenter, datacenter, defaultDatastore, folder, vCenterCluster } = config; if (cloudProviderConfig) { const configIniString = mergeCloudProviderConfig( @@ -172,6 +172,7 @@ server = "${vcenter}" datacenter = "${datacenter}" default-datastore = "${defaultDatastore}" folder = "${folder}" +resourcepool-path = "/${datacenter}/host/${vCenterCluster}/Resources" [VirtualCenter "${vcenter}"] datacenters = "${datacenter}" diff --git a/frontend/packages/vsphere-plugin/src/components/utils.ts b/frontend/packages/vsphere-plugin/src/components/utils.ts index 3e71733346f..906dafc98c9 100644 --- a/frontend/packages/vsphere-plugin/src/components/utils.ts +++ b/frontend/packages/vsphere-plugin/src/components/utils.ts @@ -27,7 +27,13 @@ export const decodeBase64 = (data: string) => Buffer.from(data, 'base64').toStri export const mergeCloudProviderConfig = ( existingIni: string, - { vcenter, datacenter, defaultDatastore: defaultdatastore, folder }: ConnectionFormContextValues, + { + vcenter, + datacenter, + defaultDatastore: defaultdatastore, + folder, + vCenterCluster, + }: ConnectionFormContextValues, ): string => { const configIni = decode(existingIni); @@ -42,6 +48,7 @@ export const mergeCloudProviderConfig = ( configIni.Workspace.datacenter = datacenter; configIni.Workspace['default-datastore'] = defaultdatastore; configIni.Workspace.folder = folder; + configIni.Workspace['resourcepool-path'] = `/${datacenter}/host/${vCenterCluster}/Resources`; Object.keys(configIni).forEach((k: string) => { if (k.startsWith('VirtualCenter')) { @@ -62,6 +69,12 @@ export const mergeCloudProviderConfig = ( // We do not want to have the value escaped ("safe") return `[VirtualCenter "${vcenter}"]`; } + if (line.startsWith('folder=')) { + const value = line.split('folder=', 2); + if (value[1]) { + return `folder="${value[1]}"`; + } + } return line; }) .join('\n'); diff --git a/frontend/packages/vsphere-plugin/src/hooks/use-connection-form.ts b/frontend/packages/vsphere-plugin/src/hooks/use-connection-form.ts index 0fd09f76026..55cee2a4200 100644 --- a/frontend/packages/vsphere-plugin/src/hooks/use-connection-form.ts +++ b/frontend/packages/vsphere-plugin/src/hooks/use-connection-form.ts @@ -3,7 +3,6 @@ import { K8sModel, k8sGet } from '@console/dynamic-plugin-sdk/src/api/core-api'; import { useConnectionFormContext } from '../components/ConnectionFormContext'; import { ConnectionFormContextSetters } from '../components/types'; import { decodeBase64, parseKeyValue } from '../components/utils'; -import { FAILURE_DOMAIN_NAME } from '../constants'; import { ConfigMap, Infrastructure, Secret } from '../resources'; import { useConnectionModels } from './use-connection-models'; @@ -25,6 +24,15 @@ export const initialLoad = async ( const dc = keyValues.datacenter || ''; const ds = keyValues['default-datastore'] || ''; const folder = keyValues.folder || ''; + + let vCenterCluster = ''; + const resourcePoolPath = keyValues['resourcepool-path'] as string; + if (resourcePoolPath?.length) { + const paths = resourcePoolPath.split('/'); + if (paths.length > 3) { + [, , , vCenterCluster] = paths; + } + } let username = ''; let pwd = ''; @@ -56,7 +64,6 @@ export const initialLoad = async ( } } - let vCenterCluster = ''; try { const infrastructure = await k8sGet({ model: infrastructureModel, @@ -64,9 +71,19 @@ export const initialLoad = async ( }); const domain = infrastructure?.spec?.platformSpec?.vsphere?.failureDomains?.find( - (d) => d.name === FAILURE_DOMAIN_NAME, + (d) => d.server === server, ); - vCenterCluster = domain?.topology?.networks?.[0] || ''; + if (domain) { + const computeCluster = domain.topology?.computeCluster?.split('/'); + let infraVCenterCluster = ''; + if (computeCluster.length > 3) { + [, , , infraVCenterCluster] = computeCluster; + } + + if (!vCenterCluster) { + vCenterCluster = infraVCenterCluster; + } + } } catch (e) { // eslint-disable-next-line no-console console.error('Failed to fetch infrastructure resource', e); diff --git a/frontend/public/co-fetch.ts b/frontend/public/co-fetch.ts index bad21281d60..bc61ab2636c 100644 --- a/frontend/public/co-fetch.ts +++ b/frontend/public/co-fetch.ts @@ -56,7 +56,8 @@ export const validateStatus = async ( } if (response.status === 401 && shouldLogout(url)) { - authSvc.logout(window.location.pathname, getActiveCluster(storeHandler.getStore()?.getState())); // TODO remove multicluster + const next = window.location.pathname + window.location.search + window.location.hash; + authSvc.logout(next, getActiveCluster(storeHandler.getStore()?.getState())); // TODO remove multicluster } const contentType = response.headers.get('content-type'); diff --git a/frontend/public/components/RBAC/bindings.jsx b/frontend/public/components/RBAC/bindings.jsx index 940510ac776..eaa8267540a 100644 --- a/frontend/public/components/RBAC/bindings.jsx +++ b/frontend/public/components/RBAC/bindings.jsx @@ -146,6 +146,14 @@ const menuActions = ({ subjectIndex, subjects }, startImpersonate) => { actions.unshift(() => ({ label: i18next.t('public~Impersonate {{kind}} "{{name}}"', subject), callback: () => startImpersonate(subject.kind, subject.name), + // Must use API group authorization.k8s.io, NOT user.openshift.io + // See https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation + accessReview: { + group: 'authorization.k8s.io', + resource: subject.kind === 'Group' ? 'groups' : 'users', + name: subject.name, + verb: 'impersonate', + }, })); } diff --git a/frontend/public/components/app.jsx b/frontend/public/components/app.jsx index b8134b4c927..526d84c7131 100644 --- a/frontend/public/components/app.jsx +++ b/frontend/public/components/app.jsx @@ -281,9 +281,21 @@ const AppRouter = () => { const CaptureTelemetry = React.memo(function CaptureTelemetry() { const [perspective] = useActivePerspective(); const fireTelemetryEvent = useTelemetry(); - + const [debounceTime, setDebounceTime] = React.useState(5000); + const [titleOnLoad, setTitleOnLoad] = React.useState(''); // notify of identity change const user = useSelector(getUser); + const telemetryTitle = getTelemetryTitle(); + + React.useEffect( + () => + setTimeout(() => { + setTitleOnLoad(telemetryTitle); + setDebounceTime(500); + }, 5000), + [], + ); + React.useEffect(() => { if (user.metadata?.uid || user.metadata?.name) { fireTelemetryEvent('identify', { perspective, user }); @@ -301,8 +313,11 @@ const CaptureTelemetry = React.memo(function CaptureTelemetry() { title: getTelemetryTitle(), ...withoutSensitiveInformations(location), }); - }); + }, debounceTime); React.useEffect(() => { + if (!titleOnLoad) { + return; + } fireUrlChangeEvent(history.location); let { pathname, search } = history.location; const unlisten = history.listen((location) => { diff --git a/frontend/public/components/build.tsx b/frontend/public/components/build.tsx index f0a4144340f..96889fb0359 100644 --- a/frontend/public/components/build.tsx +++ b/frontend/public/components/build.tsx @@ -38,6 +38,7 @@ import { history, humanizeBinaryBytes, humanizeCpuCores, + isManaged, isUpstream, Kebab, KebabAction, @@ -231,7 +232,7 @@ const BuildMetrics = ({ obj }) => { const OpenShiftPipelines: React.FC = () => { const { t } = useTranslation(); const text = t('public~OpenShift Pipelines based on Tekton'); - return isUpstream() ? ( + return isUpstream() || isManaged() ? ( <>{text} ) : ( diff --git a/frontend/public/components/catalog/catalog-item-icon.tsx b/frontend/public/components/catalog/catalog-item-icon.tsx index a47ccfbcb4d..7a7f3aea9d7 100644 --- a/frontend/public/components/catalog/catalog-item-icon.tsx +++ b/frontend/public/components/catalog/catalog-item-icon.tsx @@ -77,6 +77,7 @@ import * as pythonImg from '../../imgs/logos/python.svg'; import * as quarkusImg from '../../imgs/logos/quarkus.svg'; import * as rabbitmqImg from '../../imgs/logos/rabbitmq.svg'; import * as railsImg from '../../imgs/logos/rails.svg'; +import * as reactImg from '../../imgs/logos/react.svg'; import * as redisImg from '../../imgs/logos/redis.svg'; import * as rhIntegrationImg from '../../imgs/logos/rh-integration.svg'; import * as rhSpringBoot from '../../imgs/logos/rh-spring-boot.svg'; @@ -174,6 +175,7 @@ const logos = new Map() .set('icon-quarkus', quarkusImg) .set('icon-rabbitmq', rabbitmqImg) .set('icon-rails', railsImg) + .set('icon-react', reactImg) .set('icon-redis', redisImg) .set('icon-rh-integration', rhIntegrationImg) .set('icon-rh-spring-boot', rhSpringBoot) diff --git a/frontend/public/components/cluster-settings/cluster-settings.tsx b/frontend/public/components/cluster-settings/cluster-settings.tsx index 1b7aa1ee4b0..3d627241db4 100644 --- a/frontend/public/components/cluster-settings/cluster-settings.tsx +++ b/frontend/public/components/cluster-settings/cluster-settings.tsx @@ -101,6 +101,7 @@ import { FirehoseResource, getDocumentationURL, HorizontalNav, + isManaged, PageHeading, ReleaseNotesLink, ResourceLink, @@ -401,7 +402,7 @@ export const CurrentVersion: React.FC = ({ cv }) => { {lastVersion} - + ) : ( <>{t('public~None')} @@ -472,9 +473,11 @@ const ChannelHeader: React.FC<{}> = () => { 'public~Channels help to control the pace of updates and recommend the appropriate release versions. Update channels are tied to a minor version of OpenShift Container Platform, for example 4.5.', )} - - - + {!isManaged() && ( + + + + )} @@ -749,9 +752,8 @@ export const UpdatesGraph: React.FC = ({ cv }) => { const availableUpdates = getSortedAvailableUpdates(cv); const lastVersion = getLastCompletedUpdate(cv); const newestVersion = availableUpdates[0]?.version; - const minorVersionIsNewer = newestVersion - ? isMinorVersionNewer(lastVersion, newestVersion) - : false; + const minorVersionIsNewer = + lastVersion && newestVersion ? isMinorVersionNewer(lastVersion, newestVersion) : false; const secondNewestVersion = availableUpdates[1]?.version; const currentChannel = cv.spec.channel; const currentPrefix = splitClusterVersionChannel(currentChannel)?.prefix; diff --git a/frontend/public/components/dashboard/dashboards-page/cluster-dashboard/getting-started/__tests__/cluster-setup-alert-receiver-link.spec.ts b/frontend/public/components/dashboard/dashboards-page/cluster-dashboard/getting-started/__tests__/cluster-setup-alert-receiver-link.spec.ts index a84815a3b76..ba76f2012fc 100644 --- a/frontend/public/components/dashboard/dashboards-page/cluster-dashboard/getting-started/__tests__/cluster-setup-alert-receiver-link.spec.ts +++ b/frontend/public/components/dashboard/dashboards-page/cluster-dashboard/getting-started/__tests__/cluster-setup-alert-receiver-link.spec.ts @@ -51,11 +51,11 @@ const defaultClusterAlertManagerConfigYAML = ` "receiver": "Default" "repeat_interval": "12h" "routes": - - "match": - "alertname": "Watchdog" + - "matchers": + - "alertname = Watchdog" "receiver": "Watchdog" - - "match": - "severity": "critical" + - "matchers": + - "severity = critical" "receiver": "Critical" `; @@ -95,12 +95,12 @@ route: receiver: Default repeat_interval: 12h routes: - - match: - alertname: Watchdog + - matchers: + - "alertname = Watchdog" receiver: Watchdog - receiver: Critical - match: - severity: critical + matchers: + - "severity = critical" `; describe('useAlertReceiverLink', () => { diff --git a/frontend/public/components/deployment-config.tsx b/frontend/public/components/deployment-config.tsx index 3da28acae86..9f03bf51f85 100644 --- a/frontend/public/components/deployment-config.tsx +++ b/frontend/public/components/deployment-config.tsx @@ -47,6 +47,7 @@ import { ExternalLink, getDocumentationURL, documentationURLs, + isManaged, } from './utils'; import { ReplicationControllersPage } from './replication-controller'; import { WorkloadTableRow, WorkloadTableHeader } from './workload-table'; @@ -232,11 +233,13 @@ export const DeploymentConfigDeprecationAlert: React.FC = () => { 'public~DeploymentConfigs will continue to be supported for security and critical fixes, but you should migrate to Deployments wherever it is possible.', )}

- + {!isManaged() && ( + + )} ); }; diff --git a/frontend/public/components/edit-yaml.jsx b/frontend/public/components/edit-yaml.jsx index 47e1de8a23d..f724e525343 100644 --- a/frontend/public/components/edit-yaml.jsx +++ b/frontend/public/components/edit-yaml.jsx @@ -199,7 +199,7 @@ const EditYAMLInner = (props) => { const notAll = !resp.status.allowed; setNotAllowed(notAll); if (monacoRef.current) { - monacoRef.current.editor.updateOptions({ readOnly: notAll }); + monacoRef.current.editor?.updateOptions({ readOnly: notAll }); } }) .catch((e) => { @@ -225,7 +225,7 @@ const EditYAMLInner = (props) => { yaml = allowMultiple ? appendYAMLString(obj) : obj; } else { try { - yaml = safeDump(obj); + yaml = safeDump(obj, { lineWidth: -1 }); checkEditAccess(obj); } catch (e) { yaml = t('public~Error getting YAML: {{e}}', { e }); @@ -387,6 +387,10 @@ const EditYAMLInner = (props) => { const validate = React.useCallback( (obj) => { + if (!obj) { + return t('public~No YAML content found.'); + } + if (!obj.apiVersion) { return t('public~No "apiVersion" field found in YAML.'); } @@ -638,7 +642,7 @@ const EditYAMLInner = (props) => { 'co-file-dropzone--drop-over': isOver, }); - monacoRef.current?.editor.updateOptions({ hover: showTooltips }); + monacoRef.current?.editor?.updateOptions({ hover: showTooltips }); if (displayResults) { return ( diff --git a/frontend/public/components/events.jsx b/frontend/public/components/events.jsx index 9d7ef02b760..90b5705058b 100644 --- a/frontend/public/components/events.jsx +++ b/frontend/public/components/events.jsx @@ -390,27 +390,33 @@ const EventStream = (props) => { }; React.useEffect(() => { + // If the namespace has changed, create a new WebSocket with the new namespace if (!props.mock) { wsInit(props.namespace); - } + // Reset the messages and events + setSortedMessages([]); + setFilteredEvents([]); - return () => { - ws && ws.destroy(); - }; + return () => { + ws && ws.destroy(); + }; + } // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [props.namespace]); - const [prevProps, setPrevProps] = React.useState(props); + const prevSortedMessages = React.useRef([]); + + const toggleStream = () => { + setActive((prev) => !prev); + prevSortedMessages.current = sortedMessages; + }; React.useEffect(() => { - // If the namespace has changed, created a new WebSocket with the new namespace - if (prevProps.namespace !== props.namespace) { - ws && ws.destroy(); - wsInit(props.namespace); - } - setPrevProps(props); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [props.namespace, prevProps?.namespace]); + // If the filter has changed, update the filteredEvents + setFilteredEvents( + EventStream.filterEvents(active ? sortedMessages : prevSortedMessages.current, props), + ); + }, [active, sortedMessages, props, prevSortedMessages]); // Messages can come in extremely fast when the buffer flushes. // Instead of calling setState() on every single message, let onmessage() @@ -419,25 +425,14 @@ const EventStream = (props) => { const sorted = sortEvents(messages); sorted.splice(maxMessages); setSortedMessages(sorted); - setFilteredEvents(EventStream.filterEvents(sorted, props)); + setFilteredEvents( + EventStream.filterEvents(active ? sorted : prevSortedMessages.current, props), + ); - // Shrink this.messages back to maxMessages messages, to stop it growing indefinitely + // Shrink messages back to maxMessages messages, to stop it growing indefinitely messages = _.keyBy(sorted, 'metadata.uid'); }; - const toggleStream = () => { - setActive((prev) => !prev); - }; - - React.useEffect(() => { - if (active) { - ws && ws.unpause(); - } else { - ws && ws.pause(); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [active]); - const count = filteredEvents.length; const allCount = sortedMessages.length; const noEvents = allCount === 0 && ws && ws.bufferSize() === 0; @@ -557,31 +552,6 @@ EventStream.filterEvents = (messages, { kind, type, filter, textFilter }) => { return _.filter(messages, f); }; -EventStream.getDerivedStateFromProps = (nextProps, prevState) => { - const { filter, kind, type, textFilter, loading } = prevState; - - if ( - _.isEqual(filter, nextProps.filter) && - kind === nextProps.kind && - type === nextProps.type && - textFilter === nextProps.textFilter - ) { - return {}; - } - - return { - active: !nextProps.mock, - loading: !nextProps.mock && loading, - // update the filteredEvents - filteredEvents: EventStream.filterEvents(prevState.sortedMessages, nextProps), - // we need these for bookkeeping because getDerivedStateFromProps doesn't get prevProps - textFilter: nextProps.textFilter, - kind: nextProps.kind, - type: nextProps.type, - filter: nextProps.filter, - }; -}; - export const ResourceEventStream_ = ({ obj: { kind, diff --git a/frontend/public/components/instantiate-template.tsx b/frontend/public/components/instantiate-template.tsx index e12dcaf096f..20b1af1cee3 100644 --- a/frontend/public/components/instantiate-template.tsx +++ b/frontend/public/components/instantiate-template.tsx @@ -136,15 +136,19 @@ const TemplateForm_: React.FC = (props) => { const [parameters, setParameters] = React.useState([]); const [inProgress, setInProgress] = React.useState(false); const [error, setError] = React.useState(''); + const isInitialLoad = React.useRef(true); const { t } = useTranslation(); React.useEffect(() => { - const object = (obj.data.parameters || []).reduce((acc, { name, value }) => { - acc[name] = value; - return acc; - }, {}); - setParameters(object); + if (isInitialLoad.current && obj.loaded) { + const object = (obj.data.parameters || []).reduce((acc, { name, value }) => { + acc[name] = value; + return acc; + }, {}); + setParameters(object); + isInitialLoad.current = false; + } }, [obj]); const onParameterChanged: React.ReactEventHandler = (event) => { diff --git a/frontend/public/components/masthead.jsx b/frontend/public/components/masthead.jsx index 08c4d2116cb..4fd2f0acd56 100644 --- a/frontend/public/components/masthead.jsx +++ b/frontend/public/components/masthead.jsx @@ -81,7 +81,7 @@ export const Masthead = React.memo(({ isMastheadStacked, isNavOpen, onNavToggle - + diff --git a/frontend/public/components/modals/cluster-channel-modal.tsx b/frontend/public/components/modals/cluster-channel-modal.tsx index 77a40968665..b6283f5a414 100644 --- a/frontend/public/components/modals/cluster-channel-modal.tsx +++ b/frontend/public/components/modals/cluster-channel-modal.tsx @@ -6,7 +6,7 @@ import * as semver from 'semver'; import { ChannelDocLink } from '../cluster-settings/cluster-settings'; import { ClusterVersionModel } from '../../models'; -import { Dropdown, HandlePromiseProps, withHandlePromise } from '../utils'; +import { Dropdown, HandlePromiseProps, isManaged, withHandlePromise } from '../utils'; import { createModalLauncher, ModalBody, @@ -53,9 +53,11 @@ const ClusterChannelModal = withHandlePromise((props: ClusterChannelModalProps) 'public~Input a channel that reflects the desired version. To verify if the version exists in a channel, save and check the update status. Critical security updates will be delivered to any vulnerable channels.', )} - - - + {!isManaged() && ( + + + + )}
diff --git a/frontend/public/components/modals/cluster-update-modal.tsx b/frontend/public/components/modals/cluster-update-modal.tsx index 04b2e1495d9..b196ccac93f 100644 --- a/frontend/public/components/modals/cluster-update-modal.tsx +++ b/frontend/public/components/modals/cluster-update-modal.tsx @@ -11,7 +11,13 @@ import { import { DropdownWithSwitch } from '@console/shared/src/components/dropdown'; import { ClusterVersionModel, MachineConfigPoolModel, NodeModel } from '../../models'; -import { FieldLevelHelp, HandlePromiseProps, LinkifyExternal, withHandlePromise } from '../utils'; +import { + FieldLevelHelp, + HandlePromiseProps, + LinkifyExternal, + isManaged, + withHandlePromise, +} from '../utils'; import { ClusterVersionKind, getConditionUpgradeableFalse, @@ -118,20 +124,18 @@ const ClusterUpdateModal = withHandlePromise((props: ClusterUpdateModalProps) => // Clear any previous error message. setError(''); - let MCPsToResumePromises; let MCPsToPausePromises; + let MCPsToResumePromises; if (upgradeType === upgradeTypes.Full) { - MCPsToResumePromises = getMCPsToPausePromises(pausedMCPs, false); MCPsToPausePromises = []; + MCPsToResumePromises = getMCPsToPausePromises(pausedMCPs, false); } else { - const MCPsToResume = pausedMCPs.filter((mcp) => - machineConfigPoolsToPause.find((m) => m !== mcp.metadata.name), - ); const MCPsToPause = pauseableMCPs.filter((mcp) => machineConfigPoolsToPause.find((m) => m === mcp.metadata.name), ); - MCPsToResumePromises = getMCPsToPausePromises(MCPsToResume, false); + const MCPsToResume = pauseableMCPs.filter((mcp) => !MCPsToPause.includes(mcp)); MCPsToPausePromises = getMCPsToPausePromises(MCPsToPause, true); + MCPsToResumePromises = getMCPsToPausePromises(MCPsToResume, false); } const patch = [ { @@ -223,7 +227,6 @@ const ClusterUpdateModal = withHandlePromise((props: ClusterUpdateModalProps) => {desiredNotRecommendedUpdate && desiredNotRecommendedUpdateConditions?.message && ( title={t('public~You must resume updates within 60 days to avoid failures.')} className="pf-u-mb-md" > - {t('public~Learn more')} + {!isManaged() && ( + {t('public~Learn more')} + )} ) diff --git a/frontend/public/components/modals/configure-cluster-upstream-modal.tsx b/frontend/public/components/modals/configure-cluster-upstream-modal.tsx index f0fc4201e9f..0e53f299404 100644 --- a/frontend/public/components/modals/configure-cluster-upstream-modal.tsx +++ b/frontend/public/components/modals/configure-cluster-upstream-modal.tsx @@ -14,6 +14,8 @@ import { ExternalLink, getDocumentationURL, HandlePromiseProps, + isManaged, + isUpstream, withHandlePromise, } from '../utils'; import { useTranslation } from 'react-i18next'; @@ -60,12 +62,14 @@ export const ConfigureClusterUpstreamModal = withHandlePromise( 'public~Select a configuration to receive updates. Updates can be configured to receive information from Red Hat or a custom update service.', )}

-

- -

+ {!isManaged() && !isUpstream() && ( +

+ +

+ )}
diff --git a/frontend/public/components/modals/create-namespace-modal.jsx b/frontend/public/components/modals/create-namespace-modal.jsx index c283e2996a1..184eaa7c7bf 100644 --- a/frontend/public/components/modals/create-namespace-modal.jsx +++ b/frontend/public/components/modals/create-namespace-modal.jsx @@ -13,7 +13,7 @@ import { FLAGS } from '@console/shared'; import { k8sCreate, referenceFor } from '../../module/k8s'; import { NamespaceModel, ProjectRequestModel, NetworkPolicyModel } from '../../models'; import { createModalLauncher, ModalTitle, ModalBody, ModalSubmitFooter } from '../factory/modal'; -import { Dropdown, history, resourceObjPath, SelectorInput } from '../utils'; +import { Dropdown, history, isManaged, resourceObjPath, SelectorInput } from '../utils'; import { setFlag } from '../../actions/features'; const allow = 'allow'; @@ -159,11 +159,13 @@ const CreateNamespaceModalWithTranslation_ = (props) => { 'public~An OpenShift project is an alternative representation of a Kubernetes namespace.', )}

-

- - {t('public~Learn more about working with projects')} - -

+ {!isManaged() && ( +

+ + {t('public~Learn more about working with projects')} + +

+ )} ) : null} diff --git a/frontend/public/components/modals/expand-pvc-modal.tsx b/frontend/public/components/modals/expand-pvc-modal.tsx index 289aa04aa6f..69337c3213d 100644 --- a/frontend/public/components/modals/expand-pvc-modal.tsx +++ b/frontend/public/components/modals/expand-pvc-modal.tsx @@ -9,13 +9,16 @@ import { validate, withHandlePromise, HandlePromiseProps, + convertToBaseValue, + humanizeBinaryBytesWithoutB, } from '../utils'; import { k8sPatch, referenceFor, K8sKind, K8sResourceKind } from '../../module/k8s/'; import { getRequestedPVCSize } from '@console/shared'; // Modal for expanding persistent volume claims const ExpandPVCModal = withHandlePromise((props: ExpandPVCModalProps) => { - const defaultSize = validate.split(getRequestedPVCSize(props.resource)); + const baseValue = convertToBaseValue(getRequestedPVCSize(props.resource)); + const defaultSize = validate.split(humanizeBinaryBytesWithoutB(baseValue).string); const [requestSizeValue, setRequestSizeValue] = React.useState(defaultSize[0] || ''); const [requestSizeUnit, setRequestSizeUnit] = React.useState(defaultSize[1] || 'Gi'); const [errorMessage, setErrorMessage] = React.useState(); diff --git a/frontend/public/components/monitoring/alertmanager/alertmanager-config.tsx b/frontend/public/components/monitoring/alertmanager/alertmanager-config.tsx index e0a0dbfbfa9..dc69ff0c0b3 100644 --- a/frontend/public/components/monitoring/alertmanager/alertmanager-config.tsx +++ b/frontend/public/components/monitoring/alertmanager/alertmanager-config.tsx @@ -51,6 +51,7 @@ const AlertRouting = ({ secret, config }: AlertRoutingProps) => { className="co-alert-manager-config__edit-alert-routing-btn" onClick={() => createAlertRoutingModal({ config, secret })} variant="secondary" + data-test="edit-alert-routing-btn" > {t('public~Edit')} @@ -102,8 +103,8 @@ const getIntegrationTypes = (receiver: AlertmanagerReceiver): string[] => { }; /** - * Recursive function which transverses routes and sub-routes to get labels for each receiver. - * Each entry is a set of labels used to route alerts to a receiver + * Recursive function which transverses routes and sub-routes to get labels and/or matchers for each receiver. + * Each entry is a set of labels and/or matchers used to route alerts to a receiver * * Ex: returns * [{ @@ -111,7 +112,8 @@ const getIntegrationTypes = (receiver: AlertmanagerReceiver): string[] => { * "labels": { * "service": "database", * "owner": "team-Y" - * } + * }, + * "matchers": ["severity = critical"] * }, * { * "receiver": "team-Y-pager", @@ -121,14 +123,19 @@ const getIntegrationTypes = (receiver: AlertmanagerReceiver): string[] => { * } * }] }*/ -const getRoutingLabelsByReceivers = (routes, parentLabels): RoutingLabelsByReceivers[] => { +const getRoutingLabelsByReceivers = ( + routes: AlertmanagerRoute[], + parentLabels: { [key: string]: string } = {}, + parentMatchers: string[] = [], +): RoutingLabelsByReceivers[] => { let results: RoutingLabelsByReceivers[] = []; let labels = {}; for (const obj of routes) { labels = _.merge({}, parentLabels, obj.match, obj.match_re); - results.push({ receiver: obj.receiver, labels }); + const matchers = [...parentMatchers, ...(obj.matchers ?? [])]; + results.push({ receiver: obj.receiver, labels, matchers }); if (obj.routes) { - results = results.concat(getRoutingLabelsByReceivers(obj.routes, labels)); + results = results.concat(getRoutingLabelsByReceivers(obj.routes, labels, matchers)); } } return results; @@ -191,28 +198,18 @@ export const numberOfIncompleteReceivers = (config: AlertmanagerConfig): number : numIncompleteReceivers; }; -// Puts sets of key=value pairs into single comma delimited label -const RoutingLabel: React.FC = ({ labels }) => { - let count = 0; - const { t } = useTranslation(); - const list = _.map(labels, (value, key) => { - count++; - return key === 'default' ? ( - {t('public~All (default receiver)')} - ) : ( - - {key} - = - {value} - {count < _.size(labels) && <>, } - - ); - }); - return ( -
- {list} -
- ); +const RoutingLabels: React.FC = ({ data }) => { + const { labels, matchers } = data; + const lbls = _.map(labels || {}, (value, key) => `${key}=${value}`); + const values = [...lbls, ...(matchers ?? [])]; + + return values.length > 0 ? ( + + {values.map((value, i) => ( + {value} + ))} + + ) : null; }; const deleteReceiver = ( @@ -310,10 +307,11 @@ const ReceiverTableRow: React.FC - {isDefaultReceiver && } - {_.map(receiverRoutingLabels, (rte, i) => { - return !_.isEmpty(rte.labels) ? : null; - })} + {isDefaultReceiver + ? t('public~All (default receiver)') + : _.map(receiverRoutingLabels, (rte, i) => { + return ; + })} @@ -336,7 +334,7 @@ const ReceiversTable: React.FC = (props) => { const { t } = useTranslation(); const routingLabelsByReceivers = React.useMemo( - () => (_.isEmpty(routes) ? [] : getRoutingLabelsByReceivers(routes, {})), + () => (_.isEmpty(routes) ? [] : getRoutingLabelsByReceivers(routes)), [routes], ); @@ -552,11 +550,13 @@ export type AlertmanagerRoute = { match?: labels[]; match_re?: labels[]; routes?: AlertmanagerRoute[]; + matchers?: string[]; }; type RoutingLabelsByReceivers = { receiver: string; labels: { [key: string]: string }; + matchers: string[]; }; type WebhookConfig = { @@ -581,6 +581,6 @@ export type AlertmanagerConfig = { receivers: AlertmanagerReceiver[]; }; -type RoutingLabelProps = { - labels: { [key: string]: string }; +type RoutingLabelsProps = { + data: RoutingLabelsByReceivers; }; diff --git a/frontend/public/components/monitoring/alertmanager/alertmanager-page.tsx b/frontend/public/components/monitoring/alertmanager/alertmanager-page.tsx index 8890d38654a..d358cdbcbf3 100644 --- a/frontend/public/components/monitoring/alertmanager/alertmanager-page.tsx +++ b/frontend/public/components/monitoring/alertmanager/alertmanager-page.tsx @@ -45,14 +45,18 @@ const AlertmanagerPage: React.FC<{ match: { url: string } }> = ({ match }) => { 'co-m-horizontal-nav-item--active': url === configPath, })} > - {t('public~Details')} + + {t('public~Details')} +
  • - {t('public~YAML')} + + {t('public~YAML')} +
  • diff --git a/frontend/public/components/monitoring/receiver-forms/alert-manager-receiver-forms.tsx b/frontend/public/components/monitoring/receiver-forms/alert-manager-receiver-forms.tsx index f4e9e25b68c..e3b4df40f19 100644 --- a/frontend/public/components/monitoring/receiver-forms/alert-manager-receiver-forms.tsx +++ b/frontend/public/components/monitoring/receiver-forms/alert-manager-receiver-forms.tsx @@ -33,9 +33,8 @@ import * as SlackForm from './slack-receiver-form'; import { coFetchJSON } from '../../../co-fetch'; /** - * Converts routes of a specific Receiver: + * Converts deprecated route match and match_re: * { - * receiver: "MyReceiver", * match: { * severity: "warning", * cluster: "myCluster" @@ -44,44 +43,19 @@ import { coFetchJSON } from '../../../co-fetch'; * service: "$foobar" * } }; - * ...to array of labels for Routing Labels Editor component - * [ - * { - * "name": "severity", - * "value": "warning", - * "isRegex": false - * }, - * { - * "name": "cluster", - * "value": "myCluster", - * "isRegex": false - * }, - * { - * "name": "service", - * "value": "$foobar", - * "isRegex": true - * } - * ] + * ...to array of matchers for Routing Labels Editor component + * Ex: ["severity = warning", "cluster = myCluster", "service =~ $foobar"] */ -const convertReceiverRoutesToEditorLabels = ( - receiver: AlertmanagerReceiver, - routes: AlertmanagerRoute[], -): RouteEditorLabel[] => { - if (!receiver) { - return []; - } - - const routesOfReceiver = _.find( - routes, - (aRoute: AlertmanagerRoute) => aRoute.receiver === receiver.name, - ); - const matches = _.map(routesOfReceiver?.match || {}, (v, k) => { - return { name: k, value: v, isRegex: false }; +const convertDeprecatedReceiverRoutesMatchesToMatchers = ( + receiverRoutes: AlertmanagerRoute, +): string[] => { + const matches = _.map(receiverRoutes?.match || {}, (v, k) => { + return `${k} = ${v}`; }); - const regexMatches = _.map(routesOfReceiver?.match_re || {}, (v, k) => { - return { name: k, value: v, isRegex: true }; + const regexMatches = _.map(receiverRoutes?.match_re || {}, (v, k) => { + return `${k} =~ ${v}`; }); - return _.concat([], matches, regexMatches); + return [...matches, ...regexMatches]; }; /** @@ -89,27 +63,14 @@ const convertReceiverRoutesToEditorLabels = ( * Ex: * { * receiver: myNewReceiver, - * match: { - * "severity": "warning", - * "cluster": "myCluster" - * } - * match_re { - * "service": "^(foo1|foo2|baz)$", - * } + * matchers: ["severity = warning"] * } */ -const createRoute = ( - receiver: AlertmanagerReceiver, - routeLabels: RouteEditorLabel[], -): AlertmanagerRoute => { - return _.reduce( - routeLabels, - (acc, label) => { - _.set(acc, [label.isRegex ? 'match_re' : 'match', label.name], label.value); - return acc; - }, - { receiver: receiver.name }, - ); +const createRoute = (receiver: AlertmanagerReceiver, routeLabels: string[]): AlertmanagerRoute => { + return { + receiver: receiver.name, + matchers: routeLabels, + }; }; /** @@ -172,11 +133,14 @@ const getRouteLabelsForEditor = ( isDefaultReceiver: boolean, receiverToEdit: AlertmanagerReceiver, allRoutes: AlertmanagerRoute[], -): RouteEditorLabel[] => { - const routeLabels = convertReceiverRoutesToEditorLabels(receiverToEdit, allRoutes); - return !isDefaultReceiver && _.isEmpty(routeLabels) - ? [{ name: '', value: '', isRegex: false }] - : routeLabels; +): string[] => { + const receiverRoutes = _.find( + allRoutes, + (aRoute: AlertmanagerRoute) => aRoute.receiver === receiverToEdit?.name, + ); + const convertedRouteLabels = convertDeprecatedReceiverRoutesMatchesToMatchers(receiverRoutes); + const routeLabels = [...(convertedRouteLabels ?? []), ...(receiverRoutes?.matchers ?? [])]; + return !isDefaultReceiver && _.isEmpty(routeLabels) ? [''] : routeLabels; }; const AlertMsg: React.FC = ({ type }) => { @@ -291,7 +255,7 @@ const ReceiverBaseForm: React.FC = ({ const isDefaultReceiver = defaultReceiver ? _.isEmpty(config?.receivers?.filter((receiver) => receiver.name === defaultReceiver)) || defaultReceiver === editReceiverNamed - : true; // defaultReceiver (the name stored in config.routes.receiver) is not defined, so this should be the default receiver + : true; // defaultReceiver (the name stored in config.route.receiver) is not defined, so this should be the default receiver INITIAL_STATE.routeLabels = getRouteLabelsForEditor( isDefaultReceiver, @@ -663,12 +627,6 @@ type ReceiverBaseFormProps = { alertmanagerGlobals?: { [key: string]: any }; }; -export type RouteEditorLabel = { - name: string; - value: string; - isRegex: boolean; -}; - type FormAction = { type: 'setFormValues'; payload: { diff --git a/frontend/public/components/monitoring/receiver-forms/routing-labels-editor.tsx b/frontend/public/components/monitoring/receiver-forms/routing-labels-editor.tsx index f6a555748f5..0cbdaf69422 100644 --- a/frontend/public/components/monitoring/receiver-forms/routing-labels-editor.tsx +++ b/frontend/public/components/monitoring/receiver-forms/routing-labels-editor.tsx @@ -1,59 +1,37 @@ import * as _ from 'lodash-es'; import * as React from 'react'; -import * as classNames from 'classnames'; import { Trans, useTranslation } from 'react-i18next'; -import { MinusCircleIcon, PlusCircleIcon, InfoCircleIcon } from '@patternfly/react-icons'; +import { MinusCircleIcon } from '@patternfly/react-icons/dist/esm/icons/minus-circle-icon'; +import { PlusCircleIcon } from '@patternfly/react-icons/dist/esm/icons/plus-circle-icon'; import { Button, Tooltip } from '@patternfly/react-core'; import { ExternalLink, SectionHeading } from '../../utils'; -import { RouteEditorLabel } from './alert-manager-receiver-forms'; const DEFAULT_RECEIVER_LABEL = 'All (default receiver)'; -const labelNamePattern = /^[a-zA-Z_][a-zA-Z0-9_]*$/; -export const getRouteLabelFieldErrors = (labels: RouteEditorLabel[]) => { - const routeLabelFieldErrors = {}; - labels.forEach((label, i) => { - if (label.name && !label.name.match(labelNamePattern)) { - routeLabelFieldErrors[`${i}_name`] = true; - } - }); - return routeLabelFieldErrors; -}; - -const hasDuplicateNames = (labels: RouteEditorLabel[]): boolean => { - const names = _.map(labels, (label) => label.name); - return names.length !== _.uniq(names).length; +const hasDuplicateNames = (labels: string[]): boolean => { + return labels.length !== _.uniq(labels).length; }; export const RoutingLabelEditor = ({ formValues, dispatchFormChange, isDefaultReceiver }) => { - const setRouteLabel = (path: string, v: any): void => { + const setRouteLabel = (path: number, v: string): void => { const labels = _.clone(formValues.routeLabels); - _.set(labels, path.split(', '), v); + labels.splice(path, 1, v); dispatchFormChange({ type: 'setFormValues', payload: { routeLabels: labels, - routeLabelFieldErrors: getRouteLabelFieldErrors(labels), routeLabelDuplicateNamesError: hasDuplicateNames(labels), }, }); }; - const onRoutingLabelChange = (path: string): ((e) => void) => { + const onRoutingLabelChange = (path: number): ((e) => void) => { return (e) => setRouteLabel(path, e.target.value); }; - const onRoutingLabelRegexChange = (e, i: number): void => { - setRouteLabel(`${i}, isRegex`, e.target.checked); - }; - const addRoutingLabel = (): void => { - setRouteLabel(`${formValues.routeLabels.length}`, { - name: '', - value: '', - isRegex: false, - }); + setRouteLabel(formValues.routeLabels.length, ''); }; const removeRoutingLabel = (i: number): void => { @@ -70,129 +48,52 @@ export const RoutingLabelEditor = ({ formValues, dispatchFormChange, isDefaultRe const { t } = useTranslation(); - const InvalidLabelName = () => ( - - {t('public~Invalid name.')} - - {t( - "public~Label name must not begin with a digit and contain only alphanumeric characters or '_'.", - )} -

    - } - > - -
    -
    - ); - return (
    -

    +

    - Firing alerts with labels that match all of these selectors will be sent to this receiver. - Label values can be matched exactly or with a{' '} + Firing alerts with labels that match all of these{' '} - . + href="https://prometheus.io/docs/alerting/latest/configuration/#matcher" + text={t('public~matchers')} + />{' '} + will be sent to this receiver.

    -
    -
    {t('public~Name')}
    -
    {t('public~Value')}
    -
    {isDefaultReceiver && (
    -
    -
    -
    - -
    -
    -
    -
    - -
    -
    +
    +
    )} {_.map(formValues.routeLabels, (routeLabel, i: number) => { - const hasLabelNameError = formValues?.routeLabelFieldErrors?.[`${i}_name`]; return (
    -
    -
    -
    - - {hasLabelNameError && } -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    +
    +
    -
    +
    - {alternateServicesList.length > 0 && ( + {alternateBackendsList.length > 0 && ( <>
    @@ -400,11 +404,11 @@ class CreateRouteWithTranslation extends React.Component<

    - {alternateServicesList} + {alternateBackendsList} )} - {alternateServicesList.length < MAX_ALT_SERVICE_TARGET && - alternateServicesList.length + 1 < _.keys(serviceOptions).length && + {alternateBackendsList.length < MAX_ALT_SERVICE_TARGET && + alternateBackendsList.length + 1 < _.keys(serviceOptions).length && service && (