diff --git a/.cfignore b/.cfignore index 92be10a235..f58e7a9d67 100644 --- a/.cfignore +++ b/.cfignore @@ -22,3 +22,4 @@ docs/ build/dev_config.json e2e-reports/ website/ +.helm-cache/ diff --git a/.gitignore b/.gitignore index 0e2542f532..eb452d4518 100644 --- a/.gitignore +++ b/.gitignore @@ -96,6 +96,7 @@ src/jetstream/config.properties src/jetstream/db/dbconf.yml src/jetstream/plugins/monocular/chart-repo/chartrepo src/jetstream/plugins/analysis/container/analyzers +src/jetstream/.helm-cache # Customisations - these can be removed in the future # Left in for now to prevent these files being checked-in, if they are still present diff --git a/CHANGELOG.md b/CHANGELOG.md index 23026997c3..a2916e1e66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,40 @@ # Change Log +## 4.1.0 + +[Full Changelog](https://github.com/SUSE/stratos/compare/4.0.1...4.1.0) + +This release contains a number of fixes and improvements: + +**Improvements:** + +- Helm: Add support for JSON Schema when inputting values [\#447](https://github.com/SUSE/stratos/issues/447) +- Helm: Improve chart values YAML editor [\#481](https://github.com/SUSE/stratos/issues/481) +- Helm: Support helm upgrade of a workload [\#446](https://github.com/SUSE/stratos/issues/446) +- Helm: Allow users to view and use repositories from Helm Hub [\#445](https://github.com/SUSE/stratos/issues/445) +- Add support for API Keys [\#4504](https://github.com/cloudfoundry/stratos/issues/4504) +- Workload List: Show Chart Version instead of Release Version [\#473](https://github.com/SUSE/stratos/issues/473) +- Helm: Allow users to view revision history for a Helm Release [\#460](https://github.com/SUSE/stratos/issues/460) +- Kubernetes Terminal: It can take a long time to get to the prompt with large helm repositories registered [\#452](https://github.com/SUSE/stratos/issues/452) +- Show CaaSP Node version and whether updates are pending on the summary view [\#450](https://github.com/SUSE/stratos/issues/450) +- Update SUSE login screen to latest EOS design [\#448](https://github.com/SUSE/stratos/issues/448) +- Toggle live updates in workloads view [\#442](https://github.com/SUSE/stratos/issues/442) +- Container List in Pods List Improvements [\#360](https://github.com/SUSE/stratos/issues/360) +- Register Endpoint: Reduce size of cards [\#4568](https://github.com/cloudfoundry/stratos/issues/4568) +- Metrics view: Add "The last day" to time range selector [\#4516](https://github.com/cloudfoundry/stratos/issues/4516) + + +**Fixes:** + +- Helm Install: Install button becomes enabled before namespace supplied [\#471](https://github.com/SUSE/stratos/issues/471) +- Helm Chart: Charts view incorrectly shows development versions as the latest versions [\#455](https://github.com/SUSE/stratos/issues/455) +- Sort endpoint table by type results in empty sort drop down in cf endpoints list [\#4565](https://github.com/cloudfoundry/stratos/issues/4565) +- Endpoint unregister clears user sort & filter selection [\#4563](https://github.com/cloudfoundry/stratos/issues/4563) +- Metrics: Metrics detail page can show two endpoint cards if URLs have trailing slash [\#4528](https://github.com/cloudfoundry/stratos/issues/4528) +- Deploy from Gitlab: Repos in groups do not work [\#4153](https://github.com/cloudfoundry/stratos/issues/4153) +- Deploy app from Github repo only shows branches from a-e in dropdown [\#3966](https://github.com/cloudfoundry/stratos/issues/3966) + + ## 4.0.1 [Full Changelog](https://github.com/SUSE/stratos/compare/4.0.0...4.0.1) diff --git a/angular.json b/angular.json index e2a013de65..31c5a47d56 100644 --- a/angular.json +++ b/angular.json @@ -28,7 +28,17 @@ "input": "custom-src/frontend/assets/custom", "output": "/core/assets/custom" }, - "src/frontend/packages/core/favicon.ico" + "src/frontend/packages/core/favicon.ico", + { + "glob": "**/*", + "input": "node_modules/ngx-monaco-editor/assets/monaco", + "output": "/core/assets/monaco" + }, + { + "glob": "**/*", + "input": "node_modules/@cfstratos/monaco-yaml/lib", + "output": "/core/assets/monaco/vs/language/yaml" + } ], "styles": [ "src/frontend/packages/core/src/styles.scss", diff --git a/deploy/Dockerfile.all-in-one b/deploy/Dockerfile.all-in-one index 9ff9b1a73a..f9dbdf8adf 100644 --- a/deploy/Dockerfile.all-in-one +++ b/deploy/Dockerfile.all-in-one @@ -16,65 +16,6 @@ RUN deploy/all-in-one/src-build.sh # Generate dev-certs RUN CERTS_PATH=/home/stratos/dev-certs ./generate_cert.sh -# --------------------------------------------------------------------------------------------------- -# Docker build for FDB Server component -FROM splatform/stratos-bk-build-base:leap15_1 as fdbserver-builder - -# Install FoundationDB Binaries -ARG FDB_VERSION=6.2.15 -ARG FDB_WEBSITE=https://www.foundationdb.org - -USER root -WORKDIR /home/stratos/tmp -WORKDIR /home/stratos -RUN pwd && ls -al -RUN curl $FDB_WEBSITE/downloads/$FDB_VERSION/linux/fdb_$FDB_VERSION.tar.gz -o fdb_$FDB_VERSION.tar.gz && \ - tar -xzf fdb_$FDB_VERSION.tar.gz --strip-components=1 && \ - rm fdb_$FDB_VERSION.tar.gz && \ - chmod u+x fdbbackup fdbcli fdbdr fdbmonitor fdbrestore fdbserver backup_agent dr_agent && \ - mv fdbbackup fdbcli fdbdr fdbmonitor fdbrestore fdbserver backup_agent dr_agent /usr/bin - -WORKDIR /var/fdb - -# Install FoundationDB Client Libraries - -ARG FDB_VERSION=6.2.15 -ARG FDB_ADDITIONAL_VERSIONS="5.1.7" -ARG FDB_WEBSITE=https://www.foundationdb.org - -COPY deploy/containers/monocular/fdb-server/download_multiversion_libraries.bash scripts/ - -# Set Up Runtime Scripts and Directories -COPY deploy/containers/monocular/fdb-server/fdb.bash scripts/ -COPY deploy/containers/monocular/fdb-server/create_server_environment.bash scripts/ -COPY deploy/containers/monocular/fdb-server/create_cluster_file.bash scripts/ -COPY deploy/containers/monocular/fdb-server/configure_db.bash scripts/ -RUN chmod u+x scripts/*.bash && \ - mkdir -p logs - -# --------------------------------------------------------------------------------------------------- -# Docker build for FDB Document Layer component -FROM splatform/stratos-bk-build-base:leap15_1 as fdbdoclayer-builder - -# Install FoundationDB Document Layer Binaries -ARG FDB_DOC_VERSION=1.6.3 -ARG FDB_WEBSITE=https://www.foundationdb.org - -WORKDIR /home/stratos -RUN curl $FDB_WEBSITE/downloads/$FDB_DOC_VERSION/linux/fdb-document-layer-$FDB_DOC_VERSION-Linux.tar.gz -o fdb-document-layer-$FDB_DOC_VERSION-Linux.tar.gz && \ - tar -xvf fdb-document-layer-$FDB_DOC_VERSION-Linux.tar.gz && \ - mv fdb-document-layer-$FDB_DOC_VERSION-Linux doclayer - -# --------------------------------------------------------------------------------------------------- -# Docker build for Chart Repo component -FROM splatform/stratos-bk-build-base:leap15_1 as chartrepo-builder - -COPY --chown=stratos:users src/jetstream/plugins/monocular/chart-repo /go/src/github.com/helm/monocular -WORKDIR /go/src/github.com/helm/monocular -ARG VERSION -RUN GO111MODULE=on GOPROXY=https://gocenter.io CGO_ENABLED=0 go build -a -installsuffix cgo -ldflags "-X main.version=$VERSION" . - - # --------------------------------------------------------------------------------------------------- # Final AIO Container # --------------------------------------------------------------------------------------------------- @@ -92,59 +33,15 @@ COPY --from=jetstream-builder /home/stratos/config.properties /srv/config.proper # User Invite templates COPY --from=jetstream-builder /home/stratos/src/jetstream/templates /srv/templates -# Pull in binaries from fdbserver-builder -COPY --from=fdbserver-builder /usr/bin /usr/bin -COPY --from=fdbserver-builder /var/fdb/scripts /var/fdb/scripts - -# Install FoundationDB Client Libraries directly here rather than in the fdbserver-builder, as the install destination depends on an external script. -ARG FDB_VERSION=6.2.15 -ARG FDB_ADDITIONAL_VERSIONS="5.1.7" -ARG FDB_WEBSITE=https://www.foundationdb.org -RUN mkdir -p /mnt/website -RUN curl $FDB_WEBSITE/downloads/$FDB_VERSION/linux/libfdb_c_$FDB_VERSION.so -o /usr/lib64/libfdb_c.so && \ - bash /var/fdb/scripts/download_multiversion_libraries.bash $FDB_WEBSITE $FDB_ADDITIONAL_VERSIONS && \ - rm -rf /mnt/website -VOLUME /var/fdb/data -RUN mkdir -p /var/fdb/logs - -# Pull in binaries from fdbdoclayer-builder -COPY --from=fdbdoclayer-builder /home/stratos/doclayer/bin/fdbdoc /usr/bin/fdbdoc -COPY --from=fdbdoclayer-builder /home/stratos/doclayer/lib/foundationdb/document/fdbmonitor /usr/bin/fdbmonitor -# Bring in doclayer startup script -COPY deploy/containers/monocular/fdb-doclayer/fdbdoc.bash /var/fdb/scripts -RUN chmod u+x /var/fdb/scripts/fdbdoc.bash -# Doclayer startup script dependency -# FoundationDB environment variables -ENV FDB_PORT 4500 -ENV FDB_CLUSTER_FILE /var/fdb/fdb.cluster -# Set to host, since all processes run in single container -ENV FDB_NETWORKING_MODE host -ENV FDB_COORDINATOR_PORT 4500 -ENV FDB_PROCESS_CLASS unset -ENV CLUSTER_ID docker:docker -ENV FDB_COORDINATOR localhost -ENV FDB_DOC_PORT 27016 -ENV FDB_LISTEN_IP 0.0.0.0 - -# Pull in binaries from chartrepo-builder -COPY --from=chartrepo-builder /go/src/github.com/helm/monocular/chartrepo /chartrepo - # Enable persistence features if canary build flag is set RUN if [ "x$CANARY_BUILD" != "x" ] ; then printf "\nFORCE_ENABLE_PERSISTENCE_FEATURES=true\n" >> /srv/config.properties ; fi # Enable tech preview features if canary build flag is set RUN if [ "x$CANARY_BUILD" != "x" ] ; then printf "\nENABLE_TECH_PREVIEW=true\n" >> /srv/config.properties ; fi -# Fix dig to resolve localhost to 127.0.0.1 -RUN mv /usr/bin/dig /usr/bin/digit -COPY deploy/all-in-one/dig.sh /usr/bin/dig -RUN chmod +x /usr/bin/dig - EXPOSE 443 # Need to be root to bind to port 443 USER root -COPY deploy/aio-entrypoint.sh . -RUN chmod +x ./aio-entrypoint.sh -ENTRYPOINT ["bash", "-c", "./aio-entrypoint.sh -u mongodb://localhost:${FDB_DOC_PORT}"] +ENTRYPOINT ["./jetstream"] diff --git a/deploy/aio-entrypoint.sh b/deploy/aio-entrypoint.sh deleted file mode 100644 index 939eea2a85..0000000000 --- a/deploy/aio-entrypoint.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -while getopts ":u:" opt; do - case ${opt} in - u ) - doclayer=$OPTARG - ;; - \? ) echo "Invalid option: $OPTARG" 1>&2 - ;; - : ) - echo "$OPTARG requires a value set to the URL of the document layer." 1>&2 - ;; - esac -done -shift $((OPTIND -1)) - -if [ -z "$doclayer" ]; -then - echo "--doclayer-url must be set." >&2 - exit 1 -fi - -/var/fdb/scripts/fdb.bash & -/var/fdb/scripts/fdbdoc.bash & -/chartrepo serve --doclayer-url=$doclayer & -./jetstream \ No newline at end of file diff --git a/deploy/all-in-one/config.all-in-one.properties b/deploy/all-in-one/config.all-in-one.properties index aab49027df..458d85e2e7 100644 --- a/deploy/all-in-one/config.all-in-one.properties +++ b/deploy/all-in-one/config.all-in-one.properties @@ -14,5 +14,4 @@ STRATOS_DEPLOYMENT_DOCKER_AIO=true SKIP_SSL_VALIDATION=true SQLITE_KEEP_DB=true TEMPLATE_DIR=./templates -FDB_URL=mongodb://localhost:27016 -SYNC_SERVER_URL=http://localhost:8080 \ No newline at end of file +HELM_CACHE_FOLDER=./helm-cache \ No newline at end of file diff --git a/deploy/all-in-one/dig.sh b/deploy/all-in-one/dig.sh deleted file mode 100644 index 82fb0a6fe6..0000000000 --- a/deploy/all-in-one/dig.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -HOST="${@: -1}" -if [ "$HOST" == "localhost" ]; then - echo "127.0.0.1" -else - /usr/bin/digit $@ -fi diff --git a/deploy/ci/console-dev-releases.yml b/deploy/ci/console-dev-releases.yml index 4696f6a39b..e52789e24f 100644 --- a/deploy/ci/console-dev-releases.yml +++ b/deploy/ci/console-dev-releases.yml @@ -13,8 +13,8 @@ resources: uri: git@github.com:((stratos-repository-organization))/((stratos-repository)) branch: ((stratos-repository-branch)) private_key: ((github-private-key)) - # Match any Version 3 release (alpha, beta, rc) other than the actual release - tag_filter: "3*-*" + # Match any Version 4 release (alpha, beta, rc) other than the actual release + tag_filter: "4*-*" - name: helm-repo type: git source: diff --git a/deploy/ci/console-nightly-releases.yml b/deploy/ci/console-nightly-releases.yml index 68751c0160..03d7a82c4e 100644 --- a/deploy/ci/console-nightly-releases.yml +++ b/deploy/ci/console-nightly-releases.yml @@ -106,25 +106,25 @@ jobs: dockerfile: stratos/deploy/Dockerfile.bk build: stratos/ target_name: prod-build - tag: stratos/deploy/ci/tasks/dev-releases/nightly-tag + tag: stratos/deploy/ci/tasks/build-images/nightly-tag build_args_file: image-tag/build-args - put: mariadb-image params: dockerfile: stratos/deploy/db/Dockerfile.mariadb build: stratos/deploy/db - tag: stratos/deploy/ci/tasks/dev-releases/nightly-tag + tag: stratos/deploy/ci/tasks/build-images/nightly-tag - do: - put: config-init-image params: dockerfile: stratos/deploy/Dockerfile.init build: stratos/ - tag: stratos/deploy/ci/tasks/dev-releases/nightly-tag + tag: stratos/deploy/ci/tasks/build-images/nightly-tag - put: ui-image params: dockerfile: stratos/deploy/Dockerfile.ui build: stratos/ target_name: prod-build - tag: stratos/deploy/ci/tasks/dev-releases/nightly-tag + tag: stratos/deploy/ci/tasks/build-images/nightly-tag prebuild_script: build/store-git-metadata.sh - name: create-chart plan: @@ -153,7 +153,7 @@ jobs: - put: nightly-gh-release params: name: stratos/deploy/ci/tasks/dev-releases/nightly-release-name - tag: stratos/deploy/ci/tasks/dev-releases/nightly-tag + tag: stratos/deploy/ci/tasks/build-images/nightly-tag body: stratos/deploy/ci/tasks/dev-releases/nightly-release-description globs: - helm-chart-tarball/*.tgz diff --git a/deploy/ci/suse-console-dev-releases.yml b/deploy/ci/suse-console-dev-releases.yml index be33e148f5..e05f578318 100644 --- a/deploy/ci/suse-console-dev-releases.yml +++ b/deploy/ci/suse-console-dev-releases.yml @@ -14,8 +14,8 @@ resources: uri: git@github.com:((stratos-repository-organization))/((stratos-repository)) branch: ((stratos-repository-branch)) private_key: ((github-private-key)) - # Match any Version 3 release (alpha, beta, rc) other than the actual release - tag_filter: "3*-*" + # Match any Version 4 release (alpha, beta, rc) other than the actual release + tag_filter: "4*-*" - name: helm-repo type: git source: @@ -47,24 +47,6 @@ resources: username: ((docker-username)) password: ((docker-password)) repository: ((docker-repository))/stratos-console -- name: fdbserver-image - type: docker-image - source: - username: ((docker-username)) - password: ((docker-password)) - repository: ((docker-repository))/stratos-fdbserver -- name: fdbdoclayer-image - type: docker-image - source: - username: ((docker-username)) - password: ((docker-password)) - repository: ((docker-repository))/stratos-fdbdoclayer -- name: chartsync-image - type: docker-image - source: - username: ((docker-username)) - password: ((docker-password)) - repository: ((docker-repository))/stratos-chartsync - name: kube-terminal-image type: docker-image source: @@ -159,13 +141,6 @@ jobs: tag: image-tag/v2-alpha-tag patch_base_reg: ((patch-base-reg)) patch_base_tag: ((patch-base-tag)) - - put: kube-terminal-image - params: - dockerfile: stratos/deploy/containers/kube-terminal/Dockerfile.kubeterminal - build: stratos/deploy/containers/kube-terminal - tag: image-tag/v2-alpha-tag - patch_base_reg: ((patch-base-reg)) - patch_base_tag: ((patch-base-tag)) - do: - put: ui-image params: @@ -184,31 +159,17 @@ jobs: patch_base_reg: ((patch-base-reg)) patch_base_tag: ((patch-base-tag)) - do: - - put: fdbserver-image - params: - dockerfile: stratos/deploy/containers/monocular/fdb-server/Dockerfile - build: stratos/deploy/containers/monocular/fdb-server/ - tag: image-tag/v2-alpha-tag - patch_base_reg: ((patch-base-reg)) - patch_base_tag: ((patch-base-tag)) - - put: fdbdoclayer-image - params: - dockerfile: stratos/deploy/containers/monocular/fdb-doclayer/Dockerfile - build: stratos/deploy/containers/monocular/fdb-doclayer/ - tag: image-tag/v2-alpha-tag - patch_base_reg: ((patch-base-reg)) - patch_base_tag: ((patch-base-tag)) - - put: chartsync-image + - put: analyzers-image params: - dockerfile: stratos/src/jetstream/plugins/monocular/chart-repo/Dockerfile - build: stratos/src/jetstream/plugins/monocular/chart-repo/ + dockerfile: stratos/src/jetstream/plugins/analysis/container/Dockerfile + build: stratos/src/jetstream/plugins/analysis/container/ tag: image-tag/v2-alpha-tag patch_base_reg: ((patch-base-reg)) patch_base_tag: ((patch-base-tag)) - - put: analyzers-image + - put: kube-terminal-image params: - dockerfile: stratos/src/jetstream/plugins/analysis/container/Dockerfile - build: stratos/src/jetstream/plugins/analysis/container/ + dockerfile: stratos/deploy/containers/kube-terminal/Dockerfile.kubeterminal + build: stratos/deploy/containers/kube-terminal tag: image-tag/v2-alpha-tag patch_base_reg: ((patch-base-reg)) patch_base_tag: ((patch-base-tag)) diff --git a/deploy/ci/tasks/dev-releases/nightly-release-description b/deploy/ci/tasks/dev-releases/nightly-release-description index 10f5c521b9..f7b1480f6d 100644 --- a/deploy/ci/tasks/dev-releases/nightly-release-description +++ b/deploy/ci/tasks/dev-releases/nightly-release-description @@ -1 +1 @@ -This is an alpha release of Stratos v2 \ No newline at end of file +Nightly Stratos build \ No newline at end of file diff --git a/deploy/ci/tasks/dev-releases/nightly-release-name b/deploy/ci/tasks/dev-releases/nightly-release-name index ae784554be..fc7a445a46 100644 --- a/deploy/ci/tasks/dev-releases/nightly-release-name +++ b/deploy/ci/tasks/dev-releases/nightly-release-name @@ -1 +1 @@ -Stratos 3.0.0 Nightly Release \ No newline at end of file +Stratos Nightly Release \ No newline at end of file diff --git a/deploy/cloud-foundry/config.properties b/deploy/cloud-foundry/config.properties index a5cb9bd20a..648d77d54a 100644 --- a/deploy/cloud-foundry/config.properties +++ b/deploy/cloud-foundry/config.properties @@ -19,4 +19,6 @@ ENCRYPTION_KEY=B374A26A71490437AA024E4FADD5B497FDFF1A8EA6FF12F6FB65AF2720B59CCF #VCAP_APPLICATION={"cf_api": "https://api.10.4.21.240.nip.io:8443"} # User invite templates -TEMPLATE_DIR=./templates \ No newline at end of file +TEMPLATE_DIR=./templates + +HELM_CACHE_FOLDER=./helm-cache \ No newline at end of file diff --git a/deploy/containers/monocular/fdb-doclayer/Dockerfile b/deploy/containers/monocular/fdb-doclayer/Dockerfile deleted file mode 100644 index e670a88e2a..0000000000 --- a/deploy/containers/monocular/fdb-doclayer/Dockerfile +++ /dev/null @@ -1,64 +0,0 @@ - -# Original Source: https://raw.githubusercontent.com/kreinecke/fdb-document-layer/master/packaging/docker/Dockerfile - -# Dockerfile -# -# This source file is part of the FoundationDB open source project -# -# Copyright 2013-2018 Apple Inc. and the FoundationDB project authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Builder container -FROM splatform/stratos-bk-build-base:leap15_1 as builder - -# Install dependencies - -# Our Stratos base image already has curl and less in it - -# Install FoundationDB Document Layer Binaries - -ARG FDB_DOC_VERSION=1.6.3 -ARG FDB_WEBSITE=https://www.foundationdb.org - -WORKDIR /home/stratos -RUN curl $FDB_WEBSITE/downloads/$FDB_DOC_VERSION/linux/fdb-document-layer-$FDB_DOC_VERSION-Linux.tar.gz -o fdb-document-layer-$FDB_DOC_VERSION-Linux.tar.gz && \ - tar -xvf fdb-document-layer-$FDB_DOC_VERSION-Linux.tar.gz && \ - mv fdb-document-layer-$FDB_DOC_VERSION-Linux doclayer - - -# This Docker image is just packing 6.0 client libraries. Document Layer works with -# any FoundationDB server >= 5.1.0. If your server version is not 6.0, then you might -# have to add the correct version client library here. -ARG FDB_CLIENT_VERSION=6.2.15 -RUN curl $FDB_WEBSITE/downloads/$FDB_CLIENT_VERSION/linux/libfdb_c_$FDB_CLIENT_VERSION.so -o /home/stratos/libfdb_c.so && \ - chmod +x /home/stratos/libfdb_c.so && \ - rm -rf /mnt/website - -# Final doclayer container -FROM splatform/stratos-bk-init-base:leap15_1 -WORKDIR /var/fdb - -COPY --from=builder /home/stratos/libfdb_c.so /usr/lib64/libfdb_c.so -COPY --from=builder /home/stratos/doclayer/bin/fdbdoc /usr/bin/fdbdoc -COPY --from=builder /home/stratos/doclayer/lib/foundationdb/document/fdbmonitor /usr/bin/fdbmonitor - -COPY fdbdoc.bash scripts/ -RUN chmod u+x scripts/*.bash && mkdir -p logs - -CMD /var/fdb/scripts/fdbdoc.bash - -# Runtime Configuration Options -ENV FDB_DOC_PORT 27016 -ENV FDB_NETWORKING_MODE container \ No newline at end of file diff --git a/deploy/containers/monocular/fdb-doclayer/fdb-doc-layer-networkpolicy.yaml b/deploy/containers/monocular/fdb-doclayer/fdb-doc-layer-networkpolicy.yaml deleted file mode 100644 index 7322378bd0..0000000000 --- a/deploy/containers/monocular/fdb-doclayer/fdb-doc-layer-networkpolicy.yaml +++ /dev/null @@ -1,21 +0,0 @@ - -{{- if .Values.fdbdoclayer.networkPolicy.enabled }} -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: {{ template "fullname" . }}-fdbdoclayer - labels: - app: {{ template "fullname" . }}-fdbdoclayer - chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" - release: "{{ .Release.Name }}" - heritage: "{{ .Release.Service }}" -spec: - podSelector: - matchLabels: - app: {{ template "fullname" . }}-fdbdoclayer - release: {{ .Release.Name }} - ingress: - ports: - port: 27016 - protocol: TCP -{{- end }} diff --git a/deploy/containers/monocular/fdb-doclayer/fdbdoc.bash b/deploy/containers/monocular/fdb-doclayer/fdbdoc.bash deleted file mode 100755 index 935c119741..0000000000 --- a/deploy/containers/monocular/fdb-doclayer/fdbdoc.bash +++ /dev/null @@ -1,75 +0,0 @@ -#! /bin/bash - -function setup_cluster_file() { - FDB_CLUSTER_FILE=${FDB_CLUSTER_FILE:-/etc/foundationdb/fdb.cluster} - mkdir -p $(dirname $FDB_CLUSTER_FILE) - - if [[ -n $FDB_COORDINATOR ]]; then - echo "FDB coordinator: $FDB_COORDINATOR" - if [[ -z $coordinator_ip ]]; then - coordinator_ip=$(dig +short +search $FDB_COORDINATOR) - fi - echo "Coordinator IP: $coordinator_ip" - if [[ -z "$coordinator_ip" ]]; then - echo "Failed to look up coordinator address for $FDB_COORDINATOR" 1>&2 - exit 1 - fi - coordinator_port=${FDB_COORDINATOR_PORT:-4500} - if [[ -z $CLUSTER_ID ]]; then - echo "CLUSTER_ID environment variable not defined" 1>&2 - exit 1 - fi - echo "$CLUSTER_ID@$coordinator_ip:$coordinator_port" > $FDB_CLUSTER_FILE - else - echo "FDB_COORDINATOR environment variable not defined" 1>&2 - exit 1 - fi - - if [ ! -f ${FDB_CLUSTER_FILE} ]; then - echo "Failed to locate cluster file at $FDB_CLUSTER_FILE" 1>&2 - exit 1 - fi -} - -function setup_public_ip() { - if [[ "$FDB_NETWORKING_MODE" == "host" ]]; then - public_ip=127.0.0.1 - elif [[ "$FDB_NETWORKING_MODE" == "container" ]]; then - public_ip=$(grep `hostname` /etc/hosts | sed -e "s/\s *`hostname`.*//") - coordinator_ip=$public_ip - else - echo "Unknown FDB Networking mode \"$FDB_NETWORKING_MODE\"" 1>&2 - exit 1 - fi - - PUBLIC_IP=$public_ip -} - -setup_public_ip -setup_cluster_file - -if [ -n ${FDB_LISTEN_IP} ]; then - LISTEN_IP=${FDB_LISTEN_IP} -else - LISTEN_IP=${PUBLIC_IP} -fi - -echo "====================================================================================" -echo "FDB Document Layer starting" -echo "====================================================================================" -echo "" - -echo "Connecting to FDB server at: $CLUSTER_ID@$coordinator_ip:$coordinator_port" -echo "Cluster file contents: " -cat $FDB_CLUSTER_FILE - -echo "Listen IP is ${LISTEN_IP}" -echo "Public IP is ${PUBLIC_IP}" - -if [[ "$ENABLE_TLS" == "true" ]]; then - echo "Starting FDB Document Layer on $LISTEN_IP:$FDB_DOC_PORT:tls. TLS enabled." - fdbdoc -V --listen_address $LISTEN_IP:$FDB_DOC_PORT:tls --tls_certificate_file $SERVER_CRT --tls_ca_file $CA_CRT --tls_key_file $SERVER_KEY --logdir /var/fdb/logs -else - echo "Starting FDB Document Layer on $LISTEN_IP:$FDB_DOC_PORT. No TLS." - fdbdoc -V --listen_address $LISTEN_IP:$FDB_DOC_PORT --logdir /var/fdb/logs -fi diff --git a/deploy/containers/monocular/fdb-server/Dockerfile b/deploy/containers/monocular/fdb-server/Dockerfile deleted file mode 100644 index 48e589f3b1..0000000000 --- a/deploy/containers/monocular/fdb-server/Dockerfile +++ /dev/null @@ -1,89 +0,0 @@ -# Taken from: https://github.com/kreinecke/foundationdb/tree/fdb-k8s/packaging/docker - -# Dockerfile -# -# This source file is part of the FoundationDB open source project -# -# Copyright 2013-2018 Apple Inc. and the FoundationDB project authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -FROM splatform/stratos-bk-build-base:leap15_1 as builder - -# Install dependencies - -# RUN apt-get update && \ -# apt-get install -y curl>=7.58.0-2ubuntu3.6 \ -# dnsutils>=1:9.11.3+dfsg-1ubuntu1.7 && \ -# rm -r /var/lib/apt/lists/* - -# NOTE: Dependencies are in the base image - -# Install FoundationDB Binaries - -ARG FDB_VERSION=6.2.15 -ARG FDB_WEBSITE=https://www.foundationdb.org - -USER root -WORKDIR /home/stratos/tmp -WORKDIR /home/stratos -RUN pwd && ls -al -RUN curl $FDB_WEBSITE/downloads/$FDB_VERSION/linux/fdb_$FDB_VERSION.tar.gz -o fdb_$FDB_VERSION.tar.gz && \ - tar -xzf fdb_$FDB_VERSION.tar.gz --strip-components=1 && \ - rm fdb_$FDB_VERSION.tar.gz && \ - chmod u+x fdbbackup fdbcli fdbdr fdbmonitor fdbrestore fdbserver backup_agent dr_agent && \ - mv fdbbackup fdbcli fdbdr fdbmonitor fdbrestore fdbserver backup_agent dr_agent /home/stratos/tmp - - -# Main container -FROM splatform/stratos-bk-base:leap15_1 - -WORKDIR /var/fdb - -# Install FoundationDB Client Libraries - -ARG FDB_VERSION=6.2.15 -ARG FDB_ADDITIONAL_VERSIONS="5.1.7" -ARG FDB_WEBSITE=https://www.foundationdb.org - -COPY --from=builder /home/stratos/tmp /usr/bin - -COPY download_multiversion_libraries.bash scripts/ - -RUN mkdir -p /mnt/website -RUN curl $FDB_WEBSITE/downloads/$FDB_VERSION/linux/libfdb_c_$FDB_VERSION.so -o /usr/lib/libfdb_c.so && \ - bash scripts/download_multiversion_libraries.bash $FDB_WEBSITE $FDB_ADDITIONAL_VERSIONS && \ - rm -rf /mnt/website - -# Set Up Runtime Scripts and Directories - -COPY fdb.bash scripts/ -COPY create_server_environment.bash scripts/ -COPY create_cluster_file.bash scripts/ -COPY configure_db.bash scripts/ -RUN chmod u+x scripts/*.bash && \ - mkdir -p logs -VOLUME /var/fdb/data - -CMD /var/fdb/scripts/fdb.bash - -# Runtime Configuration Options - -ENV FDB_PORT 4500 -ENV FDB_CLUSTER_FILE /var/fdb/fdb.cluster -ENV FDB_NETWORKING_MODE container -ENV FDB_COORDINATOR "" -ENV FDB_COORDINATOR_PORT 4500 -ENV FDB_CLUSTER_FILE_CONTENTS "" -ENV FDB_PROCESS_CLASS unset \ No newline at end of file diff --git a/deploy/containers/monocular/fdb-server/configure_db.bash b/deploy/containers/monocular/fdb-server/configure_db.bash deleted file mode 100755 index df2a8d6e91..0000000000 --- a/deploy/containers/monocular/fdb-server/configure_db.bash +++ /dev/null @@ -1,45 +0,0 @@ -#! /bin/bash - -# -# create_cluster_file.bash -# -# This source file is part of the FoundationDB open source project -# -# Copyright 2013-2018 Apple Inc. and the FoundationDB project authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# This script creates a cluster file for a server or client. -# This takes the cluster file path from the FDB_CLUSTER_FILE -# environment variable, with a default of /etc/foundationdb/fdb.cluster -# -# The name of the coordinator must be defined in the FDB_COORDINATOR environment -# variable, and it must be a name that can be resolved through DNS. - -function configure_db() { - max_retry=10 - counter=0 - until fdbcli --exec "configure new single memory" | grep 'Database created' - do - sleep 1 - [[ counter -eq $max_retry ]] && echo "Failed!" && exit 1 - echo "Could not init db yet. Trying again. Try #$counter" - ((counter++)) - done - echo "Database created." -} - -if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then - configure_db "$@" -fi \ No newline at end of file diff --git a/deploy/containers/monocular/fdb-server/create_cluster_file.bash b/deploy/containers/monocular/fdb-server/create_cluster_file.bash deleted file mode 100644 index 0a710264a4..0000000000 --- a/deploy/containers/monocular/fdb-server/create_cluster_file.bash +++ /dev/null @@ -1,61 +0,0 @@ -#! /bin/bash - -# -# create_cluster_file.bash -# -# This source file is part of the FoundationDB open source project -# -# Copyright 2013-2018 Apple Inc. and the FoundationDB project authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# This script creates a cluster file for a server or client. -# This takes the cluster file path from the FDB_CLUSTER_FILE -# environment variable, with a default of /etc/foundationdb/fdb.cluster -# -# The name of the coordinator must be defined in the FDB_COORDINATOR environment -# variable, and it must be a name that can be resolved through DNS. - -function create_cluster_file() { - FDB_CLUSTER_FILE=${FDB_CLUSTER_FILE:-/etc/foundationdb/fdb.cluster} - mkdir -p $(dirname $FDB_CLUSTER_FILE) - - if [[ -n "$FDB_CLUSTER_FILE_CONTENTS" ]]; then - echo "$FDB_CLUSTER_FILE_CONTENTS" > $FDB_CLUSTER_FILE - elif [[ -n $FDB_COORDINATOR ]]; then - #We are in k8s and must lookup the fdb service hostname, not the pod IP. - coordinator_ip=$(dig +short +search $FDB_COORDINATOR) - if [[ -z "$coordinator_ip" ]]; then - echo "Failed to look up coordinator address for $FDB_COORDINATOR" 1>&2 - exit 1 - fi - if [[ -z $CLUSTER_ID ]]; then - echo "CLUSTER_ID environment variable not defined" 1>&2 - exit 1 - fi - coordinator_port=${FDB_COORDINATOR_PORT:-4500} - echo "export COORDINATOR_IP=$coordinator_ip" >> $env_file - echo "export COORDINATOR_PORT=$coordinator_port" >> $env_file - source $env_file - #echo "$CLUSTER_ID@$coordinator_ip:$coordinator_port,127.0.0.1:4500" > $FDB_CLUSTER_FILE - echo "$CLUSTER_ID@$PUBLIC_IP:4500" > $FDB_CLUSTER_FILE - else - echo "FDB_COORDINATOR environment variable not defined" 1>&2 - exit 1 - fi -} - -if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then - create_cluster_file "$@" -fi \ No newline at end of file diff --git a/deploy/containers/monocular/fdb-server/create_server_environment.bash b/deploy/containers/monocular/fdb-server/create_server_environment.bash deleted file mode 100644 index d417f0367d..0000000000 --- a/deploy/containers/monocular/fdb-server/create_server_environment.bash +++ /dev/null @@ -1,49 +0,0 @@ -#! /bin/bash - -# -# create_server_environment.bash -# -# This source file is part of the FoundationDB open source project -# -# Copyright 2013-2018 Apple Inc. and the FoundationDB project authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -source /var/fdb/scripts/create_cluster_file.bash - -function create_server_environment() { - fdb_dir=/var/fdb - env_file=$fdb_dir/.fdbenv - - : > $env_file - - # If using docker in host network mode, or if we are running our client in the same container, use 127.0.0.1 - if [[ "$FDB_NETWORKING_MODE" == "host" ]]; then - public_ip=127.0.0.1 - elif [[ "$FDB_NETWORKING_MODE" == "container" ]]; then - #We are running in k8s - public_ip=$(grep `hostname` /etc/hosts | sed -e "s/\s *`hostname`.*//") - else - echo "Unknown FDB Networking mode \"$FDB_NETWORKING_MODE\"" 1>&2 - exit 1 - fi - - echo "export PUBLIC_IP=$public_ip" >> $env_file - # If the FDB_COORDINATOR node is not set, then we are the coordinator - use our public IP - if [[ -z $FDB_COORDINATOR ]]; then - FDB_CLUSTER_FILE_CONTENTS="docker:docker@$public_ip:$FDB_PORT" - fi - - create_cluster_file -} diff --git a/deploy/containers/monocular/fdb-server/download_multiversion_libraries.bash b/deploy/containers/monocular/fdb-server/download_multiversion_libraries.bash deleted file mode 100644 index 651947857c..0000000000 --- a/deploy/containers/monocular/fdb-server/download_multiversion_libraries.bash +++ /dev/null @@ -1,31 +0,0 @@ -#! /bin/bash - -# -# download_multiversion_libraries.bash -# -# This source file is part of the FoundationDB open source project -# -# Copyright 2013-2018 Apple Inc. and the FoundationDB project authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -mkdir -p /usr/lib/fdb/multiversion -website=$1 -shift -for version in $*; do - origin=$website/downloads/$version/linux/libfdb_c_$version.so - destination=/usr/lib/fdb/multiversion/libfdb_c_$version.so - echo "Downloading $origin to $destination" - curl $origin -o $destination -done \ No newline at end of file diff --git a/deploy/containers/monocular/fdb-server/fdb.bash b/deploy/containers/monocular/fdb-server/fdb.bash deleted file mode 100644 index 44842089e4..0000000000 --- a/deploy/containers/monocular/fdb-server/fdb.bash +++ /dev/null @@ -1,46 +0,0 @@ -#! /bin/bash - -# -# fdb.bash -# -# This source file is part of the FoundationDB open source project -# -# Copyright 2013-2018 Apple Inc. and the FoundationDB project authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -source /var/fdb/scripts/create_server_environment.bash -create_server_environment -source /var/fdb/.fdbenv -echo "Starting FDB server listening on: $PUBLIC_IP:$FDB_PORT public address: $PUBLIC_IP:$FDB_PORT" -cat /var/fdb/fdb.cluster -source /var/fdb/scripts/configure_db.bash -configure_db & - -if [ -n ${FDB_LISTEN_IP} ]; then - LISTEN_IP=${FDB_LISTEN_IP} -else - LISTEN_IP=${PUBLIC_IP} -fi - -echo "====================================================================================" -echo "FDB Server starting" -echo "====================================================================================" -echo "" - -echo "Listen IP is ${LISTEN_IP}" -echo "Public IP is ${PUBLIC_IP}" - -fdbserver --listen_address $LISTEN_IP:$FDB_PORT --public_address $PUBLIC_IP:$FDB_PORT \ - --datadir /var/fdb/data --logdir /var/fdb/logs \ No newline at end of file diff --git a/deploy/containers/monocular/fdb-server/fdbserver-networkpolicy.yaml b/deploy/containers/monocular/fdb-server/fdbserver-networkpolicy.yaml deleted file mode 100644 index 2752d371e2..0000000000 --- a/deploy/containers/monocular/fdb-server/fdbserver-networkpolicy.yaml +++ /dev/null @@ -1,21 +0,0 @@ - -{{- if .Values.fdbserver.networkPolicy.enabled }} -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: {{ template "fullname" . }}-fdbserver - labels: - app: {{ template "fullname" . }}-fdbserver - chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" - release: "{{ .Release.Name }}" - heritage: "{{ .Release.Service }}" -spec: - podSelector: - matchLabels: - app: {{ template "fullname" . }}-fdbserver - release: {{ .Release.Name }} - ingress: - - ports: - - port: 4500 - protocol: TCP -{{- end }} diff --git a/deploy/kubernetes/build.sh b/deploy/kubernetes/build.sh index f838cc3549..4dc645a607 100755 --- a/deploy/kubernetes/build.sh +++ b/deploy/kubernetes/build.sh @@ -296,5 +296,5 @@ printf "${RESET}" echo echo "To deploy using Helm, execute the following:" echo -echo " helm install helm-chart --namespace console --name my-console" +echo " helm install my-console ./helm-chart --namespace console" echo diff --git a/deploy/kubernetes/console/Chart.yaml b/deploy/kubernetes/console/Chart.yaml index 22538c426e..aaea5f78e9 100644 --- a/deploy/kubernetes/console/Chart.yaml +++ b/deploy/kubernetes/console/Chart.yaml @@ -1,8 +1,17 @@ apiVersion: v1 -description: A Helm chart for deploying Stratos UI Console +description: A Helm chart for deploying Stratos name: console version: 0.1.0 appVersion: 0.1.0 sources: - https://github.com/cloudfoundry/stratos -icon: https://raw.githubusercontent.com/cloudfoundry/stratos/master/deploy/kubernetes/console/icon.png \ No newline at end of file +icon: https://raw.githubusercontent.com/cloudfoundry/stratos/master/deploy/kubernetes/console/icon.png +home: https://stratos.app +maintainers: + - name: Stratos Maintainers + email: stratos-maintainers@suse.de +keywords: + - Stratos + - "Cloud Foundry" + - Kubernetes + - Helm \ No newline at end of file diff --git a/deploy/kubernetes/console/README.md b/deploy/kubernetes/console/README.md index ee238aa880..cadd5c7d49 100644 --- a/deploy/kubernetes/console/README.md +++ b/deploy/kubernetes/console/README.md @@ -22,7 +22,7 @@ Check the repository was successfully added by searching for the `console`, for ``` helm search repo console NAME CHART VERSION APP VERSION DESCRIPTION -stratos/console 4.0.1 4.0.1 A Helm chart for deploying Stratos UI Console +stratos/console 4.1.0 4.1.0 A Helm chart for deploying Stratos UI Console ``` > Note: Version numbers will depend on the version of Stratos available from the Helm repository diff --git a/deploy/kubernetes/console/templates/NOTES.txt b/deploy/kubernetes/console/templates/NOTES.txt new file mode 100644 index 0000000000..a423cd3600 --- /dev/null +++ b/deploy/kubernetes/console/templates/NOTES.txt @@ -0,0 +1,31 @@ +{{- if .Values.console.techPreview }} +Tech Preview is enabled, extra features will be shown. +{{- end}} + +To access Stratos: +{{- $ingress := .Values.console.ingress | default dict }} +{{- if $ingress.enabled }} +From outside the cluster, the server URL is: http://{{ .Values.console.ingress.host }} +{{- else }} +Get the URL by running these commands in the same shell: +{{- if contains "NodePort" .Values.console.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ .Release.Name }}-ui-ext) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo https://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.console.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get svc --namespace {{ .Release.Namespace }} -w {{ .Release.Name }}-ui-ext' + + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ .Release.Name }}-ui-ext -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + echo http://$SERVICE_IP:{{ .Values.console.service.servicePort }} +{{- else if contains "ClusterIP" .Values.console.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app=stratos-0,component=ui" -o jsonpath="{.items[0].metadata.name}") + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 443 +{{- end }} +{{- end }} + +To learn more about the release, try: + $ helm status {{ .Release.Name }} -n {{ .Release.Namespace }} + $ helm get values {{ .Release.Name }} -n {{ .Release.Namespace }} + $ kubectl get services -n {{ .Release.Namespace }} + $ kubectl get pods -n {{ .Release.Namespace }} \ No newline at end of file diff --git a/deploy/kubernetes/console/templates/chartsync.yaml b/deploy/kubernetes/console/templates/chartsync.yaml deleted file mode 100644 index 1eeab2affd..0000000000 --- a/deploy/kubernetes/console/templates/chartsync.yaml +++ /dev/null @@ -1,70 +0,0 @@ ---- -{{- if semverCompare ">=1.16" (printf "%s.%s" .Capabilities.KubeVersion.Major (trimSuffix "+" .Capabilities.KubeVersion.Minor) )}} -apiVersion: apps/v1 -{{- else }} -apiVersion: extensions/v1beta1 -{{- end }} -kind: Deployment -metadata: - name: stratos-chartsync - labels: - app.kubernetes.io/name: "stratos" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "stratos-chartsync" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -spec: - selector: - matchLabels: - app.kubernetes.io/name: "stratos" - app.kubernetes.io/component: "stratos-chartsync" - template: - metadata: - labels: - app.kubernetes.io/name: "stratos" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "stratos-chartsync" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" - app: "{{ .Release.Name }}" - spec: - containers: - - name: chartsync - image: {{.Values.kube.registry.hostname}}/{{.Values.kube.organization}}/{{ default "stratos-chartsync" .Values.images.chartsync}}:{{.Values.consoleVersion}} - imagePullPolicy: {{.Values.imagePullPolicy}} - command: ["/chartrepo"] - args: ["serve", "--doclayer-url=mongodb://{{ .Release.Name }}-fdbdoclayer:27016", "--cafile=/etc/certs/ca.crt", "--certfile=/etc/certs/tls.crt", "--keyfile=/etc/certs/tls.key"] - env: - - name: STRATOS_IMAGE_REF - value: "{{.Values.consoleVersion}}:{{ .Release.Revision }}" - ports: - - name: endpoint - containerPort: 8080 - volumeMounts: - - name: certs - mountPath: "/etc/certs" - readOnly: true - volumes: - - name: certs - secret: - secretName: {{ .Release.Name }}-fdbdoclayer-certs ---- -apiVersion: v1 -kind: Service -metadata: - name: "{{ .Release.Name }}-chartsync" - labels: - app.kubernetes.io/name: "stratos" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "stratos-chartsync-service" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -spec: - type: ClusterIP - ports: - - name: chartsync - port: 8080 - targetPort: 8080 - selector: - app: "{{ .Release.Name }}" - app.kubernetes.io/component: "stratos-chartsync" diff --git a/deploy/kubernetes/console/templates/deployment.yaml b/deploy/kubernetes/console/templates/deployment.yaml index 75743bfa44..40280e1ba8 100644 --- a/deploy/kubernetes/console/templates/deployment.yaml +++ b/deploy/kubernetes/console/templates/deployment.yaml @@ -284,6 +284,8 @@ spec: value: {{ default "false" .Values.console.techPreview | quote }} - name: API_KEYS_ENABLED value: {{ default "admin_only" .Values.console.apiKeysEnabled | quote }} + - name: HELM_CACHE_FOLDER + value: /helm-cache {{- if .Values.console.ui }} {{- if .Values.console.ui.listMaxSize }} - name: UI_LIST_MAX_SIZE @@ -317,9 +319,8 @@ spec: name: "{{ .Release.Name }}-templates" readOnly: true {{- end }} - - mountPath: "/etc/monocular-certs" - name: "{{ .Release.Name }}-monocular-certs" - readOnly: true + - mountPath: /helm-cache + name: helm-cache-volume {{- if and .Values.kube.registry.username .Values.kube.registry.password }} imagePullSecrets: - name: {{.Values.dockerRegistrySecret}} @@ -349,6 +350,5 @@ spec: configMap: name: {{ .Values.console.templatesConfigMapName }} {{- end }} - - name: "{{ .Release.Name }}-monocular-certs" - secret: - secretName: {{ .Release.Name }}-fdbdoclayer-certs + - name: helm-cache-volume + emptyDir: {} diff --git a/deploy/kubernetes/console/templates/fdb-secrets.yaml b/deploy/kubernetes/console/templates/fdb-secrets.yaml deleted file mode 100644 index 7e9598779f..0000000000 --- a/deploy/kubernetes/console/templates/fdb-secrets.yaml +++ /dev/null @@ -1,27 +0,0 @@ -{{/* -Generate self-signed certificate -*/}} -{{- define "fdbdoclayer.generateCertificate" -}} -{{- $altNames := list ( printf "%s%s" .Release.Name "-fdbdoclayer") ( printf "%s%s.%s" (include "fullname" .) "-fdbdoclayer" .Release.Namespace ) ( printf "%s.%s.svc" (include "console.certName" .) .Release.Namespace ) -}} -{{- $ca := genCA "stratos-ca" 365 -}} -{{- $cert := genSignedCert "fdbdoclayer" nil $altNames 365 $ca -}} -tls.crt: {{ $cert.Cert | b64enc }} -tls.key: {{ $cert.Key | b64enc }} -tls.pem: {{ printf "%s\n%s\n%s\n" ($cert.Cert) ($ca.Cert) ($cert.Key) | b64enc }} -ca.crt: {{ $ca.Cert | b64enc }} -{{- end -}} - ---- -apiVersion: v1 -kind: Secret -type: kubernetes.io/tls -metadata: - name: {{ .Release.Name }}-fdbdoclayer-certs - labels: - app.kubernetes.io/name: "stratos" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "fdbdoclayer-certs" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -data: -{{ (include "fdbdoclayer.generateCertificate" .) | indent 2 }} \ No newline at end of file diff --git a/deploy/kubernetes/console/templates/fdbdoclayer/deployment.yaml b/deploy/kubernetes/console/templates/fdbdoclayer/deployment.yaml deleted file mode 100644 index 90923912b0..0000000000 --- a/deploy/kubernetes/console/templates/fdbdoclayer/deployment.yaml +++ /dev/null @@ -1,123 +0,0 @@ - -{{- if semverCompare ">=1.16" (printf "%s.%s" .Capabilities.KubeVersion.Major (trimSuffix "+" .Capabilities.KubeVersion.Minor) )}} -apiVersion: apps/v1 -{{- else }} -apiVersion: apps/v1beta2 -{{- end }} -kind: Deployment -metadata: - name: stratos-chartstore -{{- if .Values.console.deploymentAnnotations }} - annotations: -{{ toYaml .Values.console.deploymentAnnotations | indent 4 }} -{{- end }} - labels: - app.kubernetes.io/name: "stratos" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "stratos-chartstore" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -{{- if .Values.console.deploymentExtraLabels }} -{{ toYaml .Values.console.deploymentExtraLabels | indent 4 }} -{{- end }} -spec: - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/name: "stratos" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/component: "stratos-chartstore" - template: - metadata: -{{- if .Values.console.podAnnotations }} - annotations: -{{ toYaml .Values.console.podAnnotations | indent 8 }} -{{- end }} - labels: - app.kubernetes.io/name: "stratos" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "stratos-chartstore" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" - {{- if .Values.console.podExtraLabels}} - {{ toYaml .Values.console.podExtraLabels | nindent 8 }} - {{- end}} - spec: -{{- with .Values.securityContext }} - securityContext: -{{ toYaml . | indent 8 }} -{{- end }} - containers: - - name: fdbdoclayer - image: {{.Values.kube.registry.hostname}}/{{.Values.kube.organization}}/{{.Values.images.fdbdoclayer}}:{{.Values.consoleVersion}} - imagePullPolicy: {{.Values.imagePullPolicy}} - env: - - name: STRATOS_IMAGE_REF - value: "{{.Values.consoleVersion}}:{{ .Release.Revision }}" - - name: FDB_COORDINATOR - value: {{ .Release.Name }}-fdbdoclayer - - name: FDB_LISTEN_IP - value: 0.0.0.0 - - name: CLUSTER_ID - value: chartdb:erlklkg - - name: ENABLE_TLS - value: "true" - - name: SERVER_CRT - value: "/etc/secrets/tls.crt" - - name: SERVER_KEY - value: "/etc/secrets/tls.key" - - name: CA_CRT - value: "/etc/secrets/ca.crt" - - name: FDB_NETWORKING_MODE - value: container - ports: - - containerPort: 27016 - livenessProbe: - tcpSocket: - port: 27016 - initialDelaySeconds: 3 - periodSeconds: 20 - readinessProbe: - tcpSocket: - port: 27016 - initialDelaySeconds: 3 - periodSeconds: 10 - volumeMounts: - - name: certs - mountPath: "/etc/secrets" - readOnly: true - resources: - limits: - memory: 384Mi - requests: - memory: 256Mi - - name: fdbserver - image: {{.Values.kube.registry.hostname}}/{{.Values.kube.organization}}/{{.Values.images.fdbserver}}:{{.Values.consoleVersion}} - imagePullPolicy: {{.Values.imagePullPolicy}} - env: - - name: STRATOS_IMAGE_REF - value: "{{.Values.consoleVersion}}:{{ .Release.Revision }}" - - name: FDB_COORDINATOR - value: {{ .Release.Name }}-fdbdoclayer - - name: FDB_LISTEN_IP - value: 0.0.0.0 - - name: CLUSTER_ID - value: chartdb:erlklkg - - name: FDB_NETWORKING_MODE - value: container - ports: - - containerPort: 4500 - livenessProbe: - tcpSocket: - port: 4500 - initialDelaySeconds: 3 - periodSeconds: 20 - readinessProbe: - tcpSocket: - port: 4500 - initialDelaySeconds: 3 - periodSeconds: 10 - volumes: - - name: certs - secret: - secretName: {{ .Release.Name }}-fdbdoclayer-certs diff --git a/deploy/kubernetes/console/templates/fdbdoclayer/fdb-doc-layer-service.yaml b/deploy/kubernetes/console/templates/fdbdoclayer/fdb-doc-layer-service.yaml deleted file mode 100644 index b3c74bfb0a..0000000000 --- a/deploy/kubernetes/console/templates/fdbdoclayer/fdb-doc-layer-service.yaml +++ /dev/null @@ -1,33 +0,0 @@ - -apiVersion: v1 -kind: Service -metadata: - name: {{ .Release.Name }}-fdbdoclayer -{{- if .Values.console.service -}} -{{- if .Values.console.service.annotations }} - annotations: -{{ toYaml .Values.console.service.annotations | indent 4 }} -{{- end }} -{{- end }} - labels: - app.kubernetes.io/name: "stratos" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "{{ .Release.Name }}-fdbdoclayer" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -{{- if .Values.console.service -}} -{{- if .Values.console.service.extraLabels }} -{{ toYaml .Values.console.service.extraLabels | indent 4 }} -{{- end }} -{{- end }} -spec: - type: ClusterIP - ports: - - port: 27016 - targetPort: 27016 - protocol: TCP - name: fdbdoclayer - selector: - app.kubernetes.io/name: "stratos" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/component: "stratos-chartstore" diff --git a/deploy/kubernetes/console/values.yaml b/deploy/kubernetes/console/values.yaml index ff68bbe3b4..e9999e9d35 100644 --- a/deploy/kubernetes/console/values.yaml +++ b/deploy/kubernetes/console/values.yaml @@ -131,9 +131,6 @@ images: proxy: stratos-jetstream mariadb: stratos-mariadb configInit: stratos-config-init - fdbserver: stratos-fdbserver - fdbdoclayer: stratos-fdbdoclayer - chartsync: stratos-chartsync analyzers: stratos-analyzers # Specify which storage class should be used for PVCs diff --git a/deploy/kubernetes/custom/__stratos.tpl b/deploy/kubernetes/custom/__stratos.tpl index d5eb091928..653bade6f6 100644 --- a/deploy/kubernetes/custom/__stratos.tpl +++ b/deploy/kubernetes/custom/__stratos.tpl @@ -2,16 +2,6 @@ # Extra env vars for the Jetstream Pod in deployment.yaml {{- define "stratosJetstreamEnv" }} -- name: MONOCULAR_CRT_PATH - value: "/etc/monocular-certs/tls.crt" -- name: MONOCULAR_KEY_PATH - value: "/etc/monocular-certs/tls.key" -- name: MONOCULAR_CA_CRT_PATH - value: "/etc/monocular-certs/ca.crt" -- name: FDB_URL - value: "mongodb://{{ .Release.Name }}-fdbdoclayer:27016" -- name: SYNC_SERVER_URL - value: "http://{{ .Release.Name }}-chartsync:8080" - name: ANALYSIS_SERVICES_API value: "http://{{ .Release.Name }}-analyzers:8090" - name: STRATOS_KUBERNETES_NAMESPACE diff --git a/deploy/kubernetes/custom/custom-build.sh b/deploy/kubernetes/custom/custom-build.sh index b44f9fbd41..8b413607cf 100644 --- a/deploy/kubernetes/custom/custom-build.sh +++ b/deploy/kubernetes/custom/custom-build.sh @@ -12,16 +12,6 @@ printf "${RESET}" function custom_image_build() { - log "-- Building/publishing Foundation DB Server" - patchAndPushImage stratos-fdbserver Dockerfile "${STRATOS_PATH}/deploy/containers/monocular/fdb-server" - - log "-- Building/publishing Foundation DB Document Layer" - patchAndPushImage stratos-fdbdoclayer Dockerfile "${STRATOS_PATH}/deploy/containers/monocular/fdb-doclayer" - - # Build and push an image for the Helm Repo Sync Tool - log "-- Building/publishing Monocular Chart Repo Sync Tool" - patchAndPushImage stratos-chartsync Dockerfile "${STRATOS_PATH}/src/jetstream/plugins/monocular/chart-repo" - # Build and push an image for the Kubernetes Terminal log "-- Building/publishing Kubernetes Terminal" patchAndPushImage stratos-kube-terminal Dockerfile.kubeterminal "${STRATOS_PATH}/deploy/containers/kube-terminal" diff --git a/package-lock.json b/package-lock.json index 6ae8d5b767..a7b9794ca8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "stratos", - "version": "4.0.1", + "version": "4.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -3921,6 +3921,26 @@ "lodash-es": "^4.17.15" } }, + "@cfstratos/monaco-yaml": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@cfstratos/monaco-yaml/-/monaco-yaml-2.5.0.tgz", + "integrity": "sha512-4ojHL0lnXZHHW8k2J8ipaXsl+aQj3bOXVdJEIwyhLtsJcYpq3npEiQkQs/U7ySi4Ehm/3qTdccXlilAyuWLxyw==", + "requires": { + "js-yaml": "^3.12.0", + "prettier": "^1.19.1", + "vscode-json-languageservice": "^3.8.3", + "vscode-languageserver": "^6.1.1", + "vscode-uri": "^2.1.2" + }, + "dependencies": { + "prettier": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", + "optional": true + } + } + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -5156,7 +5176,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "requires": { "sprintf-js": "~1.0.2" } @@ -8638,8 +8657,7 @@ "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, "esrecurse": { "version": "4.2.1", @@ -11433,9 +11451,9 @@ "dev": true }, "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -11503,7 +11521,6 @@ "version": "3.13.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -11611,6 +11628,11 @@ "minimist": "^1.2.5" } }, + "jsonc-parser": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz", + "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==" + }, "jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", @@ -11790,13 +11812,6 @@ "path-exists": "^4.0.0" } }, - "fsevents": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", - "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", - "dev": true, - "optional": true - }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -13458,15 +13473,20 @@ "tslib": "^1.9.0" } }, + "ngx-monaco-editor": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/ngx-monaco-editor/-/ngx-monaco-editor-9.0.0.tgz", + "integrity": "sha512-fPXT3M8W920Vs0KPMX6iA7GJYjBRnl9naug9A7D2inPPzxQGtgPZrSEatIPf37a6ir9Ts+8fwt1bTkFzfTgIpQ==" + }, "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", "dev": true }, "node-fetch-npm": { @@ -18242,8 +18262,7 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "sshpk": { "version": "1.16.1", @@ -20184,6 +20203,67 @@ "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", "dev": true }, + "vscode-json-languageservice": { + "version": "3.8.4", + "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-3.8.4.tgz", + "integrity": "sha512-njDG0+YJvYNKXH+6plQGZMxgbifATFrRpC6Qnm/SAn4IW8bMHxsYunsxrjtpqK42CVSz6Lr7bpbTEZbVuOmFLw==", + "requires": { + "jsonc-parser": "^2.3.1", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "3.16.0-next.2", + "vscode-nls": "^5.0.0", + "vscode-uri": "^2.1.2" + } + }, + "vscode-jsonrpc": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz", + "integrity": "sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A==" + }, + "vscode-languageserver": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-6.1.1.tgz", + "integrity": "sha512-DueEpkUAkD5XTR4MLYNr6bQIp/UFR0/IPApgXU3YfCBCB08u2sm9hRCs6DxYZELkk++STPjpcjksR2H8qI3cDQ==", + "requires": { + "vscode-languageserver-protocol": "^3.15.3" + } + }, + "vscode-languageserver-protocol": { + "version": "3.15.3", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz", + "integrity": "sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw==", + "requires": { + "vscode-jsonrpc": "^5.0.1", + "vscode-languageserver-types": "3.15.1" + }, + "dependencies": { + "vscode-languageserver-types": { + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz", + "integrity": "sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ==" + } + } + }, + "vscode-languageserver-textdocument": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.1.tgz", + "integrity": "sha512-UIcJDjX7IFkck7cSkNNyzIz5FyvpQfY7sdzVy+wkKN/BLaD4DQ0ppXQrKePomCxTS7RrolK1I0pey0bG9eh8dA==" + }, + "vscode-languageserver-types": { + "version": "3.16.0-next.2", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.2.tgz", + "integrity": "sha512-QjXB7CKIfFzKbiCJC4OWC8xUncLsxo19FzGVp/ADFvvi87PlmBSCAtZI5xwGjF5qE0xkLf0jjKUn3DzmpDP52Q==" + }, + "vscode-nls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.0.0.tgz", + "integrity": "sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA==" + }, + "vscode-uri": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-2.1.2.tgz", + "integrity": "sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A==" + }, "watchpack": { "version": "1.7.4", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.4.tgz", diff --git a/package.json b/package.json index 33da7f330e..6a2065af21 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "stratos", - "version": "4.0.1", + "version": "4.1.0", "description": "Stratos Console", "main": "index.js", "scripts": { @@ -62,31 +62,33 @@ "@angular/platform-browser-dynamic": "^9.1.6", "@angular/platform-server": "^9.1.6", "@angular/router": "^9.1.6", + "@cfstratos/ajsf-material": "^0.1.6", + "@cfstratos/monaco-yaml": "^2.5.0", "@ngrx/effects": "^9.1.2", "@ngrx/router-store": "^9.1.2", "@ngrx/store": "^9.1.2", "@ngrx/store-devtools": "^9.1.2", "@swimlane/ngx-charts": "^13.0.3", "@swimlane/ngx-graph": "^7.0.1", - "@types/moment-timezone": "^0.5.13", "@types/marked": "^0.7.4", + "@types/moment-timezone": "^0.5.13", "angular2-virtual-scroll": "^0.4.16", "core-js": "^3.6.5", "immer": "^6.0.3", + "intersect": "^1.0.1", "lodash-es": "^4.17.14", "mappy-breakpoints": "^0.2.3", "marked": "^0.8.2", - "intersect": "^1.0.1", "moment": "^2.24.0", "moment-timezone": "^0.5.28", "ngrx-store-localstorage": "9.0.0", "ngx-moment": "^3.5.0", + "ngx-monaco-editor": "^9.0.0", "normalizr": "^3.6.0", "reselect": "^4.0.0", "rxjs": "^6.5.5", "rxjs-spy": "^7.0.2", "rxjs-websockets": "~8.0.1", - "@cfstratos/ajsf-material": "^0.1.6", "ts-md5": "^1.2.7", "tslib": "^1.10.0", "web-animations-js": "^2.3.2", diff --git a/src/frontend/packages/cloud-foundry/src/features/applications/deploy-application/deploy-application-steps.types.ts b/src/frontend/packages/cloud-foundry/src/features/applications/deploy-application/deploy-application-steps.types.ts index 5acb4f80d2..b20ecdb697 100644 --- a/src/frontend/packages/cloud-foundry/src/features/applications/deploy-application/deploy-application-steps.types.ts +++ b/src/frontend/packages/cloud-foundry/src/features/applications/deploy-application/deploy-application-steps.types.ts @@ -34,7 +34,8 @@ export class ApplicationDeploySourceTypes { helpText: 'Please select the public GitHub project and branch you would like to deploy from.', graphic: { // TODO: Move cf assets to CF package (#3769) - location: '/core/assets/endpoint-icons/github-logo.png' + location: '/core/assets/endpoint-icons/github-logo.png', + transform: 'scale(0.7)' } }, { @@ -51,7 +52,8 @@ export class ApplicationDeploySourceTypes { id: DEPLOY_TYPES_IDS.GIT_URL, helpText: 'Please enter the public git url and branch or tag you would like to deploy from.', graphic: { - location: '/core/assets/endpoint-icons/Git-logo.png' + location: '/core/assets/endpoint-icons/Git-logo.png', + transform: 'scale(0.7)' } }, { @@ -59,12 +61,13 @@ export class ApplicationDeploySourceTypes { id: DEPLOY_TYPES_IDS.DOCKER_IMG, helpText: 'Please specify an application name and the Docker image to be used (registry/org/image-name).', graphic: { - location: '/core/assets/endpoint-icons/docker.png' + location: '/core/assets/endpoint-icons/docker.png', + transform: 'scale(0.8)' }, disabledText: 'The selected Cloud Foundry cannot deploy Docker images. Please check that the Diego Docker feature flag is enabled' }, { - name: 'Application Archive File', + name: 'Application Archive', id: DEPLOY_TYPES_IDS.FILE, helpText: 'Please select the archive file that contains the application you would like to deploy.', graphic: { matIcon: 'unarchive' } diff --git a/src/frontend/packages/cloud-foundry/src/features/applications/new-application-base-step/new-application-base-step.component.html b/src/frontend/packages/cloud-foundry/src/features/applications/new-application-base-step/new-application-base-step.component.html index 0b8f2f5066..9bd7ae404a 100644 --- a/src/frontend/packages/cloud-foundry/src/features/applications/new-application-base-step/new-application-base-step.component.html +++ b/src/frontend/packages/cloud-foundry/src/features/applications/new-application-base-step/new-application-base-step.component.html @@ -7,7 +7,8 @@

New Application

Select application source

To create an application you can either deploy from a specific source or create an application shell. An application shell is an empty application with no package associated with it.

- + + \ No newline at end of file diff --git a/src/frontend/packages/core/sass/mat-desktop.scss b/src/frontend/packages/core/sass/mat-desktop.scss index 7e11af970e..dd073dffa5 100644 --- a/src/frontend/packages/core/sass/mat-desktop.scss +++ b/src/frontend/packages/core/sass/mat-desktop.scss @@ -103,6 +103,7 @@ $desktop-toggle-button-item-height: $desktop-menu-item-height - 2px; line-height: normal; } } + } // Smaller page header @@ -127,4 +128,19 @@ $desktop-toggle-button-item-height: $desktop-menu-item-height - 2px; app-profile-info .user-profile { top: $desktop-page-header-height; } + + // Smaller dialog titles & content + .mat-dialog-title { + font-size: 18px; + } + .mat-dialog-content { + font-size: 16px; + } + + // Reduced padding on steppers - the stepper already had padding applied because they are included in the dashboard, which + // has padding via the '.dashboard__content` class - so we end up with double padding - this removes the extra level of padding + .steppers__inner { + padding: 0; + } + } diff --git a/src/frontend/packages/core/src/features/endpoints/create-endpoint/create-endpoint-base-step/create-endpoint-base-step.component.html b/src/frontend/packages/core/src/features/endpoints/create-endpoint/create-endpoint-base-step/create-endpoint-base-step.component.html index 5a3116dce9..c37555a655 100644 --- a/src/frontend/packages/core/src/features/endpoints/create-endpoint/create-endpoint-base-step/create-endpoint-base-step.component.html +++ b/src/frontend/packages/core/src/features/endpoints/create-endpoint/create-endpoint-base-step/create-endpoint-base-step.component.html @@ -12,7 +12,8 @@

Select the type of the endpoint to register

the 'Connecting' process. To do this return to the Endpoints page.

--> - + diff --git a/src/frontend/packages/core/src/features/login/login-page/login-page.component.theme.scss b/src/frontend/packages/core/src/features/login/login-page/login-page.component.theme.scss index 9a682c63c1..e6a56746e2 100644 --- a/src/frontend/packages/core/src/features/login/login-page/login-page.component.theme.scss +++ b/src/frontend/packages/core/src/features/login/login-page/login-page.component.theme.scss @@ -1,5 +1,7 @@ @mixin login-page-theme($theme, $app-theme) { $primary: map-get($theme, primary); + $warn: map-get($theme, warn); + .login { background-color: map-get($app-theme, app-background-color); &__title { @@ -9,4 +11,10 @@ color: mat-contrast($primary, 500); } } + + .suse-login-sso { + .suse-login__message { + color: mat-color($warn); + } + } } diff --git a/src/frontend/packages/core/src/features/login/login-page/login-page.component.ts b/src/frontend/packages/core/src/features/login/login-page/login-page.component.ts index 8764ac68f0..3a4d78324d 100644 --- a/src/frontend/packages/core/src/features/login/login-page/login-page.component.ts +++ b/src/frontend/packages/core/src/features/login/login-page/login-page.component.ts @@ -29,6 +29,7 @@ export class LoginPageComponent implements OnInit, OnDestroy { loggedIn: boolean; loggingIn: boolean; + isLoginFlow: boolean; verifying: boolean; error: boolean; @@ -36,6 +37,7 @@ export class LoginPageComponent implements OnInit, OnDestroy { ssoOptions: string; busy$: Observable; + initialLoad$: Observable; redirect: RouterRedirect; @@ -47,29 +49,37 @@ export class LoginPageComponent implements OnInit, OnDestroy { this.ssoLogin = false; this.store.dispatch(new VerifySession()); const auth$ = this.store.select(s => ({ auth: s.auth, endpoints: s.endpoints })); + this.initialLoad$ = auth$.pipe( + map(({ auth, }) => + auth.verifying && !auth.loggingIn || // checking if user is logged in but not in processed of logging in + (auth.sessionData && auth.sessionData.valid) && !this.isLoginFlow // logged in but haven't hit log in + ), + ); + this.busy$ = auth$.pipe( - map( - ({ auth, endpoints }) => !auth.error || !(auth.sessionData && auth.sessionData.valid) && - (auth.sessionData && auth.sessionData.valid) || auth.verifying || auth.loggingIn || endpoints.loading + map(({ auth, endpoints }) => + !auth.error || + !(auth.sessionData && auth.sessionData.valid) && (auth.sessionData && auth.sessionData.valid) || + auth.verifying || + auth.loggingIn || + endpoints.loading ), startWith(true) ); - this.subscription = - auth$ - .pipe( - tap(({ auth }) => { - this.redirect = auth.redirect; - this.handleOther(auth); - }), - takeWhile(({ auth }) => { - const loggedIn = !auth.loggingIn && auth.loggedIn; - const validSession = auth.sessionData && auth.sessionData.valid; - return !(loggedIn && validSession); - }), - ) - .subscribe({ - complete: () => this.handleSuccess() - }); + this.subscription = auth$.pipe( + tap(({ auth }) => { + this.redirect = auth.redirect; + this.handleOther(auth); + }), + takeWhile(({ auth }) => { + const loggedIn = !auth.loggingIn && auth.loggedIn; + const validSession = auth.sessionData && auth.sessionData.valid; + return !(loggedIn && validSession); + }), + ) + .subscribe({ + complete: () => this.handleSuccess() + }); } ngOnDestroy() { @@ -107,6 +117,7 @@ export class LoginPageComponent implements OnInit, OnDestroy { private handleOther(auth: AuthState) { this.loggedIn = auth.loggedIn; this.loggingIn = auth.loggingIn; + this.isLoginFlow = this.isLoginFlow || auth.loggingIn; this.verifying = auth.verifying; this.ssoOptions = auth.sessionData && auth.sessionData.ssoOptions; this.ssoLogin = !!this.ssoOptions; diff --git a/src/frontend/packages/core/src/shared/components/stepper/steppers/steppers.component.ts b/src/frontend/packages/core/src/shared/components/stepper/steppers/steppers.component.ts index 6c87a6e2a4..062ef505f2 100644 --- a/src/frontend/packages/core/src/shared/components/stepper/steppers/steppers.component.ts +++ b/src/frontend/packages/core/src/shared/components/stepper/steppers/steppers.component.ts @@ -126,7 +126,7 @@ export class SteppersComponent implements OnInit, AfterContentInit, OnDestroy { console.warn('Stepper failed: ', err); return observableOf({ success: false, - message: 'Failed', + message: err || 'Failed', redirectPayload: null, redirect: false, data: {}, diff --git a/src/frontend/packages/core/src/shared/components/tile-selector-tile/tile-selector-tile.component.html b/src/frontend/packages/core/src/shared/components/tile-selector-tile/tile-selector-tile.component.html index 155b4b7885..eefa5a8c67 100644 --- a/src/frontend/packages/core/src/shared/components/tile-selector-tile/tile-selector-tile.component.html +++ b/src/frontend/packages/core/src/shared/components/tile-selector-tile/tile-selector-tile.component.html @@ -1,13 +1,16 @@ -
-
- {{ tile.graphic.matIcon }} - - - +
+
+
+ {{ tile.graphic.matIcon }} + + + +
+
+

{{ tile.label }}

+
+
{{ tile.description }}
-
-

{{ tile.label }}

-
-
{{ tile.description }}
-
+
\ No newline at end of file diff --git a/src/frontend/packages/core/src/shared/components/tile-selector-tile/tile-selector-tile.component.scss b/src/frontend/packages/core/src/shared/components/tile-selector-tile/tile-selector-tile.component.scss index 68db7e8382..2a6328ad84 100644 --- a/src/frontend/packages/core/src/shared/components/tile-selector-tile/tile-selector-tile.component.scss +++ b/src/frontend/packages/core/src/shared/components/tile-selector-tile/tile-selector-tile.component.scss @@ -1,74 +1,82 @@ -.tile-selector { - $tile-height: 200px; - border-radius: 3px; - cursor: pointer; - display: flex; - flex-direction: column; - height: $tile-height; - opacity: .5; - transition: transform .2s ease; - user-select: none; - width: 100%; - &__taller { - height: 260px; - } - &:hover { - opacity: 1; - transform: scale(1.02); - } - &__more { - align-items: center; +@mixin create($tile-height: 200px, $icon-size: 80px) { + $img-size: $tile-height / 2; + + .tile-selector { + border-radius: 3px; cursor: pointer; display: flex; - font-size: 16px; + flex-direction: column; height: $tile-height; - justify-content: center; - } + opacity: .5; + transition: transform .2s ease; + user-select: none; + width: 100%; - &:active { - transform: scale(.98); - } + &:hover { + opacity: 1; + transform: scale(1.02); + } + &__more { + align-items: center; + cursor: pointer; + display: flex; + font-size: 16px; + height: $tile-height; + justify-content: center; + } - &__active { - opacity: 1; - } + &:active { + transform: scale(.98); + } - &__header { - align-items: center; - display: flex; - flex: 2; - justify-content: center; - width: 100%; + &__active { + opacity: 1; + } + + &__header { + align-items: center; + display: flex; + flex: 2; + justify-content: center; + width: 100%; - img { - height: 100px; + img { + height: $img-size; + } } - } - &__description { - flex: 0; - padding: 10px; - text-align: center; - } + &__description { + flex: 0; + padding: 10px; + text-align: center; + } - &__content { - display: flex; - flex: 0; - flex-direction: column; - justify-content: center; - opacity: .8; - padding: 0 1em; - position: relative; - text-align: center; - word-wrap: break-word; + &__content { + display: flex; + flex: 0; + flex-direction: column; + justify-content: center; + opacity: .8; + padding: 0 1em; + position: relative; + text-align: center; + word-wrap: break-word; + } + + &__icon { + font-size: $icon-size; + height: $icon-size; + opacity: .6; + width: $icon-size; + } } +} + +.tile-selector-parent { + @include create; - $icon-size: 80px; - &__icon { - font-size: $icon-size; - height: $icon-size; - opacity: .6; - width: $icon-size; + &.smaller { + @include create(160px, 60px); } } diff --git a/src/frontend/packages/core/src/shared/components/tile-selector-tile/tile-selector-tile.component.ts b/src/frontend/packages/core/src/shared/components/tile-selector-tile/tile-selector-tile.component.ts index f4f6a69ec7..5b11d6ac48 100644 --- a/src/frontend/packages/core/src/shared/components/tile-selector-tile/tile-selector-tile.component.ts +++ b/src/frontend/packages/core/src/shared/components/tile-selector-tile/tile-selector-tile.component.ts @@ -1,17 +1,20 @@ -import { Component, Input, EventEmitter, Output } from '@angular/core'; -import { ITileConfig, ITileIconConfig, ITileImgConfig, ITileData } from '../tile/tile-selector.types'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; + +import { ITileConfig, ITileData, ITileGraphic } from '../tile/tile-selector.types'; @Component({ selector: 'app-tile-selector-tile', templateUrl: './tile-selector-tile.component.html', styleUrls: ['./tile-selector-tile.component.scss'] }) -export class TileSelectorTileComponent { +export class TileSelectorTileComponent { @Input() tile: ITileConfig; @Input() active: boolean; + @Input() smaller = false; + @Output() tileSelect = new EventEmitter(); public onClick(tile: ITileConfig) { diff --git a/src/frontend/packages/core/src/shared/components/tile-selector/tile-selector.component.html b/src/frontend/packages/core/src/shared/components/tile-selector/tile-selector.component.html index b3b0112dae..7f05aa5e7d 100644 --- a/src/frontend/packages/core/src/shared/components/tile-selector/tile-selector.component.html +++ b/src/frontend/packages/core/src/shared/components/tile-selector/tile-selector.component.html @@ -11,7 +11,7 @@
- + \ No newline at end of file diff --git a/src/frontend/packages/core/src/shared/components/tile-selector/tile-selector.component.scss b/src/frontend/packages/core/src/shared/components/tile-selector/tile-selector.component.scss index c5a5ce6951..cccdb39707 100644 --- a/src/frontend/packages/core/src/shared/components/tile-selector/tile-selector.component.scss +++ b/src/frontend/packages/core/src/shared/components/tile-selector/tile-selector.component.scss @@ -9,17 +9,31 @@ min-height: 0; min-width: 0; - @include breakpoint(tablet) { + + @include breakpoint(mobileonly) { + // < 600 + grid-template-columns: repeat(1, 1fr); + } + + @include breakpoint(phablet) { + // > 600 grid-column-gap: $bottom-space; grid-row-gap: $bottom-space; grid-template-columns: repeat(2, 1fr); } + @include breakpoint(tablet) { + // > 900 + grid-template-columns: repeat(3, 1fr); + } + @include breakpoint(laptop) { + // > 1200 grid-template-columns: repeat(4, 1fr); } @include breakpoint(desktop) { + // > 1800 grid-template-columns: repeat(5, 1fr); } } diff --git a/src/frontend/packages/core/src/shared/components/tile-selector/tile-selector.component.ts b/src/frontend/packages/core/src/shared/components/tile-selector/tile-selector.component.ts index 18c8c144de..cd7d92f4b2 100644 --- a/src/frontend/packages/core/src/shared/components/tile-selector/tile-selector.component.ts +++ b/src/frontend/packages/core/src/shared/components/tile-selector/tile-selector.component.ts @@ -12,6 +12,7 @@ export class TileSelectorComponent { public pOptions: ITileConfig[] = []; public hiddenOptions: ITileConfig[] = []; public showingMore = false; + @Input() smallerTiles = false; @Input() set options(options: ITileConfig[]) { if (!options) { return; diff --git a/src/frontend/packages/core/src/shared/components/tile/tile-selector.types.ts b/src/frontend/packages/core/src/shared/components/tile/tile-selector.types.ts index d443c03fcb..92f314d6b5 100644 --- a/src/frontend/packages/core/src/shared/components/tile/tile-selector.types.ts +++ b/src/frontend/packages/core/src/shared/components/tile/tile-selector.types.ts @@ -5,6 +5,7 @@ export interface ITileIconConfig { export interface ITileImgConfig { location: string; + transform?: string; } export type ITileGraphic = ITileIconConfig | ITileImgConfig; diff --git a/src/frontend/packages/kubernetes/src/helm/create-release/create-release.component.scss b/src/frontend/packages/kubernetes/src/helm/create-release/create-release.component.scss deleted file mode 100644 index a2974cf6f0..0000000000 --- a/src/frontend/packages/kubernetes/src/helm/create-release/create-release.component.scss +++ /dev/null @@ -1,50 +0,0 @@ -:host { - flex: 1; -} - -.helm-create-release { - &__heading { - align-items: center; - display: flex; - } - - &__title { - flex: 1; - font-size: 14px; - } - &__button { - height: 36px; - } -} - -form { - flex: 1; - - mat-checkbox { - display: flex; - height: 23px; - margin-top: 10px; - } -} - -.overrides { - &__yaml { - background-color: rgba(0, 0, 0, .1); - font-family: 'Source Code Pro', monospace; - height: 400px; - } - - &_form { - max-width: 100%; - } - - &_form-field { - flex: 1; - height: 100%; - width: 100%; - } -} - -form.overrides_form { - max-width: 100%; -} \ No newline at end of file diff --git a/src/frontend/packages/kubernetes/src/helm/create-release/create-release.module.ts b/src/frontend/packages/kubernetes/src/helm/create-release/create-release.module.ts deleted file mode 100644 index f08faf6395..0000000000 --- a/src/frontend/packages/kubernetes/src/helm/create-release/create-release.module.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; - -import { CoreModule } from '../../../../core/src/core/core.module'; -import { SharedModule } from '../../../../core/src/shared/shared.module'; -import { CreateReleaseComponent } from './create-release.component'; - - -@NgModule({ - imports: [ - CommonModule, - CoreModule, - SharedModule - ], - declarations: [ - CreateReleaseComponent, - ] -}) -export class CreateReleaseModule { } diff --git a/src/frontend/packages/kubernetes/src/helm/helm-entity-generator.ts b/src/frontend/packages/kubernetes/src/helm/helm-entity-generator.ts index 0832107627..b58ac7a109 100644 --- a/src/frontend/packages/kubernetes/src/helm/helm-entity-generator.ts +++ b/src/frontend/packages/kubernetes/src/helm/helm-entity-generator.ts @@ -3,13 +3,14 @@ import { of } from 'rxjs'; import { map } from 'rxjs/operators'; import { IListAction } from '../../../core/src/shared/components/list/list.component.types'; +import { AppState } from '../../../store/src/app-state'; import { StratosBaseCatalogEntity, StratosCatalogEndpointEntity, StratosCatalogEntity, } from '../../../store/src/entity-catalog/entity-catalog-entity/entity-catalog-entity'; import { StratosEndpointExtensionDefinition } from '../../../store/src/entity-catalog/entity-catalog.types'; -import { AppState, EndpointModel } from '../../../store/src/public-api'; +import { EndpointModel } from '../../../store/src/public-api'; import { IFavoriteMetadata } from '../../../store/src/types/user-favorites.types'; import { helmEntityCatalog } from './helm-entity-catalog'; import { diff --git a/src/frontend/packages/kubernetes/src/helm/helm-hub-registration/helm-hub-registration.component.spec.ts b/src/frontend/packages/kubernetes/src/helm/helm-hub-registration/helm-hub-registration.component.spec.ts index 69893fc648..df9c494821 100644 --- a/src/frontend/packages/kubernetes/src/helm/helm-hub-registration/helm-hub-registration.component.spec.ts +++ b/src/frontend/packages/kubernetes/src/helm/helm-hub-registration/helm-hub-registration.component.spec.ts @@ -1,8 +1,8 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { EndpointsService } from '../../../../core/src/core/endpoints.service'; -import { UserService } from '../../../../core/src/core/user.service'; -import { BaseTestModules } from '../../../../core/test-framework/core-test.helper'; +import { EndpointsService } from '../../../../../core/src/core/endpoints.service'; +import { UserService } from '../../../../../core/src/core/user.service'; +import { BaseTestModules } from '../../../../../core/test-framework/core-test.helper'; import { HelmHubRegistrationComponent } from './helm-hub-registration.component'; describe('HelmHubRegistrationComponent', () => { diff --git a/src/frontend/packages/kubernetes/src/helm/helm-hub-registration/helm-hub-registration.component.ts b/src/frontend/packages/kubernetes/src/helm/helm-hub-registration/helm-hub-registration.component.ts index 45adcc2cdc..2b1a2ceeb3 100644 --- a/src/frontend/packages/kubernetes/src/helm/helm-hub-registration/helm-hub-registration.component.ts +++ b/src/frontend/packages/kubernetes/src/helm/helm-hub-registration/helm-hub-registration.component.ts @@ -1,9 +1,9 @@ import { Component } from '@angular/core'; import { filter, map, pairwise } from 'rxjs/operators'; -import { StepOnNextFunction } from '../../../../core/src/shared/components/stepper/step/step.component'; -import { ActionState } from '../../../../store/src/reducers/api-request-reducer/types'; -import { stratosEntityCatalog } from '../../../../store/src/stratos-entity-catalog'; +import { StepOnNextFunction } from '../../../../../core/src/shared/components/stepper/step/step.component'; +import { ActionState } from '../../../../../store/src/reducers/api-request-reducer/types'; +import { stratosEntityCatalog } from '../../../../../store/src/stratos-entity-catalog'; import { HELM_ENDPOINT_TYPE, HELM_HUB_ENDPOINT_TYPE } from '../helm-entity-factory'; @Component({ diff --git a/src/frontend/packages/kubernetes/src/helm/helm.routing.ts b/src/frontend/packages/kubernetes/src/helm/helm.routing.ts index 37dc491734..b523106013 100644 --- a/src/frontend/packages/kubernetes/src/helm/helm.routing.ts +++ b/src/frontend/packages/kubernetes/src/helm/helm.routing.ts @@ -2,7 +2,6 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { MonocularChartViewComponent } from './chart-view/monocular.component'; -import { CreateReleaseComponent } from './create-release/create-release.component'; import { MonocularTabBaseComponent } from './monocular-tab-base/monocular-tab-base.component'; import { CatalogTabComponent } from './tabs/catalog-tab/catalog-tab.component'; @@ -18,8 +17,6 @@ const monocular: Routes = [ }, { pathMatch: 'full', path: 'charts/:endpoint/:repo/:chartName/:version', component: MonocularChartViewComponent }, { path: 'charts/:endpoint/:repo/:chartName', component: MonocularChartViewComponent }, - { pathMatch: 'full', path: 'install/:endpoint/:repo/:chartName/:version', component: CreateReleaseComponent }, - { path: 'install/:endpoint/:repo/:chartName', component: CreateReleaseComponent }, ]; @NgModule({ diff --git a/src/frontend/packages/kubernetes/src/helm/monocular/chart-details/chart-details-readme/chart-details-readme.component.ts b/src/frontend/packages/kubernetes/src/helm/monocular/chart-details/chart-details-readme/chart-details-readme.component.ts index 182e652b31..171a260696 100644 --- a/src/frontend/packages/kubernetes/src/helm/monocular/chart-details/chart-details-readme/chart-details-readme.component.ts +++ b/src/frontend/packages/kubernetes/src/helm/monocular/chart-details/chart-details-readme/chart-details-readme.component.ts @@ -19,19 +19,22 @@ export class ChartDetailsReadmeComponent { } } - public loading = true; + public loading = false; public readmeContent$: Observable; private renderer = new markdown.Renderer(); + private loadingDelay: any; constructor(private chartsService: ChartsService) { this.renderer.link = (href, title, text) => `${text}`; this.renderer.code = (text: string) => `${text}`; + this.loadingDelay = setTimeout(() => this.loading = true, 100); } // TODO: See #150 - This should not require loading the specific version and then the readme private getReadme(currentVersion: ChartVersion): Observable { return this.chartsService.getChartReadme(currentVersion).pipe( map(resp => { + clearTimeout(this.loadingDelay); this.loading = false; return markdown(resp, { renderer: this.renderer diff --git a/src/frontend/packages/kubernetes/src/helm/monocular/chart-details/chart-details-usage/chart-details-usage.component.ts b/src/frontend/packages/kubernetes/src/helm/monocular/chart-details/chart-details-usage/chart-details-usage.component.ts index ee7fc0ba45..425fe87746 100644 --- a/src/frontend/packages/kubernetes/src/helm/monocular/chart-details/chart-details-usage/chart-details-usage.component.ts +++ b/src/frontend/packages/kubernetes/src/helm/monocular/chart-details/chart-details-usage/chart-details-usage.component.ts @@ -39,7 +39,7 @@ export class ChartDetailsUsageComponent implements OnInit { } get installUrl(): string { - return `/monocular/install/${getMonocularEndpoint(this.route, this.chart)}/${this.chart.id}/${this.currentVersion}`; + return `/workloads/install/${getMonocularEndpoint(this.route, this.chart)}/${this.chart.id}/${this.currentVersion}`; } } diff --git a/src/frontend/packages/kubernetes/src/helm/monocular/chart-details/chart-details.component.html b/src/frontend/packages/kubernetes/src/helm/monocular/chart-details/chart-details.component.html index fae3d4f7d5..31c68dc996 100644 --- a/src/frontend/packages/kubernetes/src/helm/monocular/chart-details/chart-details.component.html +++ b/src/frontend/packages/kubernetes/src/helm/monocular/chart-details/chart-details.component.html @@ -1,5 +1,5 @@
- +

Sorry, we couldn't find the chart

diff --git a/src/frontend/packages/kubernetes/src/helm/monocular/chart-details/chart-details.component.ts b/src/frontend/packages/kubernetes/src/helm/monocular/chart-details/chart-details.component.ts index 76262da43a..5d63a62325 100644 --- a/src/frontend/packages/kubernetes/src/helm/monocular/chart-details/chart-details.component.ts +++ b/src/frontend/packages/kubernetes/src/helm/monocular/chart-details/chart-details.component.ts @@ -16,16 +16,21 @@ import { getMonocularEndpoint } from '../stratos-monocular.helper'; export class ChartDetailsComponent implements OnInit { /* This resource will be different, probably ChartVersion */ chart: Chart; - loading = true; + loading = false; + initing = true; currentVersion: ChartVersion; iconUrl: string; titleVersion: string; + loadingDelay: any; + constructor( private route: ActivatedRoute, private chartsService: ChartsService, private config: ConfigService, - ) { } + ) { + this.loadingDelay = setTimeout(() => this.loading = true, 100); + } ngOnInit() { this.route.params.forEach((params: Params) => { @@ -34,7 +39,9 @@ export class ChartDetailsComponent implements OnInit { if (!!chartName) { this.chartsService.getChart(repo, chartName).pipe(first()).subscribe(chart => { + clearTimeout(this.loadingDelay); this.loading = false; + this.initing = false; this.chart = chart; const version = params.version || this.chart.relationships.latestChartVersion.data.version; this.chartsService.getVersion(repo, chartName, version).pipe(first()) @@ -42,8 +49,8 @@ export class ChartDetailsComponent implements OnInit { this.currentVersion = chartVersion; this.titleVersion = this.currentVersion.attributes.app_version || ''; this.updateMetaTags(); + this.iconUrl = this.chartsService.getChartIconURL(this.chart, chartVersion); }); - this.iconUrl = this.chartsService.getChartIconURL(this.chart); }); } }); diff --git a/src/frontend/packages/kubernetes/src/helm/monocular/chart-item/chart-item.component.scss b/src/frontend/packages/kubernetes/src/helm/monocular/chart-item/chart-item.component.scss index e42c43a10e..813c0b946e 100644 --- a/src/frontend/packages/kubernetes/src/helm/monocular/chart-item/chart-item.component.scss +++ b/src/frontend/packages/kubernetes/src/helm/monocular/chart-item/chart-item.component.scss @@ -9,6 +9,7 @@ $chart-item-logo-width: 80px; &-logo { max-height: 80%; + max-width: 80%; } &-info { diff --git a/src/frontend/packages/kubernetes/src/helm/monocular/monocular.module.ts b/src/frontend/packages/kubernetes/src/helm/monocular/monocular.module.ts index 450e9f8864..240e637d44 100644 --- a/src/frontend/packages/kubernetes/src/helm/monocular/monocular.module.ts +++ b/src/frontend/packages/kubernetes/src/helm/monocular/monocular.module.ts @@ -1,7 +1,7 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; -import { CoreModule, SharedModule } from '../../../../core/src/public-api'; +import { CoreModule, SharedModule } from '../../../../../core/src/public-api'; import { ChartDetailsInfoComponent } from './chart-details/chart-details-info/chart-details-info.component'; import { ChartDetailsReadmeComponent } from './chart-details/chart-details-readme/chart-details-readme.component'; import { ChartDetailsUsageComponent } from './chart-details/chart-details-usage/chart-details-usage.component'; diff --git a/src/frontend/packages/kubernetes/src/helm/monocular/shared/services/charts.service.ts b/src/frontend/packages/kubernetes/src/helm/monocular/shared/services/charts.service.ts index 8b62546ae1..a215e262d7 100644 --- a/src/frontend/packages/kubernetes/src/helm/monocular/shared/services/charts.service.ts +++ b/src/frontend/packages/kubernetes/src/helm/monocular/shared/services/charts.service.ts @@ -123,9 +123,9 @@ export class ChartsService { * @return An observable that will be a chartReadme */ getChartReadme(chartVersion: ChartVersion): Observable { - return this.http.get(`${this.hostname}${chartVersion.attributes.readme}`, { + return chartVersion.attributes.readme ? this.http.get(`${this.hostname}${chartVersion.attributes.readme}`, { responseType: 'text' - }); + }) : of('

No Readme available for this chart

'); } /** @@ -173,9 +173,11 @@ export class ChartsService { * * @param chart Chart object */ - getChartIconURL(chart: Chart): string { - if (chart.attributes.icon) { - return this.updateStratosUrl(chart, `${this.hostname}${chart.attributes.icon}`); + getChartIconURL(chart: Chart, version?: ChartVersion): string { + let url = version ? version.relationships.chart.data.icon : null; + url = url || chart.attributes.icon; + if (url) { + return this.updateStratosUrl(chart, `${this.hostname}${url}`); } else { return '/core/assets/custom/placeholder.png'; } diff --git a/src/frontend/packages/kubernetes/src/helm/store/helm.effects.ts b/src/frontend/packages/kubernetes/src/helm/store/helm.effects.ts index d0c569bb63..7a91c6a055 100644 --- a/src/frontend/packages/kubernetes/src/helm/store/helm.effects.ts +++ b/src/frontend/packages/kubernetes/src/helm/store/helm.effects.ts @@ -4,15 +4,29 @@ import { MatSnackBar } from '@angular/material/snack-bar'; import { Actions, Effect, ofType } from '@ngrx/effects'; import { Action, Store } from '@ngrx/store'; import { combineLatest, Observable, of } from 'rxjs'; -import { catchError, flatMap, map, mergeMap, withLatestFrom } from 'rxjs/operators'; +import { catchError, first, flatMap, map, mergeMap, withLatestFrom } from 'rxjs/operators'; import { environment } from '../../../../core/src/environments/environment'; -import { GET_ENDPOINTS_SUCCESS, GetAllEndpointsSuccess } from '../../../../store/src/actions/endpoint.actions'; -import { ClearPaginationOfType } from '../../../../store/src/actions/pagination.actions'; +import { + EndpointActionComplete, + GET_ENDPOINTS_SUCCESS, + GetAllEndpointsSuccess, + REGISTER_ENDPOINTS_SUCCESS, + UNREGISTER_ENDPOINTS_SUCCESS, + UnregisterEndpoint, +} from '../../../../store/src/actions/endpoint.actions'; +import { ClearPaginationOfType, ResetPaginationOfType } from '../../../../store/src/actions/pagination.actions'; import { isJetstreamError } from '../../../../store/src/jetstream'; -import { AppState, entityCatalog, NormalizedResponse, WrapperRequestActionSuccess } from '../../../../store/src/public-api'; +import { + AppState, + EndpointModel, + entityCatalog, + NormalizedResponse, + WrapperRequestActionSuccess, +} from '../../../../store/src/public-api'; import { ApiRequestTypes } from '../../../../store/src/reducers/api-request-reducer/request-helpers'; import { endpointOfTypeSelector } from '../../../../store/src/selectors/endpoint.selectors'; +import { stratosEntityCatalog } from '../../../../store/src/stratos-entity-catalog'; import { EntityRequestAction, StartRequestAction, @@ -215,7 +229,9 @@ export class HelmEffects { }, base); return processedData; }, [], { - 'x-cap-cnsi-list': action.monocularEndpoint !== stratosMonocularEndpointGuid ? action.monocularEndpoint : '' + 'x-cap-cnsi-list': action.monocularEndpoint && action.monocularEndpoint !== stratosMonocularEndpointGuid ? + action.monocularEndpoint : + '' }); }) ); @@ -271,6 +287,38 @@ export class HelmEffects { }) ); + @Effect() + endpointUnregister$ = this.actions$.pipe( + ofType(UNREGISTER_ENDPOINTS_SUCCESS), + flatMap(action => stratosEntityCatalog.endpoint.store.getEntityMonitor(action.guid).entity$.pipe( + first(), + mergeMap(endpoint => { + if (endpoint.cnsi_type !== HELM_ENDPOINT_TYPE) { + return []; + } + return [ + new ResetPaginationOfType(helmEntityCatalog.chart.getSchema()), + new ResetPaginationOfType(helmEntityCatalog.chartVersions.getSchema()), + new ResetPaginationOfType(helmEntityCatalog.version.getSchema()), + ]; + }) + )) + ); + + @Effect() + registerEndpoint$ = this.actions$.pipe( + ofType(REGISTER_ENDPOINTS_SUCCESS), + flatMap(action => { + const endpoint: EndpointModel = action.endpoint as EndpointModel; + if (endpoint && endpoint.cnsi_type === HELM_ENDPOINT_TYPE && endpoint.sub_type === HELM_HUB_ENDPOINT_TYPE) { + return [ + new ResetPaginationOfType(helmEntityCatalog.chart.getSchema()), + ]; + } + return []; + }) + ); + private static createHelmErrorMessage(err: any): string { if (err) { if (err.error && err.error.message) { diff --git a/src/frontend/packages/kubernetes/src/helm/store/helm.types.ts b/src/frontend/packages/kubernetes/src/helm/store/helm.types.ts index 7c9c4afa9c..d36f381907 100644 --- a/src/frontend/packages/kubernetes/src/helm/store/helm.types.ts +++ b/src/frontend/packages/kubernetes/src/helm/store/helm.types.ts @@ -45,35 +45,27 @@ export enum HelmStatus { Pending_Rollback = 8 } -export interface HelmInstallValues { +export interface HelmChartReference { + name: string; + repo: string; + version: string; +} + +export interface HelmUpgradeInstallValues { + monocularEndpoint: string; + values: string; + chart: HelmChartReference; + chartUrl: string; +} + +export interface HelmInstallValues extends HelmUpgradeInstallValues { endpoint: string; monocularEndpoint: string; releaseName: string; releaseNamespace: string; - values: string; - chart: { - name: string; - repo: string; - version: string; - }; } -export interface HelmUpgradeValues { - values: string; - chart: { - name: string; - repo: string; - version: string; - }; - restartPods?: boolean; - monocularEndpoint?: string; -} -export interface HelmUpgradeValues { - chart: { - name: string; - repo: string; - version: string; - }; +export interface HelmUpgradeValues extends HelmUpgradeInstallValues { restartPods?: boolean; } diff --git a/src/frontend/packages/kubernetes/src/kubernetes/workloads/chart-values-editor/chart-values-editor.component.html b/src/frontend/packages/kubernetes/src/kubernetes/workloads/chart-values-editor/chart-values-editor.component.html new file mode 100644 index 0000000000..2baa79aaf0 --- /dev/null +++ b/src/frontend/packages/kubernetes/src/kubernetes/workloads/chart-values-editor/chart-values-editor.component.html @@ -0,0 +1,63 @@ + +
+
+
Loading ...
+ +
+
+ + + Form + YAML + + + +
YAML Editor
+
+ +
+ +
+ + + + + + + + + +
+ +
+ + format_list_numbered + + + map + +
+ +
+
+
+
+
+ warning +
+
+
Error - YAML is not valid
+
Use the YAML editor to correct it so the values can be loaded into the form
+
+
+
+ +
+
+ +
+ +
diff --git a/src/frontend/packages/kubernetes/src/kubernetes/workloads/chart-values-editor/chart-values-editor.component.scss b/src/frontend/packages/kubernetes/src/kubernetes/workloads/chart-values-editor/chart-values-editor.component.scss new file mode 100644 index 0000000000..8e28d24ff1 --- /dev/null +++ b/src/frontend/packages/kubernetes/src/kubernetes/workloads/chart-values-editor/chart-values-editor.component.scss @@ -0,0 +1,104 @@ +:host { + display: flex; + width: 100%; +} +.editor { + + $toolbar-height: 40px; + + &-card { + flex: 1; + padding: 0; + } + + &-title { + font-size: 14px; + } + + &-form { + display: block; + height: calc(100% - #{$toolbar-height}); + overflow-y: scroll; + padding: 20px; + width: 100%; + + &.editor-hidden { + display: none; + } + } + + &-loading { + align-items: center; + display: flex; + height: 100%; + justify-content: center; + position: absolute; + width: 100%; + z-index: 100; + + &__msg { + text-align: center; + } + &__progress-bar { + margin-top: 10px; + min-width: 120px; + } + } + + &-yaml-error { + align-items: center; + display: flex; + height: calc(100% - #{$toolbar-height}); + justify-content: center; + margin: -20px; + opacity: 0.7; + position: absolute; + width: 100%; + z-index: 100; + + &__msg { + display: flex; + flex-direction: row; + } + &__text { + line-height: 24px; + } + &__icon { + font-size: 32px; + height: 32px; + margin-right: 12px; + width: 32px; + } + } + + &-toolbar-buttons { + display: flex; + mat-button-toggle { + margin-left: 8px; + } + } + + &-spacer { + flex: 1 1 auto; + } + + &-monaco { + display: block; + + &.editor-hidden { + display: none; + } + } + + &-monaco-edit { + position: absolute; + } + + &-menu-divider { + margin: 4px 0; + } +} + +.mat-card.editor-card>:first-child { + margin: 0; +} \ No newline at end of file diff --git a/src/frontend/packages/kubernetes/src/kubernetes/workloads/chart-values-editor/chart-values-editor.component.spec.ts b/src/frontend/packages/kubernetes/src/kubernetes/workloads/chart-values-editor/chart-values-editor.component.spec.ts new file mode 100644 index 0000000000..8dc9906951 --- /dev/null +++ b/src/frontend/packages/kubernetes/src/kubernetes/workloads/chart-values-editor/chart-values-editor.component.spec.ts @@ -0,0 +1,41 @@ +import { HttpClient, HttpClientModule, HttpHandler } from '@angular/common/http'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { createBasicStoreModule } from '@stratosui/store/testing'; + +import { ConfirmationDialogService } from '../../../../../../core/src/shared/components/confirmation-dialog.service'; +import { MDAppModule } from './../../../../../../core/src/core/md.module'; +import { ChartValuesEditorComponent } from './chart-values-editor.component'; + +describe('ChartValuesEditorComponent', () => { + let component: ChartValuesEditorComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ChartValuesEditorComponent ], + providers: [ + HttpClient, + HttpHandler, + ConfirmationDialogService, + ], + imports: [ + MDAppModule, + HttpClientModule, + HttpClientTestingModule, + createBasicStoreModule(), + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ChartValuesEditorComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/packages/kubernetes/src/kubernetes/workloads/chart-values-editor/chart-values-editor.component.theme.scss b/src/frontend/packages/kubernetes/src/kubernetes/workloads/chart-values-editor/chart-values-editor.component.theme.scss new file mode 100644 index 0000000000..fa694c22cd --- /dev/null +++ b/src/frontend/packages/kubernetes/src/kubernetes/workloads/chart-values-editor/chart-values-editor.component.theme.scss @@ -0,0 +1,65 @@ +@mixin app-chart-values-editor-theme($theme, $app-theme) { + + $background: map-get($theme, background); + $foreground: map-get($theme, foreground); + + $app-background: map-get($app-theme, app-background-color); + + // Fixes some layout issues with Angular Json Schema Form due to use of Angular flex-layout + + // Also tweaks sizing and spacing of elements + + $vert-padding: 10px; + $toolbar-icon-size: 16px; + $toolbar-item-height: 24px; + $toolbar-item-font-size: 12px; + + // We discourage use of !important, but this is only way to override + // the angular flex settings in the ajsf library - otherwise the layout is wrong + .form-flex-column { + flex-flow: column !important; + } + + .legend { + font-size: 14px; + padding: $vert-padding 0; + } + + .editor-loading, .editor-yaml-error { + background-color: $app-background; + } + + // Make controls in the editor toolbar smaller + .editor-toolbar { + height: 40px; + + .mat-button-toggle-button { + display: flex; + } + .mat-button-toggle-label-content { + font-size: $toolbar-item-font-size; + line-height: $toolbar-item-height; + padding: 0 8px; + + .mat-icon { + font-size: $toolbar-icon-size; + height: $toolbar-icon-size; + width: $toolbar-icon-size; + } + } + .mat-button { + font-size: $toolbar-item-font-size; + line-height: $toolbar-item-height; + padding: 0 12px; + } + + } + + // Override hover color for context menu to align with Stratos theme + // Monaco doesn't seem to expose this as a theme colour + .monaco-editor .monaco-menu .action-item.focused a.action-menu-item { + background: mat-color($background, 'hover') !important; + color: mat-color($foreground, 'text') !important; + } + +} diff --git a/src/frontend/packages/kubernetes/src/kubernetes/workloads/chart-values-editor/chart-values-editor.component.ts b/src/frontend/packages/kubernetes/src/kubernetes/workloads/chart-values-editor/chart-values-editor.component.ts new file mode 100644 index 0000000000..ff0ace0e14 --- /dev/null +++ b/src/frontend/packages/kubernetes/src/kubernetes/workloads/chart-values-editor/chart-values-editor.component.ts @@ -0,0 +1,430 @@ +import { HttpClient } from '@angular/common/http'; +import { AfterViewInit, Component, ElementRef, Input, OnDestroy, OnInit, Renderer2, ViewChild } from '@angular/core'; +import { JsonSchemaFormComponent } from '@cfstratos/ajsf-core'; +import * as yaml from 'js-yaml'; +import { BehaviorSubject, combineLatest, fromEvent, Observable, of, Subscription } from 'rxjs'; +import { catchError, debounceTime, filter, map, startWith, tap } from 'rxjs/operators'; + +import { ConfirmationDialogConfig } from '../../../../../../core/src/shared/components/confirmation-dialog.config'; +import { ConfirmationDialogService } from '../../../../../../core/src/shared/components/confirmation-dialog.service'; +import { ThemeService } from './../../../../../../store/src/theme.service'; +import { diffObjects } from './diffvalues'; +import { generateJsonSchemaFromObject } from './json-schema-generator'; +import { mergeObjects } from './merge'; + + +export interface ChartValuesConfig { + + // URL of the JSON Schema for the chart values + schemaUrl: string; + + // URL of the Chart Values + valuesUrl: string; + + // Values for the current release (optional) + releaseValues?: string; +} + +// Height of the toolbar that sits above the editor conmponent +const TOOLBAR_HEIGHT = 40; + +// Editor modes - can be either using the form or the code editor +enum EditorMode { + CodeEditor = 'editor', + JSonSchemaForm = 'form', +} + +@Component({ + selector: 'app-chart-values-editor', + templateUrl: './chart-values-editor.component.html', + styleUrls: ['./chart-values-editor.component.scss'] +}) +export class ChartValuesEditorComponent implements OnInit, OnDestroy, AfterViewInit { + + @Input() set config(config: ChartValuesConfig) { + if (!!config) { + this.schemaUrl = config.schemaUrl; + this.valuesUrl = config.valuesUrl; + this.releaseValues = config.releaseValues; + this.init(); + } + } + + schemaUrl: string; + valuesUrl: string; + releaseValues: string; + + // Model for the editor - we set this once when the YAML support has been loaded + public model; + + // Editor mode - either 'editor' for the Monaco Code Editor or 'form' for the JSON Schema Form editor + public mode: EditorMode = EditorMode.CodeEditor; + + // Content shown in the code editor + public code = ''; + + // JSON Schema + public schema: any; + + public hasSchema = false; + + // Data shown in the form on load + public initialFormData = {}; + + // Data updated in the form as the user changes it + public formData = {}; + + // Is the YAML in the code editor invalid? + public yamlError = false; + + // Monaco Code Editor settings + public minimap = true; + public lineNumbers = true; + + // Chart Values - as both raw text (keeping comments) and parsed JSON + public chartValuesYaml: string; + public chartValues: any; + + // Default Monaco options + public editorOptions = { + automaticLayout: false, // We will resize the editor to fit the available space + contextmenu: false, // Turn off the right-click context menu + tabSize: 2, + }; + + // Monaco editor + public editor: any; + + // Observable - are we still loading resources? + public loading$: Observable; + + // Observable for tracking if the Monaco editor has loaded + private monacoLoaded$ = new BehaviorSubject(false); + + private resizeSub: Subscription; + private themeSub: Subscription; + + // Track whether the user changes the code in the text editor + private codeOnEnter: string; + + // Reference to the editor, so we can adjust its size to fit + @ViewChild('monacoEditor', { read: ElementRef }) monacoEditor: ElementRef; + + @ViewChild('schemaForm') schemaForm: JsonSchemaFormComponent; + + // Confirmation dialog - copy values + overwriteValuesConfirmation = new ConfirmationDialogConfig( + 'Overwrite Values?', + 'Are you sure you want to replace your values with those from values.yaml?', + 'Overwrite' + ); + + // Confirmation dialog - copy release values + overwriteReleaseValuesConfirmation = new ConfirmationDialogConfig( + 'Overwrite Values?', + 'Are you sure you want to replace your values with those from the release?', + 'Overwrite' + ); + + // Confirmation dialog - diff values + overwriteDiffValuesConfirmation = new ConfirmationDialogConfig( + 'Overwrite Values?', + 'Are you sure you want to replace your values with the diff with values.yaml?', + 'Overwrite' + ); + + // Confirmation dialog - clear values + clearValuesConfirmation = new ConfirmationDialogConfig( + 'Clear Values?', + 'Are you sure you want to clear the form values?', + 'Overwrite' + ); + + constructor( + private elRef: ElementRef, + private renderer: Renderer2, + private httpClient: HttpClient, + private themeService: ThemeService, + private confirmDialog: ConfirmationDialogService, + ) { } + + ngOnInit(): void { + // Listen for window resize and resize the editor when this happens + this.resizeSub = fromEvent(window, 'resize').pipe(debounceTime(150)).subscribe(event => this.resize()); + } + + private init() { + // Observabled for loading schema and values for the Chart + const schema$ = this.httpClient.get(this.schemaUrl).pipe(catchError(e => of(null))); + const values$: Observable = this.httpClient.get(this.valuesUrl, { responseType: 'text' }).pipe( + catchError(e => of(null)) + ); + + // We need the schame, value sand the monaco editor to be all loaded before we're ready + this.loading$ = combineLatest(schema$, values$, this.monacoLoaded$).pipe( + filter(([schema, values, loaded]) => schema !== undefined && values !== undefined && loaded), + tap(([schema, values, loaded]) => { + this.schema = schema; + if (values !== null) { + this.chartValuesYaml = values as string; + this.chartValues = yaml.safeLoad(values); + // Set the form to the chart values initially, so if the user does nothing, they get the defaults + this.initialFormData = this.chartValues; + } + // Default to form if there is a schema + if (schema !== null) { + this.hasSchema = true; + this.mode = EditorMode.JSonSchemaForm; + // Register schema with the Monaco editor + this.registerSchema(this.schema); + } else { + // No Schema, so register an auto-generated schema from the Chart's values + this.registerSchema(generateJsonSchemaFromObject('Generated Schema', this.chartValues)); + + // Inherit the previous values if available (upgrade) + if (this.releaseValues) { + this.code = yaml.safeDump(this.releaseValues); + } + } + this.updateModel(); + }), + map(([schema, values, loaded]) => !loaded), + startWith(true) + ); + } + + ngAfterViewInit(): void { + this.resizeEditor(); + } + + ngOnDestroy(): void { + if (this.resizeSub) { + this.resizeSub.unsubscribe(); + } + if (this.themeSub) { + this.themeSub.unsubscribe(); + } + } + + // Toggle editor minimap on/off + toggleMinimap() { + this.minimap = !this.minimap; + this.editor.updateOptions({ minimap: { enabled: this.minimap } }); + } + + // Toggle editor line numbers on/off + toggleLineNumbers() { + this.lineNumbers = !this.lineNumbers; + this.editor.updateOptions({ lineNumbers: this.lineNumbers ? 'on' : 'off' }); + } + + // Store the update form data when the form changes + // AJSF two-way binding seems to cause issues + formChanged(data: any) { + this.formData = data; + } + + // The edit mode has changed (form or editor) + editModeChanged(mode) { + this.mode = mode.value; + + if (this.mode === EditorMode.CodeEditor) { + // Form -> Editor + // Only copy if there is not an error - otherwise keep the invalid yaml from the editor that needs fixing + if (!this.yamlError) { + const codeYaml = yaml.safeLoad(this.code || '{}'); + const data = mergeObjects(codeYaml, this.formData); + this.code = this.getDiff(data); + this.codeOnEnter = this.code; + } + + // Need to resize the editor, as it will be freshly shown + this.resizeEditor(); + } else { + // Editor -> Form + // Try and parse the YAML - if we can't this is an error, so we can't edit this back in the form + try { + if (this.codeOnEnter === this.code) { + // Code did not change + return; + } + + // Parse as json + const json = yaml.safeLoad(this.code || '{}'); + // Must be an object, otherwise it was not valid + if (typeof (json) !== 'object') { + throw new Error('Invalid YAML'); + } + this.yamlError = false; + const data = mergeObjects(this.formData, json); + this.initialFormData = data; + this.formData = data; + } catch (e) { + // The yaml in the code editor is invalid, so we can't marshal it back to json for the from editor + this.yamlError = true; + } + } + } + + // Called once the Monaco editor has loaded and then each time the model is update + // Store a reference to the editor and ensure the editor theme is synchronized with the Stratos theme + onMonacoInit(editor) { + this.editor = editor; + this.resize(); + + // Only load the YAML support once - when we set the model, onMonacoInit will et + if (this.model) { + return; + } + + // Load the YAML Language support - require is available as it will have been loaded by the Monaco vs loader + const req = (window as any).require; + req(['vs/language/yaml/monaco.contribution'], () => { + // Set the model now that YAML support is loaded - this will update the editor correctly + this.updateModel(); + this.monacoLoaded$.next(true); + }); + + // Watch for theme changes - set light/dark theme in the monaco editor as the Stratos theme changes + this.themeSub = this.themeService.getTheme().subscribe(theme => { + const monaco = (window as any).monaco; + const monacoTheme = (theme.styleName === 'dark-theme') ? 'vs-dark' : 'vs'; + monaco.editor.setTheme(monacoTheme); + }); + } + + private updateModel() { + this.model = { + language: 'yaml', + uri: this.getSchemaUri() + }; + } + + // Delayed resize of editor to fit + resizeEditor() { + setTimeout(() => this.resize(), 1); + } + + // Resize editor to fit + resize() { + // Return if resize before editor has been set + if (!this.editor) { + return; + } + + // Get width and height of the host element + const w = this.elRef.nativeElement.offsetWidth; + let h = this.elRef.nativeElement.offsetHeight; + + // Check if host element not visible (does not have a size) + if ((w === 0) && (h === 0)) { + return; + } + + // Remove height of toolbar (since this is incluced in the height of the host element) + h = h - TOOLBAR_HEIGHT; + + // Set the Monaco editor to the same size as the container + this.renderer.setStyle(this.monacoEditor.nativeElement, 'width', `${w}px`); + this.renderer.setStyle(this.monacoEditor.nativeElement, 'height', `${h}px`); + + // Ask Monaco to layout again with its new size + this.editor.layout(); + } + + // Get an absolute URI for the Schema - it is not fetched, just used as a reference + // schemaUrl is a relative URL - e.g. /p1/v1/chartsvc.... + getSchemaUri(): string { + return `https://stratos.app/schemas${this.schemaUrl}`; + } + + // Register the schema with the Monaco editor + // Reference: https://github.com/pengx17/monaco-yaml/blob/master/examples/umd/index.html#L69 + registerSchema(schema: any) { + const monaco = (window as any).monaco; + monaco.languages.yaml.yamlDefaults.setDiagnosticsOptions({ + enableSchemaRequest: true, + hover: true, + completion: true, + validate: true, + format: true, + schemas: [ + { + uri: this.getSchemaUri(), + fileMatch: [this.getSchemaUri()], + schema + } + ] + }); + } + + public getValues(): object { + return (this.mode === EditorMode.JSonSchemaForm) ? this.formData : yaml.safeLoad(this.code); + } + + public copyValues() { + const confirm = this.mode === EditorMode.JSonSchemaForm || this.mode === EditorMode.CodeEditor && this.code.length > 0; + if (confirm) { + this.confirmDialog.open(this.overwriteValuesConfirmation, () => { + this.doCopyValues(); + }); + } else { + this.doCopyValues(); + } + } + + // Copy the chart values into either the form or the code editor, depending on the current mode + private doCopyValues() { + if (this.mode === EditorMode.JSonSchemaForm) { + this.initialFormData = this.chartValues; + } else { + // Use the raw Yaml, so we keep comments and formatting + this.code = this.chartValuesYaml; + } + } + + public copyReleaseValues() { + const confirm = this.mode === EditorMode.JSonSchemaForm || this.mode === EditorMode.CodeEditor && this.code.length > 0; + if (confirm) { + this.confirmDialog.open(this.overwriteReleaseValuesConfirmation, () => { + this.doCopyReleaseValues(); + }); + } else { + this.doCopyReleaseValues(); + } + } + + // Copy the release values into either the form or the code editor, depending on the current mode + private doCopyReleaseValues() { + if (this.mode === EditorMode.JSonSchemaForm) { + this.initialFormData = this.releaseValues; + } else { + this.code = yaml.safeDump(this.releaseValues); + } + } + + // Reset the form values and the code + clearFormValues() { + this.confirmDialog.open(this.clearValuesConfirmation, () => { + this.initialFormData = {}; + this.code = ''; + this.codeOnEnter = ''; + }); + } + + // Update the code editor to only show the YAML that contains the differences with the values.yaml + diff() { + this.confirmDialog.open(this.overwriteDiffValuesConfirmation, () => { + const userValues = yaml.safeLoad(this.code); + this.code = this.getDiff(userValues); + }); + } + + getDiff(userValues: any): string { + let code = yaml.safeDump(diffObjects(userValues, this.chartValues)); + if (code.trim() === '{}') { + code = ''; + } + return code; + } +} diff --git a/src/frontend/packages/kubernetes/src/kubernetes/workloads/chart-values-editor/diffvalues.ts b/src/frontend/packages/kubernetes/src/kubernetes/workloads/chart-values-editor/diffvalues.ts new file mode 100644 index 0000000000..d4a27c6e8d --- /dev/null +++ b/src/frontend/packages/kubernetes/src/kubernetes/workloads/chart-values-editor/diffvalues.ts @@ -0,0 +1,74 @@ +// Helper for diffing user values and chart values + +function arraysAreEqual(a1: any[], a2: any[]): boolean { + if (a1.length !== a2.length) { + return false + } + + // Compare each item in the array + for (let i=0; i { + if (typeof(src[key]) !== typeof(dest[key])) { + return false; + } else if(src[key] === null && dest[key] === null) { + return true; + } else if (Array.isArray(src[key]) && !arraysAreEqual(src[key], dest[key])) { + return false; + } else if (typeof(src[key]) === 'object' && !objectsAreEqual(src[key], dest[key])) { + return false; + } + }); + + return true; +} + +// NOTE: This is a one-way diff only +// diffObjects is main export - diffs two objects and returns only the diffrence +export function diffObjects(src: any, dest: any): any { + if (!src) { + return {}; + } + + Object.keys(src).forEach(key => { + if (typeof(src[key]) === typeof(dest[key])) { + if(src[key] === null && dest[key] === null) { + delete src[key]; + } else if(Array.isArray(src[key])) { + // Array + if (arraysAreEqual(src[key], dest[key])) { + delete src[key]; + } + } else if (typeof(src[key]) === 'object') { + // Object + diffObjects(src[key], dest[key]); + if (src[key] && Object.keys(src[key]).length === 0) { + delete src[key]; + } + } else if (src[key] === dest[key]) { + // Value + delete src[key]; + } + } + }); + return src; +} \ No newline at end of file diff --git a/src/frontend/packages/kubernetes/src/kubernetes/workloads/chart-values-editor/json-schema-generator.ts b/src/frontend/packages/kubernetes/src/kubernetes/workloads/chart-values-editor/json-schema-generator.ts new file mode 100644 index 0000000000..531d3aa985 --- /dev/null +++ b/src/frontend/packages/kubernetes/src/kubernetes/workloads/chart-values-editor/json-schema-generator.ts @@ -0,0 +1,288 @@ +// Generate a JSON Schema from an object +// This code incorporates the library: https://github.com/nijikokun/generate-schema/blob/master/src/schemas/json.js +// It is modified for Typescript and to mark all properties as not required + +// Reference: https://github.com/stephenhandley/type-of-is/blob/master/index.js +// Modified for Typescript + +const BUILT_IN_TYPES = [ + Object, + Function, + Array, + String, + Boolean, + Number, + Date, + RegExp, + Error +]; + +const _toString = ({}).toString; + +function isBuiltIn(_constructor): boolean { + for (const bit of BUILT_IN_TYPES) { + if (bit === _constructor) { + return true; + } + } + return false; +}; + +function of(obj) { + if ((obj === null) || (obj === undefined)) { + return obj; + } else { + return obj.constructor; + } +} + +function stringType(obj) { + // [object Blah] -> Blah + const stype = _toString.call(obj).slice(8, -1); + if ((obj === null) || (obj === undefined)) { + return stype.toLowerCase(); + } + + const ctype = of(obj); + if (ctype && !isBuiltIn(ctype)) { + return ctype.name; + } else { + return stype; + } +}; + +// Reference: https://github.com/nijikokun/generate-schema/blob/master/src/schemas/json.js + + +const DRAFT = 'http://json-schema.org/draft-04/schema#' + +function getPropertyFormat(value) { + const type = stringType(value).toLowerCase() + + if (type === 'date') return 'date-time' + return null +} + +function getPropertyType(value) { + const type = stringType(value).toLowerCase() + + if (type === 'number') return Number.isInteger(value) ? 'integer' : type + if (type === 'date') return 'string' + if (type === 'regexp') return 'string' + if (type === 'function') return 'string' + return type +} + +function getUniqueKeys(a, b, c) { + a = Object.keys(a) + b = Object.keys(b) + c = c || [] + + let value + let cIndex + let aIndex + + for (let keyIndex = 0, keyLength = b.length; keyIndex < keyLength; keyIndex++) { + value = b[keyIndex] + aIndex = a.indexOf(value) + cIndex = c.indexOf(value) + + if (aIndex === -1) { + if (cIndex !== -1) { + // Value is optional, it doesn't exist in A but exists in B(n) + c.splice(cIndex, 1) + } + } else if (cIndex === -1) { + // Value is required, it exists in both B and A, and is not yet present in C + c.push(value) + } + } + + return c +} + +function processArray(array, output?, nested?: boolean) { + let format + let oneOf + let type + + if (nested && output) { + output = { items: output } + } else { + output = output || {} + output.type = getPropertyType(array) + output.items = output.items || {} + type = output.items.type || null + } + + // Determine whether each item is different + for (let arrIndex = 0, arrLength = array.length; arrIndex < arrLength; arrIndex++) { + const elementType = getPropertyType(array[arrIndex]) + const elementFormat = getPropertyFormat(array[arrIndex]) + + if (type && elementType !== type) { + output.items.oneOf = [] + oneOf = true + break + } else { + type = elementType + format = elementFormat + } + } + + // Setup type otherwise + if (!oneOf && type) { + output.items.type = type + if (format) { + output.items.format = format + } + } else if (oneOf && type !== 'object') { + output.items = { + oneOf: [{ type }], + required: false + } + } + + // Process each item depending + if (typeof output.items.oneOf !== 'undefined' || type === 'object') { + for (let itemIndex = 0, itemLength = array.length; itemIndex < itemLength; itemIndex++) { + const value = array[itemIndex] + const itemType = getPropertyType(value) + const itemFormat = getPropertyFormat(value) + let arrayItem + if (itemType === 'object') { + if (output.items.properties) { + output.items.required = false + } + arrayItem = processObject(value, oneOf ? {} : output.items.properties, true) + } else if (itemType === 'array') { + arrayItem = processArray(value, oneOf ? {} : output.items.properties, true) + } else { + arrayItem = {} + arrayItem.type = itemType + if (itemFormat) { + arrayItem.format = itemFormat + } + } + if (oneOf) { + const childType = stringType(value).toLowerCase() + const tempObj: any = {}; + if (!arrayItem.type && childType === 'object') { + tempObj.properties = arrayItem + tempObj.type = 'object' + arrayItem = tempObj + } + output.items.oneOf.push(arrayItem) + } else { + if (output.items.type !== 'object') { + continue; + } + output.items.properties = arrayItem + } + } + } + return nested ? output.items : output +} + +function processObject(object: any, output?: any, nested?: boolean) { + if (nested && output) { + output = { properties: output } + } else { + output = output || {} + output.type = getPropertyType(object) + output.properties = output.properties || {} + output.required = [] + } + + for(const key of Object.keys(object)) { + const value = object[key] + let typ = getPropertyType(value) + const format = getPropertyFormat(value) + + typ = typ === 'undefined' ? 'null' : typ + + if (typ === 'object') { + output.properties[key] = processObject(value, output.properties[key]) + continue + } + + if (typ === 'array') { + output.properties[key] = processArray(value, output.properties[key]) + continue + } + + if (output.properties[key]) { + const entry = output.properties[key] + const hasTypeArray = Array.isArray(entry.type) + + // When an array already exists, we check the existing + // type array to see if it contains our current property + // type, if not, we add it to the array and continue + if (hasTypeArray && entry.type.indexOf(typ) < 0) { + entry.type.push(typ) + } + + // When multiple fields of differing types occur, + // json schema states that the field must specify the + // primitive types the field allows in array format. + if (!hasTypeArray && entry.type !== typ) { + entry.type = [entry.type, typ] + } + + continue + } + + output.properties[key] = {} + output.properties[key].type = typ + + if (format) { + output.properties[key].format = format + } + } + + return nested ? output.properties : output +} + + +export function generateJsonSchemaFromObject(title, object) { + let processOutput + const output: any = { + $schema: DRAFT + } + + // Determine title exists + if (typeof title !== 'string') { + object = title + title = undefined + } else { + output.title = title + } + + // Set initial object type + output.type = stringType(object).toLowerCase() + + // Process object + if (output.type === 'object') { + processOutput = processObject(object) + output.type = processOutput.type + output.properties = processOutput.properties + + // For a generated schema, nothing is marked as required + // This is a modification to the library + output.required = false; + } + + if (output.type === 'array') { + processOutput = processArray(object) + output.type = processOutput.type + output.items = processOutput.items + + if (output.title) { + output.items.title = output.title + output.title += ' Set' + } + } + + // Output + return output +} \ No newline at end of file diff --git a/src/frontend/packages/kubernetes/src/kubernetes/workloads/chart-values-editor/merge.ts b/src/frontend/packages/kubernetes/src/kubernetes/workloads/chart-values-editor/merge.ts new file mode 100644 index 0000000000..39ef4cf042 --- /dev/null +++ b/src/frontend/packages/kubernetes/src/kubernetes/workloads/chart-values-editor/merge.ts @@ -0,0 +1,26 @@ + +export function mergeObjects(src: any, ...dest: any): any { + // Copy src + const data = JSON.parse(JSON.stringify(src)); + // Merge in all of the dest objects + for (const obj of dest) { + doMergeObjects(data, obj); + } + + return data; +} + +// merge from dest into src +function doMergeObjects(src: any, dest: any) { + // Go through the keys of dest an update them in src + Object.keys(dest).forEach(key => { + if (typeof(dest[key]) === 'object' && !Array.isArray(dest)) { + if (!src[key]) { + src[key] = {}; + } + doMergeObjects(src[key], dest[key]); + } else { + src[key] = dest[key] + } + }); +} diff --git a/src/frontend/packages/kubernetes/src/helm/create-release/create-release.component.html b/src/frontend/packages/kubernetes/src/kubernetes/workloads/create-release/create-release.component.html similarity index 70% rename from src/frontend/packages/kubernetes/src/helm/create-release/create-release.component.html rename to src/frontend/packages/kubernetes/src/kubernetes/workloads/create-release/create-release.component.html index 4aec9bb603..000f286a25 100644 --- a/src/frontend/packages/kubernetes/src/helm/create-release/create-release.component.html +++ b/src/frontend/packages/kubernetes/src/kubernetes/workloads/create-release/create-release.component.html @@ -36,17 +36,6 @@ -
-
-

Enter YAML Value Overrides

- -
- - Values - - -
+
\ No newline at end of file diff --git a/src/frontend/packages/kubernetes/src/kubernetes/workloads/create-release/create-release.component.scss b/src/frontend/packages/kubernetes/src/kubernetes/workloads/create-release/create-release.component.scss new file mode 100644 index 0000000000..cf807defaa --- /dev/null +++ b/src/frontend/packages/kubernetes/src/kubernetes/workloads/create-release/create-release.component.scss @@ -0,0 +1,18 @@ +:host { + flex: 1; +} + +.helm-create-release { + &__heading { + align-items: center; + display: flex; + } + + &__title { + flex: 1; + font-size: 14px; + } + &__button { + height: 36px; + } +} diff --git a/src/frontend/packages/kubernetes/src/helm/create-release/create-release.component.spec.ts b/src/frontend/packages/kubernetes/src/kubernetes/workloads/create-release/create-release.component.spec.ts similarity index 58% rename from src/frontend/packages/kubernetes/src/helm/create-release/create-release.component.spec.ts rename to src/frontend/packages/kubernetes/src/kubernetes/workloads/create-release/create-release.component.spec.ts index d504716f25..68c034aaa7 100644 --- a/src/frontend/packages/kubernetes/src/helm/create-release/create-release.component.spec.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/workloads/create-release/create-release.component.spec.ts @@ -2,12 +2,24 @@ import { HttpClient } from '@angular/common/http'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +<<<<<<< HEAD:src/frontend/packages/kubernetes/src/helm/create-release/create-release.component.spec.ts import { ConfirmationDialogService } from '../../../../core/src/shared/components/confirmation-dialog.service'; import { TabNavService } from '../../../../core/src/tab-nav.service'; import { EntityMonitorFactory } from '../../../../store/src/monitors/entity-monitor.factory.service'; import { InternalEventMonitorFactory } from '../../../../store/src/monitors/internal-event-monitor.factory'; import { PaginationMonitorFactory } from '../../../../store/src/monitors/pagination-monitor.factory'; import { KubernetesBaseTestModules } from '../../kubernetes/kubernetes.testing.module'; +======= +import { ConfirmationDialogService } from '../../../../../../core/src/shared/components/confirmation-dialog.service'; +import { TabNavService } from '../../../../../../core/src/tab-nav.service'; +import { EntityMonitorFactory } from '../../../../../../store/src/monitors/entity-monitor.factory.service'; +import { InternalEventMonitorFactory } from '../../../../../../store/src/monitors/internal-event-monitor.factory'; +import { PaginationMonitorFactory } from '../../../../../../store/src/monitors/pagination-monitor.factory'; +import { MockChartService } from '../../../helm/monocular/shared/services/chart.service.mock'; +import { ChartsService } from '../../../helm/monocular/shared/services/charts.service'; +import { ConfigService } from '../../../helm/monocular/shared/services/config.service'; +import { KubernetesBaseTestModules } from '../../kubernetes.testing.module'; +>>>>>>> origin/master:src/frontend/packages/kubernetes/src/kubernetes/workloads/create-release/create-release.component.spec.ts import { CreateReleaseComponent } from './create-release.component'; describe('CreateReleaseComponent', () => { @@ -30,7 +42,9 @@ describe('CreateReleaseComponent', () => { EntityMonitorFactory, InternalEventMonitorFactory, TabNavService, - ConfirmationDialogService + ConfirmationDialogService, + { provide: ChartsService, useValue: new MockChartService() }, + { provide: ConfigService, useValue: { appName: 'appName' } }, ] }) .compileComponents(); @@ -46,8 +60,6 @@ describe('CreateReleaseComponent', () => { }); it('should be created', () => { - httpMock.expectOne('/pp/v1/chartsvc/v1/assets/undefined/undefined/versions/undefined/values.yaml'); - expect(component).toBeTruthy(); }); diff --git a/src/frontend/packages/kubernetes/src/helm/create-release/create-release.component.ts b/src/frontend/packages/kubernetes/src/kubernetes/workloads/create-release/create-release.component.ts similarity index 66% rename from src/frontend/packages/kubernetes/src/helm/create-release/create-release.component.ts rename to src/frontend/packages/kubernetes/src/kubernetes/workloads/create-release/create-release.component.ts index 3fe65028e9..ef371515e7 100644 --- a/src/frontend/packages/kubernetes/src/helm/create-release/create-release.component.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/workloads/create-release/create-release.component.ts @@ -1,11 +1,10 @@ -import { HttpClient } from '@angular/common/http'; import { Component, ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { FormControl, FormGroup, Validators } from '@angular/forms'; -import { MatTextareaAutosize } from '@angular/material/input'; import { ActivatedRoute } from '@angular/router'; import { BehaviorSubject, combineLatest, Observable, of, Subscription } from 'rxjs'; import { distinctUntilChanged, filter, first, map, pairwise, startWith, switchMap } from 'rxjs/operators'; +<<<<<<< HEAD:src/frontend/packages/kubernetes/src/helm/create-release/create-release.component.ts import { EndpointsService } from '../../../../core/src/core/endpoints.service'; import { safeUnsubscribe } from '../../../../core/src/core/utils.service'; import { ConfirmationDialogConfig } from '../../../../core/src/shared/components/confirmation-dialog.config'; @@ -19,6 +18,25 @@ import { helmEntityCatalog } from '../helm-entity-catalog'; import { createMonocularProviders } from '../monocular/stratos-monocular-providers.helpers'; import { getMonocularEndpoint, stratosMonocularEndpointGuid } from '../monocular/stratos-monocular.helper'; import { HelmInstallValues } from '../store/helm.types'; +======= +import { EndpointsService } from '../../../../../../core/src/core/endpoints.service'; +import { safeUnsubscribe } from '../../../../../../core/src/core/utils.service'; +import { + StepOnNextFunction, + StepOnNextResult, +} from '../../../../../../core/src/shared/components/stepper/step/step.component'; +import { RequestInfoState } from '../../../../../../store/src/reducers/api-request-reducer/types'; +import { helmEntityCatalog } from '../../../helm/helm-entity-catalog'; +import { ChartsService } from '../../../helm/monocular/shared/services/charts.service'; +import { createMonocularProviders } from '../../../helm/monocular/stratos-monocular-providers.helpers'; +import { getMonocularEndpoint, stratosMonocularEndpointGuid } from '../../../helm/monocular/stratos-monocular.helper'; +import { HelmChartReference, HelmInstallValues } from '../../../helm/store/helm.types'; +import { kubeEntityCatalog } from '../../kubernetes-entity-catalog'; +import { KUBERNETES_ENDPOINT_TYPE } from '../../kubernetes-entity-factory'; +import { KubernetesNamespace } from '../../store/kube.types'; +import { getFirstChartUrl } from '../workload.utils'; +import { ChartValuesConfig, ChartValuesEditorComponent } from './../chart-values-editor/chart-values-editor.component'; +>>>>>>> origin/master:src/frontend/packages/kubernetes/src/kubernetes/workloads/create-release/create-release.component.ts @Component({ selector: 'app-create-release', @@ -30,13 +48,6 @@ import { HelmInstallValues } from '../store/helm.types'; }) export class CreateReleaseComponent implements OnInit, OnDestroy { - // Confirmation dialog - overwriteValuesConfirmation = new ConfirmationDialogConfig( - 'Overwrite Values?', - 'Are you sure you want to replace your values with those from values.yaml?', - 'Overwrite' - ); - // isLoading$ = observableOf(false); paginationStateSub: Subscription; @@ -46,25 +57,24 @@ export class CreateReleaseComponent implements OnInit, OnDestroy { details: FormGroup; namespaces$: Observable; - overrides: FormGroup; private endpointChanged = new BehaviorSubject(null); @ViewChild('releaseNameInputField', { static: true }) releaseNameInputField: ElementRef; - @ViewChild('overridesYamlTextArea', { static: true }) overridesYamlTextArea: ElementRef; - @ViewChild(MatTextareaAutosize, { static: false }) overridesYamlAutosize: MatTextareaAutosize; - - public valuesYaml = ''; + @ViewChild('editor', { static: true }) editor: ChartValuesEditorComponent; private subs: Subscription[] = []; private createdNamespace = false; + private chart: HelmChartReference; + public config: ChartValuesConfig; + constructor( private route: ActivatedRoute, public endpointsService: EndpointsService, - private httpClient: HttpClient, - private confirmDialog: ConfirmationDialogService, + private chartsService: ChartsService, ) { +<<<<<<< HEAD:src/frontend/packages/kubernetes/src/helm/create-release/create-release.component.ts const chart = this.route.snapshot.params; this.cancelUrl = `/monocular/charts/${getMonocularEndpoint(this.route)}/${chart.repo}/${chart.chartName}/${chart.version}`; @@ -73,23 +83,25 @@ export class CreateReleaseComponent implements OnInit, OnDestroy { this.overrides = new FormGroup({ values: new FormControl('') }); +======= + const chart = this.route.snapshot.params as HelmChartReference; + this.cancelUrl = `/monocular/charts/${getMonocularEndpoint(this.route)}/${chart.repo}/${chart.name}/${chart.version}`; + this.chart = chart; +>>>>>>> origin/master:src/frontend/packages/kubernetes/src/kubernetes/workloads/create-release/create-release.component.ts + + this.config = { + valuesUrl: `/pp/v1/chartsvc/v1/assets/${chart.repo}/${chart.name}/versions/${chart.version}/values.yaml`, + schemaUrl: `/pp/v1/chartsvc/v1/assets/${chart.repo}/${chart.name}/versions/${chart.version}/values.schema.json`, + }; - // Fetch the values.yaml for the Chart - const valuesYamlUrl = `/pp/v1/chartsvc/v1/assets/${chart.repo}/${chart.chartName}/versions/${chart.version}/values.yaml`; - - this.httpClient.get(valuesYamlUrl, { responseType: 'text' }) - .subscribe(response => { - this.valuesYaml = response; - }, err => { - console.error('Failed to fetch chart values: ', err.message || err); - }); + this.setupDetailsStep(); } private setupDetailsStep() { this.details = new FormGroup({ endpoint: new FormControl('', Validators.required), releaseName: new FormControl('', Validators.required), - releaseNamespace: new FormControl(''), + releaseNamespace: new FormControl('', Validators.required), createNamespace: new FormControl(false), }); this.details.controls.createNamespace.disable(); @@ -179,21 +191,6 @@ export class CreateReleaseComponent implements OnInit, OnDestroy { return lowerCase.length ? namespaces.filter(ns => ns.toLowerCase().indexOf(lowerCase) >= 0) : namespaces; } - public useValuesYaml() { - if (this.overrides.value.values.length !== 0) { - this.confirmDialog.open(this.overwriteValuesConfirmation, () => { - this.replaceWithValuesYaml(); - }); - - } else { - this.replaceWithValuesYaml(); - } - } - - private replaceWithValuesYaml() { - this.overrides.controls.values.setValue(this.valuesYaml, { onlySelf: true }); - } - ngOnInit() { // Auto select endpoint if there is only one this.kubeEndpoints$.pipe(first()).subscribe(ep => { @@ -207,11 +204,16 @@ export class CreateReleaseComponent implements OnInit, OnDestroy { }); } + // Ensure the editor is resized when the overrides step becomes visible onEnterOverrides = () => { +<<<<<<< HEAD:src/frontend/packages/kubernetes/src/helm/create-release/create-release.component.ts setTimeout(() => { // this.overridesYamlAutosize.resizeToFitContent(true); this.overridesYamlTextArea.nativeElement.focus(); }, 1); +======= + this.editor.resizeEditor(); +>>>>>>> origin/master:src/frontend/packages/kubernetes/src/kubernetes/workloads/create-release/create-release.component.ts }; submit: StepOnNextFunction = () => { @@ -254,35 +256,53 @@ export class CreateReleaseComponent implements OnInit, OnDestroy { // Build the request body const values: HelmInstallValues = { ...this.details.value, +<<<<<<< HEAD:src/frontend/packages/kubernetes/src/helm/create-release/create-release.component.ts ...this.overrides.value, chart: { chartName: this.route.snapshot.params.chartName, +======= + values: JSON.stringify(this.editor.getValues()), + chart: { + name: this.route.snapshot.params.name, +>>>>>>> origin/master:src/frontend/packages/kubernetes/src/kubernetes/workloads/create-release/create-release.component.ts repo: this.route.snapshot.params.repo, version: this.route.snapshot.params.version, }, monocularEndpoint: endpoint === stratosMonocularEndpointGuid ? null : endpoint }; - // Make the request - return helmEntityCatalog.chart.api.install(values).pipe( - // Wait for result of request - filter(state => !!state), - pairwise(), - filter(([oldVal, newVal]) => (oldVal.creating && !newVal.creating)), - map(([, newVal]) => newVal), - map(result => ({ - success: !result.error, - redirect: !result.error, - redirectPayload: { - path: !result.error ? `workloads/${values.endpoint}:${values.releaseNamespace}:${values.releaseName}/summary` : '' - }, - message: !result.error ? '' : result.message - })) + // Get the chart first, so we can get then install URL, then install + return this.chartsService.getVersion(this.chart.repo, this.chart.name, this.chart.version).pipe( + switchMap(chartInfo => { + if (!chartInfo) { + throw new Error('Could not get Chart URL'); + } + // Add the chart url into the values + values.chartUrl = getFirstChartUrl(chartInfo); + if (values.chartUrl.length === 0) { + throw new Error('Could not get Chart URL'); + } + // Make the request + return helmEntityCatalog.chart.api.install(values).pipe( + // Wait for result of request + filter(state => !!state), + pairwise(), + filter(([oldVal, newVal]) => (oldVal.creating && !newVal.creating)), + map(([, newVal]) => newVal), + map(result => ({ + success: !result.error, + redirect: !result.error, + redirectPayload: { + path: !result.error ? `workloads/${values.endpoint}:${values.releaseNamespace}:${values.releaseName}/summary` : '' + }, + message: !result.error ? '' : result.message + })) + ); + }) ); } ngOnDestroy() { safeUnsubscribe(...this.subs); } - } diff --git a/src/frontend/packages/kubernetes/src/kubernetes/workloads/list-types/helm-release-card/helm-release-card.component.html b/src/frontend/packages/kubernetes/src/kubernetes/workloads/list-types/helm-release-card/helm-release-card.component.html index ad120d3cea..8dde7f3341 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/workloads/list-types/helm-release-card/helm-release-card.component.html +++ b/src/frontend/packages/kubernetes/src/kubernetes/workloads/list-types/helm-release-card/helm-release-card.component.html @@ -29,9 +29,9 @@ - Version + Chart Version - {{ row.version }} + {{ row.chart.metadata.version }} diff --git a/src/frontend/packages/kubernetes/src/kubernetes/workloads/list-types/helm-releases-list-config.service.ts b/src/frontend/packages/kubernetes/src/kubernetes/workloads/list-types/helm-releases-list-config.service.ts index 4a7be68ec4..205c587b60 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/workloads/list-types/helm-releases-list-config.service.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/workloads/list-types/helm-releases-list-config.service.ts @@ -88,14 +88,14 @@ export class HelmReleasesListConfig implements IListConfig { }, { columnId: 'version', - headerCell: () => 'Version', + headerCell: () => 'Chart Version', cellDefinition: { - valuePath: 'version' + valuePath: 'chart.metadata.version' }, sort: { type: 'sort', orderKey: 'version', - field: 'version' + field: 'chart.metadata.version' }, cellFlex: '1' }, diff --git a/src/frontend/packages/kubernetes/src/kubernetes/workloads/release/tabs/helm-release-helper.service.ts b/src/frontend/packages/kubernetes/src/kubernetes/workloads/release/tabs/helm-release-helper.service.ts index 99899a0b92..d3af46d384 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/workloads/release/tabs/helm-release-helper.service.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/workloads/release/tabs/helm-release-helper.service.ts @@ -14,6 +14,7 @@ import { HelmReleaseGraph, HelmReleaseGuid, HelmReleaseResources, + HelmReleaseRevision, } from '../../workload.types'; import { workloadsEntityCatalog } from '../../workloads-entity-catalog'; @@ -147,7 +148,7 @@ export class HelmReleaseHelperService { ); } - public fetchReleaseHistory(): Observable { + public fetchReleaseHistory(): Observable { // Get the history for a Helm release return workloadsEntityCatalog.history.store.getEntityService( this.releaseTitle, diff --git a/src/frontend/packages/kubernetes/src/kubernetes/workloads/release/tabs/helm-release-history-tab/helm-release-history-tab.component.ts b/src/frontend/packages/kubernetes/src/kubernetes/workloads/release/tabs/helm-release-history-tab/helm-release-history-tab.component.ts index 86c9acf1b1..797e0bfcf7 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/workloads/release/tabs/helm-release-history-tab/helm-release-history-tab.component.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/workloads/release/tabs/helm-release-history-tab/helm-release-history-tab.component.ts @@ -74,7 +74,9 @@ export class HelmReleaseHistoryTabComponent { }, ]; - const data$ = this.helmReleaseHelper.fetchReleaseHistory(); + const data$ = this.helmReleaseHelper.fetchReleaseHistory().pipe( + map(history => [...history].sort((a, b) => b.revision - a.revision)) + ); this.dataSource = { connect: () => data$, disconnect: () => { }, diff --git a/src/frontend/packages/kubernetes/src/kubernetes/workloads/release/tabs/helm-release-summary-tab/helm-release-summary-tab.component.ts b/src/frontend/packages/kubernetes/src/kubernetes/workloads/release/tabs/helm-release-summary-tab/helm-release-summary-tab.component.ts index e2824e2531..d896843d89 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/workloads/release/tabs/helm-release-summary-tab/helm-release-summary-tab.component.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/workloads/release/tabs/helm-release-summary-tab/helm-release-summary-tab.component.ts @@ -120,6 +120,7 @@ export class HelmReleaseSummaryTabComponent implements OnDestroy { this.hasUpgrade$ = this.helmReleaseHelper.hasUpgrade().pipe(map(v => v ? v.version : null)); + // Can upgrade if the Chart is available this.canUpgrade$ = this.helmReleaseHelper.hasUpgrade(true).pipe(map(v => !!v)); this.resources$ = combineLatest( diff --git a/src/frontend/packages/kubernetes/src/kubernetes/workloads/upgrade-release/upgrade-release.component.html b/src/frontend/packages/kubernetes/src/kubernetes/workloads/upgrade-release/upgrade-release.component.html index dae9721887..4a3ab48abc 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/workloads/upgrade-release/upgrade-release.component.html +++ b/src/frontend/packages/kubernetes/src/kubernetes/workloads/upgrade-release/upgrade-release.component.html @@ -2,29 +2,11 @@ Upgrade Workload - + - -
-
-
-

Enter YAML Value Overrides

- -
- - Values - - -
- -
+ + -
\ No newline at end of file diff --git a/src/frontend/packages/kubernetes/src/kubernetes/workloads/upgrade-release/upgrade-release.component.ts b/src/frontend/packages/kubernetes/src/kubernetes/workloads/upgrade-release/upgrade-release.component.ts index d543056ca4..761b782a45 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/workloads/upgrade-release/upgrade-release.component.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/workloads/upgrade-release/upgrade-release.component.ts @@ -1,16 +1,21 @@ -import { Component } from '@angular/core'; -import { FormControl, FormGroup } from '@angular/forms'; +import { Component, ViewChild } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Store } from '@ngrx/store'; import { Observable, of } from 'rxjs'; -import { filter, first, map, pairwise } from 'rxjs/operators'; +import { filter, first, map, pairwise, tap } from 'rxjs/operators'; -import { StepComponent, StepOnNextFunction } from '../../../../../core/src/shared/components/stepper/step/step.component'; +import { + StepComponent, + StepOnNextFunction, + StepOnNextResult, +} from '../../../../../core/src/shared/components/stepper/step/step.component'; import { ActionState } from '../../../../../store/src/reducers/api-request-reducer/types'; import { stratosMonocularEndpointGuid } from '../../../helm/monocular/stratos-monocular.helper'; import { HelmUpgradeValues, MonocularVersion } from '../../../helm/store/helm.types'; +import { ChartValuesConfig, ChartValuesEditorComponent } from '../chart-values-editor/chart-values-editor.component'; import { HelmReleaseHelperService } from '../release/tabs/helm-release-helper.service'; import { HelmReleaseGuid } from '../workload.types'; +import { getFirstChartUrl } from '../workload.utils'; import { workloadsEntityCatalog } from './../workloads-entity-catalog'; import { ReleaseUpgradeVersionsListConfig } from './release-version-list-config'; @@ -33,11 +38,14 @@ import { ReleaseUpgradeVersionsListConfig } from './release-version-list-config' }) export class UpgradeReleaseComponent { + @ViewChild('editor', { static: true }) editor: ChartValuesEditorComponent; + public cancelUrl; public listConfig: ReleaseUpgradeVersionsListConfig; public validate$: Observable; private version: MonocularVersion; - public overrides: FormGroup; + + public config: ChartValuesConfig; private monocularEndpointId: string; @@ -51,11 +59,6 @@ export class UpgradeReleaseComponent { this.cancelUrl = `/workloads/${this.helper.guid}`; - // Form for overrides step (Helm Values) - this.overrides = new FormGroup({ - values: new FormControl('') - }); - this.helper.hasUpgrade(true).pipe( filter(c => !!c), first() @@ -79,6 +82,32 @@ export class UpgradeReleaseComponent { }); } + // Ensure the editor is resized when the overrides step becomes visible + onEnterOverrides = () => { + this.editor.resizeEditor(); + }; + + // Update the editor with the chosen version when the user moves to the next step + onNext = (): Observable => { + const chart = this.version.relationships.chart.data; + const version = this.version.attributes.version; + + // Fetch the release metadata so that we have the values used to install the current release + return this.helper.release$.pipe( + first(), + tap(release => { + this.config = { + schemaUrl: `/pp/v1/chartsvc/v1/assets/${chart.repo.name}/${chart.name}/versions/${version}/values.schema.json`, + valuesUrl: `/pp/v1/chartsvc/v1/assets/${chart.repo.name}/${chart.name}/versions/${version}/values.yaml`, + releaseValues: release.config + }; + }), + map(() => { + return { success: true }; + }) + ); + }; + // Hide/show the advanced options step toggleAdvancedOptions() { this.showAdvancedOptions = !this.showAdvancedOptions; @@ -90,15 +119,17 @@ export class UpgradeReleaseComponent { return of({ success: true }); } + // Add the chart url into the values const values: HelmUpgradeValues = { - values: this.overrides.controls.values.value, + values: JSON.stringify(this.editor.getValues()), restartPods: false, chart: { name: this.version.relationships.chart.data.name, repo: this.version.relationships.chart.data.repo.name, version: this.version.attributes.version, }, - monocularEndpoint: this.monocularEndpointId === stratosMonocularEndpointGuid ? null : this.monocularEndpointId + monocularEndpoint: this.monocularEndpointId === stratosMonocularEndpointGuid ? null : this.monocularEndpointId, + chartUrl: getFirstChartUrl(this.version) }; // Make the request diff --git a/src/frontend/packages/kubernetes/src/kubernetes/workloads/workload.utils.ts b/src/frontend/packages/kubernetes/src/kubernetes/workloads/workload.utils.ts new file mode 100644 index 0000000000..7c8fb71baa --- /dev/null +++ b/src/frontend/packages/kubernetes/src/kubernetes/workloads/workload.utils.ts @@ -0,0 +1,9 @@ +import { ChartVersion } from '../../helm/monocular/shared/models/chart-version'; + +// Get first URL for a Chart or return empty string if none +export function getFirstChartUrl(chart: ChartVersion): string { + if (chart && chart.attributes && chart.attributes.urls && chart.attributes.urls.length > 0) { + return chart.attributes.urls[0]; + } + return ''; +} diff --git a/src/frontend/packages/kubernetes/src/kubernetes/workloads/workloads.module.ts b/src/frontend/packages/kubernetes/src/kubernetes/workloads/workloads.module.ts index a97f422321..c419fdd2c9 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/workloads/workloads.module.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/workloads/workloads.module.ts @@ -1,10 +1,14 @@ import { CommonModule, DatePipe } from '@angular/common'; import { NgModule } from '@angular/core'; +import { MaterialDesignFrameworkModule } from '@cfstratos/ajsf-material'; import { NgxGraphModule } from '@swimlane/ngx-graph'; +import { MonacoEditorModule, NgxMonacoEditorConfig } from 'ngx-monaco-editor'; import { CoreModule } from '../../../../core/src/core/core.module'; import { SharedModule } from '../../../../core/src/shared/shared.module'; import { KubernetesModule } from '../kubernetes.module'; +import { ChartValuesEditorComponent } from './chart-values-editor/chart-values-editor.component'; +import { CreateReleaseComponent } from './create-release/create-release.component'; import { HelmReleaseCardComponent } from './list-types/helm-release-card/helm-release-card.component'; import { HelmReleaseTabBaseComponent } from './release/helm-release-tab-base/helm-release-tab-base.component'; import { @@ -25,6 +29,12 @@ import { WorkloadsStoreModule } from './store/workloads.store.module'; import { UpgradeReleaseComponent } from './upgrade-release/upgrade-release.component'; import { WorkloadsRouting } from './workloads.routing'; +// Default config for the Monaco edfior +const monacoConfig: NgxMonacoEditorConfig = { + baseUrl: '/core/assets', // configure base path for monaco editor + defaultOptions: { scrollBeyondLastLine: false } +}; + @NgModule({ imports: [ CoreModule, @@ -34,6 +44,8 @@ import { WorkloadsRouting } from './workloads.routing'; WorkloadsRouting, NgxGraphModule, KubernetesModule, + MaterialDesignFrameworkModule, + MonacoEditorModule.forRoot(monacoConfig), ], declarations: [ HelmReleasesTabComponent, @@ -46,6 +58,8 @@ import { WorkloadsRouting } from './workloads.routing'; HelmReleaseResourceGraphComponent, HelmReleaseCardComponent, HelmReleaseAnalysisTabComponent, + ChartValuesEditorComponent, + CreateReleaseComponent, WorkloadLiveReloadComponent, UpgradeReleaseComponent, HelmReleaseHistoryTabComponent, diff --git a/src/frontend/packages/kubernetes/src/kubernetes/workloads/workloads.routing.ts b/src/frontend/packages/kubernetes/src/kubernetes/workloads/workloads.routing.ts index a9773315f8..5b91cda06a 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/workloads/workloads.routing.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/workloads/workloads.routing.ts @@ -2,6 +2,7 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { NgxChartsModule } from '@swimlane/ngx-charts'; +import { CreateReleaseComponent } from './create-release/create-release.component'; import { HelmReleaseTabBaseComponent } from './release/helm-release-tab-base/helm-release-tab-base.component'; import { HelmReleaseAnalysisTabComponent, @@ -27,6 +28,8 @@ const routes: Routes = [ component: HelmReleasesTabComponent, pathMatch: 'full', }, + { pathMatch: 'full', path: 'install/:endpoint/:repo/:name/:version', component: CreateReleaseComponent }, + { pathMatch: 'full', path: 'install/:endpoint/:repo/:name', component: CreateReleaseComponent }, { // guid = kube endpoint path: ':guid/upgrade', diff --git a/src/frontend/packages/suse-extensions/src/suse-login/suse-login.component.html b/src/frontend/packages/suse-extensions/src/suse-login/suse-login.component.html index 57577ea756..e7155c7e67 100644 --- a/src/frontend/packages/suse-extensions/src/suse-login/suse-login.component.html +++ b/src/frontend/packages/suse-extensions/src/suse-login/suse-login.component.html @@ -1,4 +1,4 @@ -