From 254b3394545d9670f0eb14221849c0c76e34c274 Mon Sep 17 00:00:00 2001 From: ANOOP SINGH Date: Thu, 31 Mar 2022 20:32:53 +0530 Subject: [PATCH] Fix upstream conflict (#187) * fix(oauth): remove circular dependency on ExternalAuthTokenFilter bean in OAuth2SsoConfig when oauth2 is enabled (#1492) Previously in spring 2.2.5, if oauth2 is enabled there is no circular dependency on ExternalAuthTokenFilter bean in OAuth2SsoConfig. In spring 2.2.13, if oauth2 is enabled there is a circular dependency error on ExternalAuthTokenFilter bean. This circular dependency results in an error when the gate application tries to start up. The application fails with error: BeanCurrentlyInCreationException: Error creating bean with name 'OAuth2SsoConfig': Bean with name 'OAuth2SsoConfig' has been injected into other beans [externalAuthTokenFilter] in its raw version as part of a circular reference, but has eventually been wrapped. To fix this error, add the Component annotation to ExternalAuthTokenFilter and remove the ExternalAuthTokenFilter bean from OAuth2SsoConfig. Co-authored-by: David Byron * fix(web): disable keel by default as it is an optional service (#1453) Co-authored-by: Justin Field Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> * fix(api): fix movie quotes to match movie script (#1423) Co-authored-by: Justin Field Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> * chore(build): gradle 6.8.1 (#1413) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> * chore(dependencies): Autobump korkVersion (#1493) Co-authored-by: root * chore(dependencies): Autobump korkVersion (#1495) Co-authored-by: root * chore(dependencies): Autobump korkVersion (#1496) Co-authored-by: root * fix(vulnerability): avoid expose gate endpoints (#1497) * chore(dependencies): Autobump korkVersion (#1501) Co-authored-by: root * chore(dependencies): Autobump fiatVersion (#1504) Co-authored-by: root * chore(dependencies): Autobump korkVersion (#1502) Co-authored-by: root Co-authored-by: Matt <6519811+mattgogerly@users.noreply.github.com> * feat(web): Expose experimental account storage API (#1494) * feat(web): Expose experimental account storage API This adds some of the REST APIs introduced in the experimental account storage API in Clouddriver to Gate. Initially, these APIs are only available for admins. * Combine account and credentials endpoints * Add docs on AccountDefinition * Add alpha annotations * chore(build): update mergify config (#1506) Co-authored-by: Cameron Motevasselani * fix(gate): Typos in Account Management API (#1510) * fix(gate/web): Fix typo in PreAuthorize annotation It appears that some variables were renamed while I worked on the PR and didn't update the annotations to match. * fix(gate/core): Fix retrofit signature error * fix(gate/web): Add explicit name property to AccountDefinition (#1514) This fixes an authorization check error where Jackson knows how to handle the `name` property of an account definition, but SpEL does not see the property. Now the PostFilter annotation should work equivalently to the same filter check in Clouddriver. * chore(dependencies): Autobump korkVersion (#1515) Co-authored-by: root * fix(authn/oauth2): prevent oauth2 redirect loops (#1517) During setup of spinnaker authentication with oauth2 a common hurdle is a redirect loop. For example: https://github.com/spinnaker/spinnaker/issues/5794 https://github.com/spinnaker/spinnaker/issues/1630 Also, many threads in Slack discuss these problems. In fact this appears to be a common pitfall for the spring-security-oauth2-autoconfigure library in general. A light refresher on the ouath2 flow in play here seems worthwhile. The user is redirected from `/login` in gate to the external auth provider (google, github, etc.) and after successfully authenticating they are redirected back to the gate `/login` endpoint but this time with a code parameter that is to be used to request an access token. This request can fail for a variety of reasons, and if it does, the underlying spring library triggers a redirect to the `/error` endpoint. What causes the redirect loop for gate in particular (and for other users of the library in a similar fashion) is that the WebSecurityConfigurerAdapter in play is treating `/error` as an authenticated path and so instead of just returning with a 401, it re-redirects to `/login` and the redirect loop continues. My thought is that instead of a redirect loop, simply allowing the 401 to be returned will be a stronger more helpful signal as to what is going on. Hopefully it will save future first-time installers headaches. Spinnaker docs have included several troubleshooting hints and tips for how where you terminate SSL affects configuration etc. Even after following all of these and lots of spelunking through spinnaker github issues and combing over threads in slack, I found myself still experiencing a redirect loop even though I had applied all the combined wisdom that was applicable to my setup. As it turns out, I had a bad copy/paste of my client secret in my configuration. So the request to turn the code from google into an access token from google was failing with a 401. After much debugging and deep diving into the spring security code I found that had I turned on DEBUG in gate for these classes in gate-local.yml: ``` logging: level: org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler: DEBUG org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter: DEBUG ``` Then I would have seen in the logs that a 401 response was returned from google and perhaps it would have caused me to look closer at my botched client secret configuration. I think perhaps we don't want to require that all operators of spinnaker become spring-security-oauth2 experts. So I'm proposing adding `/error` to the list of paths in gate that aren't treated as authenticated. Thus short-circuiting the redirect loop and bringing to light helpful troubleshooting info that was previously more or less swallowed. * chore(ci): update setup-java GHA to v2 (#1518) closes: https://github.com/spinnaker/spinnaker/issues/6611 * fix(dependency): Issue with jackson-bom and kotlin-bom version conflict resolution while upgrading the spring-boot 2.3.x (#1505) * fix(dependency): Introducing spring dependency management gradle plugin Spring boot has moved to gradle based dependency management from v2.3.x. This change has brought issue of conflict resolution failure of the Jackson-bom version and kotlin-bom version with gate service when it consumes the maven-bom generated by kork. The issue details are available in given link. https://docs.google.com/document/d/1Ck4KeoB1ER0aQUTnf3e-x-M3i2Ur0It7YaaxEMiMXls/edit To resolve this issue while upgrading gate service with spring v2.3.x, we must require the spring dependency management gradle plugin. * Revert "fix(dependency): Introducing spring dependency management gradle plugin" This reverts commit b3b2c9e3f91b669ae2975a40ccac6a0ec43961c2. * fix(dependency): Issue with jackson-bom and kotlin-bom version conflict resolution while upgrading the spring-boot 2.3.x. The root cause of this issue is uncontrolled conflict resolution of jackson-bom and kotlin-bom dependency version imported from external maven BOM provided by kork-bom, as per the gradle documentation https://docs.gradle.org/6.9.1/userguide/platforms.html#sub:bom_import, we can use gradle enforcedPlatform closure as part of the implementation to strictly adhere the versions of direct and transitive dependencies imported BOM. implementation(enforcedPlatform("io.spinnaker.kork:kork-bom:$korkVersion")) * chore(dependencies): Autobump spinnakerGradleVersion (#1519) Co-authored-by: root * chore(dependencies): Autobump spinnakerGradleVersion (#1520) Co-authored-by: root * chore(dependencies): Autobump spinnakerGradleVersion (#1521) Co-authored-by: root * chore(dependencies): Autobump spinnakerGradleVersion (#1522) Co-authored-by: root * chore(dependencies): Autobump spinnakerGradleVersion (#1523) Co-authored-by: root * chore(dependencies): Autobump spinnakerGradleVersion (#1524) Co-authored-by: root * chore(dependencies): Autobump fiatVersion (#1528) Co-authored-by: root * chore(ci): GHA - container image and apt package build & push (#1529) to Google Artifact Registry see: https://github.com/spinnaker/rosco/pull/841 Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> * chore(ci): GHA - simplify build versioning (#1530) - collapse version info gathering steps into single `build_variables` step - collapse version info parts into single string and use everywhere. - use short git sha: `(git rev-parse --short HEAD)` Note `build.yml` versioning is not compatible with Debian package building as gradle plugin enforces `^[0-9]+`. We don't publish master branch or release-* branches to GAR apt repository though. Prefixing the version with `-dev-` or something and publishing Debian packages is possible but may pollute `apt-cache policy spinnaker-rosco` output and overall be unnecessary with regular releases. * fix(plugins-test): try harder for the version of versionNotSupportedPlugin to actually not be supported (#1532) Before this, a gate version >= 2.0.0 would cause versionNotSupportedPlugin to get used, causing tests to fail, and making it impossible to e.g. release gate. * chore(cit): GHA - plugin builds require SemVer (#1531) I wanted to avoid confusion between a git tag `X` building version `X` and a master or release branch building version `X-dev-*` but it seems unavoidable. SemVer is required by plugins. See constraint: https://github.com/spinnaker/kork/blob/5dc6bb98615667f1b4f3e18445c1651d773c9f6b/kork-plugins/src/main/kotlin/com/netflix/spinnaker/kork/plugins/SpinnakerServiceVersionManager.kt#L47 changes: - fetch full git repository so that we can access previous tag in branch. Convert `release.yml` to this method instead of `run: git.. --unshallow`. - use previous git tag as start of version string. Cut the 'v' prefix from the tag, 'v1.2.3' -> '1.2.3' as required for Plugins (and Debians fwiw): `Caused by: Unexpected character 'LETTER(v)' at position '0', expecting '[DIGIT]'` - append `-dev-` to designate that it is not an official version. The short git SHA and date time are NOT present on release versions (eg: 1.2.3) so that also differs. - do this version setting in `pr.yml` as well so we might pick up version issues in PR's and not just at merge. * chore(dependencies): Autobump korkVersion (#1536) Co-authored-by: root * chore(dependencies): Autobump spinnakerGradleVersion (#1537) Co-authored-by: root * feat(credentials): Update account type discriminator (#1533) This normalizes the type discriminator in account definitions to match that of account credentials instances (i.e., the type of CredentialsDefinition instance and Credentials instance are both specified through the "type" property). This also removes redundant authorization annotations that are better enforced by AccountDefinitionService in Clouddriver. Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> * resolved AuthConfig file conflict. Co-authored-by: Calvin Tse Co-authored-by: David Byron Co-authored-by: Emmanouil Katefidis Co-authored-by: Justin Field Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Thomas Swanson Co-authored-by: Cameron Fieber Co-authored-by: spinnakerbot Co-authored-by: root Co-authored-by: root Co-authored-by: root Co-authored-by: Cristhian Castaneda Co-authored-by: root Co-authored-by: root Co-authored-by: root Co-authored-by: Matt <6519811+mattgogerly@users.noreply.github.com> Co-authored-by: Matt Sicker Co-authored-by: Cameron Motevasselani Co-authored-by: Cameron Motevasselani Co-authored-by: Matt Sicker Co-authored-by: root Co-authored-by: Chris Phillips <4722632+chris-h-phillips@users.noreply.github.com> Co-authored-by: kskewes-sf <96093759+kskewes-sf@users.noreply.github.com> Co-authored-by: Sandesh Co-authored-by: root Co-authored-by: root Co-authored-by: root Co-authored-by: root Co-authored-by: root Co-authored-by: root Co-authored-by: root Co-authored-by: David Byron <82477955+dbyron-sf@users.noreply.github.com> Co-authored-by: root Co-authored-by: root --- .github/workflows/build.yml | 55 +++++++++++++++--- .github/workflows/pr.yml | 48 +++++++++++---- .github/workflows/release.yml | 55 ++++++++++++++++-- .github/workflows/release_info.sh | 2 +- .mergify.yml | 25 ++++---- build.gradle | 2 +- .../spinnaker/gate/config/AuthConfig.groovy | 8 +++ .../services/internal/ClouddriverService.java | 58 +++++++++++++++++++ .../gate/plugins/test/GatePluginsFixture.kt | 3 +- gate-web/config/gate.yml | 2 +- .../gate/controllers/AuthController.groovy | 6 +- .../controllers/CredentialsController.groovy | 54 +++++++++++++++++ gradle.properties | 6 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 14 files changed, 281 insertions(+), 45 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 89a529b608..0f396a4cd1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,6 +8,7 @@ on: env: GRADLE_OPTS: -Dorg.gradle.daemon=false -Xmx6g -Xms6g + CONTAINER_REGISTRY: us-docker.pkg.dev/spinnaker-community/docker jobs: branch-build: @@ -16,14 +17,52 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: actions/setup-java@v1 with: - java-version: 11 - - uses: actions/cache@v1 + fetch-depth: 0 + - uses: actions/setup-java@v2 with: - path: ~/.gradle - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} - restore-keys: | - ${{ runner.os }}-gradle- + java-version: 11 + distribution: 'zulu' + cache: 'gradle' + - name: Prepare build variables + id: build_variables + run: | + echo ::set-output name=REPO::${GITHUB_REPOSITORY##*/} + echo ::set-output name=VERSION::"$(git describe --tags --abbrev=0 --match="v[0-9]*" | cut -c2-)-dev-${GITHUB_REF_NAME}-$(git rev-parse --short HEAD)-$(date --utc +'%Y%m%d%H%M')" - name: Build - run: ./gradlew build --stacktrace + env: + ORG_GRADLE_PROJECT_version: ${{ steps.build_variables.outputs.VERSION }} + run: ./gradlew build --stacktrace ${{ steps.build_variables.outputs.REPO }}-web:installDist + - name: Login to GAR + # Only run this on repositories in the 'spinnaker' org, not on forks. + if: startsWith(github.repository, 'spinnaker/') + uses: docker/login-action@v1 + # use service account flow defined at: https://github.com/docker/login-action#service-account-based-authentication-1 + with: + registry: us-docker.pkg.dev + username: _json_key + password: ${{ secrets.GAR_JSON_KEY }} + - name: Build and publish slim container image + # Only run this on repositories in the 'spinnaker' org, not on forks. + if: startsWith(github.repository, 'spinnaker/') + uses: docker/build-push-action@v2 + with: + context: . + file: Dockerfile.slim + push: true + tags: | + "${{ env.CONTAINER_REGISTRY }}/${{ steps.build_variables.outputs.REPO }}:${{ github.ref_name }}-latest-unvalidated" + "${{ env.CONTAINER_REGISTRY }}/${{ steps.build_variables.outputs.REPO }}:${{ steps.build_variables.outputs.VERSION }}-unvalidated" + "${{ env.CONTAINER_REGISTRY }}/${{ steps.build_variables.outputs.REPO }}:${{ github.ref_name }}-latest-unvalidated-slim" + "${{ env.CONTAINER_REGISTRY }}/${{ steps.build_variables.outputs.REPO }}:${{ steps.build_variables.outputs.VERSION }}-unvalidated-slim" + - name: Build and publish ubuntu container image + # Only run this on repositories in the 'spinnaker' org, not on forks. + if: startsWith(github.repository, 'spinnaker/') + uses: docker/build-push-action@v2 + with: + context: . + file: Dockerfile.ubuntu + push: true + tags: | + "${{ env.CONTAINER_REGISTRY }}/${{ steps.build_variables.outputs.REPO }}:${{ github.ref_name }}-latest-unvalidated-ubuntu" + "${{ env.CONTAINER_REGISTRY }}/${{ steps.build_variables.outputs.REPO }}:${{ steps.build_variables.outputs.VERSION }}-unvalidated-ubuntu" diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 5d9e00ec5a..41d84a9bcf 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -4,20 +4,44 @@ on: [ pull_request ] env: GRADLE_OPTS: -Dorg.gradle.daemon=false -Xmx6g -Xms6g + CONTAINER_REGISTRY: us-docker.pkg.dev/spinnaker-community/docker jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-java@v1 - with: - java-version: 11 - - uses: actions/cache@v1 - with: - path: ~/.gradle - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} - restore-keys: | - ${{ runner.os }}-gradle- - - name: Build - run: ./gradlew build + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - uses: actions/setup-java@v2 + with: + java-version: 11 + distribution: 'zulu' + cache: 'gradle' + - name: Prepare build variables + id: build_variables + run: | + echo ::set-output name=REPO::${GITHUB_REPOSITORY##*/} + echo ::set-output name=VERSION::"$(git describe --tags --abbrev=0 --match="v[0-9]*" | cut -c2-)-dev-pr-$(git rev-parse --short HEAD)-$(date --utc +'%Y%m%d%H%M')" + - name: Build + env: + ORG_GRADLE_PROJECT_version: ${{ steps.build_variables.outputs.VERSION }} + run: ./gradlew build ${{ steps.build_variables.outputs.REPO }}-web:installDist + - name: Build slim container image + uses: docker/build-push-action@v2 + with: + context: . + file: Dockerfile.slim + tags: | + "${{ env.CONTAINER_REGISTRY }}/${{ steps.build_variables.outputs.REPO }}:latest" + "${{ env.CONTAINER_REGISTRY }}/${{ steps.build_variables.outputs.REPO }}:${{ steps.build_variables.outputs.VERSION }}" + "${{ env.CONTAINER_REGISTRY }}/${{ steps.build_variables.outputs.REPO }}:latest-slim" + "${{ env.CONTAINER_REGISTRY }}/${{ steps.build_variables.outputs.REPO }}:${{ steps.build_variables.outputs.VERSION }}-slim" + - name: Build ubuntu container image + uses: docker/build-push-action@v2 + with: + context: . + file: Dockerfile.ubuntu + tags: | + "${{ env.CONTAINER_REGISTRY }}/${{ steps.build_variables.outputs.REPO }}:latest-ubuntu" + "${{ env.CONTAINER_REGISTRY }}/${{ steps.build_variables.outputs.REPO }}:${{ steps.build_variables.outputs.VERSION }}-ubuntu" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index af7af10d70..9aab554ac9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,16 +8,20 @@ on: env: GRADLE_OPTS: -Dorg.gradle.daemon=false -Xmx6g -Xms6g + CONTAINER_REGISTRY: us-docker.pkg.dev/spinnaker-community/docker jobs: release: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - run: git fetch --prune --unshallow - - uses: actions/setup-java@v1 + with: + fetch-depth: 0 + - uses: actions/setup-java@v2 with: java-version: 11 + distribution: 'zulu' + cache: 'gradle' - name: Assemble release info id: release_info env: @@ -28,8 +32,12 @@ jobs: echo ::set-output name=SKIP_RELEASE::${SKIP_RELEASE} echo ::set-output name=IS_CANDIDATE::${IS_CANDIDATE} echo ::set-output name=RELEASE_VERSION::${RELEASE_VERSION} + - name: Prepare build variables + id: build_variables + run: | + echo ::set-output name=REPO::${GITHUB_REPOSITORY##*/} + echo ::set-output name=VERSION::"$(git rev-parse --short HEAD)-$(date --utc +'%Y%m%d%H%M')" - name: Release build - if: steps.release_info.outputs.IS_CANDIDATE == 'false' env: ORG_GRADLE_PROJECT_version: ${{ steps.release_info.outputs.RELEASE_VERSION }} ORG_GRADLE_PROJECT_nexusPublishEnabled: true @@ -38,7 +46,46 @@ jobs: ORG_GRADLE_PROJECT_nexusPgpSigningKey: ${{ secrets.NEXUS_PGP_SIGNING_KEY }} ORG_GRADLE_PROJECT_nexusPgpSigningPassword: ${{ secrets.NEXUS_PGP_SIGNING_PASSWORD }} run: | - ./gradlew --info publishToNexus closeAndReleaseNexusStagingRepository + ./gradlew --info build ${{ steps.build_variables.outputs.REPO }}-web:installDist publishToNexus closeAndReleaseNexusStagingRepository + - name: Publish apt packages to Google Artifact Registry + env: + ORG_GRADLE_PROJECT_version: ${{ steps.release_info.outputs.RELEASE_VERSION }} + ORG_GRADLE_PROJECT_artifactRegistryPublishEnabled: true + GAR_JSON_KEY: ${{ secrets.GAR_JSON_KEY }} + run: | + ./gradlew --info publish + - name: Login to GAR + # Only run this on repositories in the 'spinnaker' org, not on forks. + if: startsWith(github.repository, 'spinnaker/') + uses: docker/login-action@v1 + # use service account flow defined at: https://github.com/docker/login-action#service-account-based-authentication-1 + with: + registry: us-docker.pkg.dev + username: _json_key + password: ${{ secrets.GAR_JSON_KEY }} + - name: Build and publish slim container image + # Only run this on repositories in the 'spinnaker' org, not on forks. + if: startsWith(github.repository, 'spinnaker/') + uses: docker/build-push-action@v2 + with: + context: . + file: Dockerfile.slim + push: true + tags: | + "${{ env.CONTAINER_REGISTRY }}/${{ steps.build_variables.outputs.REPO }}:${{ steps.release_info.outputs.RELEASE_VERSION }}-unvalidated" + "${{ env.CONTAINER_REGISTRY }}/${{ steps.build_variables.outputs.REPO }}:${{ steps.release_info.outputs.RELEASE_VERSION }}-unvalidated-slim" + "${{ env.CONTAINER_REGISTRY }}/${{ steps.build_variables.outputs.REPO }}:${{ steps.release_info.outputs.RELEASE_VERSION }}-${{ steps.build_variables.outputs.VERSION }}-unvalidated-slim" + - name: Build and publish ubuntu container image + # Only run this on repositories in the 'spinnaker' org, not on forks. + if: startsWith(github.repository, 'spinnaker/') + uses: docker/build-push-action@v2 + with: + context: . + file: Dockerfile.ubuntu + push: true + tags: | + "${{ env.CONTAINER_REGISTRY }}/${{ steps.build_variables.outputs.REPO }}:${{ steps.release_info.outputs.RELEASE_VERSION }}-unvalidated-ubuntu" + "${{ env.CONTAINER_REGISTRY }}/${{ steps.build_variables.outputs.REPO }}:${{ steps.release_info.outputs.RELEASE_VERSION }}-${{ steps.build_variables.outputs.VERSION }}-unvalidated-ubuntu" - name: Create release if: steps.release_info.outputs.SKIP_RELEASE == 'false' uses: actions/create-release@v1 diff --git a/.github/workflows/release_info.sh b/.github/workflows/release_info.sh index 3cac206d10..0cadf01b76 100755 --- a/.github/workflows/release_info.sh +++ b/.github/workflows/release_info.sh @@ -3,7 +3,6 @@ # Only look to the latest release to determine the previous tag -- this allows us to skip unsupported tag formats (like `version-1.0.0`) export PREVIOUS_TAG=`curl --silent "https://api.github.com/repos/$1/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/'` echo "PREVIOUS_TAG=$PREVIOUS_TAG" - export NEW_TAG=${GITHUB_REF/refs\/tags\//} echo "NEW_TAG=$NEW_TAG" export CHANGELOG=`git log $NEW_TAG...$PREVIOUS_TAG --oneline` @@ -31,5 +30,6 @@ SEMVER_REGEX="\ # Used in downstream steps to determine if the release should be marked as a "prerelease" and if the build should build candidate release artifacts export IS_CANDIDATE=`[[ $NEW_TAG =~ $SEMVER_REGEX && ! -z ${BASH_REMATCH[4]} ]] && echo "true" || echo "false"` +# This is the version string we will pass to the build, trim off leading 'v' if present export RELEASE_VERSION=`[[ $NEW_TAG =~ $SEMVER_REGEX ]] && echo "${NEW_TAG:1}" || echo "${NEW_TAG}"` echo "RELEASE_VERSION=$RELEASE_VERSION" diff --git a/.mergify.yml b/.mergify.yml index 9acb7204b7..3b41473867 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -1,3 +1,8 @@ +queue_rules: + - name: default + conditions: + - status-success=build + pull_request_rules: - name: Automatically merge on CI success and review conditions: @@ -6,9 +11,9 @@ pull_request_rules: - "label=ready to merge" - "approved-reviews-by=@oss-approvers" actions: - merge: + queue: method: squash - strict: smart + name: default label: add: ["auto merged"] - name: Automatically merge release branch changes on CI success and release manager review @@ -18,9 +23,9 @@ pull_request_rules: - "label=ready to merge" - "approved-reviews-by=@release-managers" actions: - merge: + queue: method: squash - strict: smart + name: default label: add: ["auto merged"] # This rule exists to handle release branches that are still building using Travis CI instead of @@ -32,9 +37,9 @@ pull_request_rules: - "label=ready to merge" - "approved-reviews-by=@release-managers" actions: - merge: + queue: method: squash - strict: smart + name: default label: add: ["auto merged"] - name: Automatically merge PRs from maintainers on CI success and review @@ -44,9 +49,9 @@ pull_request_rules: - "label=ready to merge" - "author=@oss-approvers" actions: - merge: + queue: method: squash - strict: smart + name: default label: add: ["auto merged"] - name: Automatically merge autobump PRs on CI success @@ -56,9 +61,9 @@ pull_request_rules: - "label~=autobump-*" - "author:spinnakerbot" actions: - merge: + queue: method: squash - strict: smart + name: default label: add: ["auto merged"] - name: Request reviews for autobump PRs on CI failure diff --git a/build.gradle b/build.gradle index ee5fc3f15d..0dad63c283 100644 --- a/build.gradle +++ b/build.gradle @@ -26,7 +26,7 @@ allprojects { } dependencies { - implementation platform("io.spinnaker.kork:kork-bom:$korkVersion") + implementation enforcedPlatform("io.spinnaker.kork:kork-bom:$korkVersion") annotationProcessor platform("io.spinnaker.kork:kork-bom:$korkVersion") annotationProcessor("org.springframework.boot:spring-boot-configuration-processor") testAnnotationProcessor platform("io.spinnaker.kork:kork-bom:$korkVersion") diff --git a/gate-core/src/main/groovy/com/netflix/spinnaker/gate/config/AuthConfig.groovy b/gate-core/src/main/groovy/com/netflix/spinnaker/gate/config/AuthConfig.groovy index 081504e09e..456e0a69b1 100644 --- a/gate-core/src/main/groovy/com/netflix/spinnaker/gate/config/AuthConfig.groovy +++ b/gate-core/src/main/groovy/com/netflix/spinnaker/gate/config/AuthConfig.groovy @@ -100,6 +100,8 @@ class AuthConfig { http .requestMatcher(requestMatcherProvider.requestMatcher()) .authorizeRequests() + .antMatchers("/error").permitAll() + .antMatchers('/favicon.ico').permitAll() .antMatchers("/resources/**").permitAll() .antMatchers("/images/**").permitAll() .antMatchers("/js/**").permitAll() @@ -156,6 +158,8 @@ class AuthConfig { http .requestMatcher(requestMatcherProvider.requestMatcher()) .authorizeRequests() + .antMatchers("/error").permitAll() + .antMatchers('/favicon.ico').permitAll() .antMatchers("/resources/**").permitAll() .antMatchers("/images/**").permitAll() .antMatchers("/js/**").permitAll() @@ -211,6 +215,8 @@ class AuthConfig { http .requestMatcher(requestMatcherProvider.requestMatcher()) .authorizeRequests() + .antMatchers("/error").permitAll() + .antMatchers('/favicon.ico').permitAll() .antMatchers("/resources/**").permitAll() .antMatchers("/images/**").permitAll() .antMatchers("/js/**").permitAll() @@ -257,6 +263,8 @@ class AuthConfig { http .requestMatcher(requestMatcherProvider.requestMatcher()) .authorizeRequests() + .antMatchers("/error").permitAll() + .antMatchers('/favicon.ico').permitAll() .antMatchers("/resources/**").permitAll() .antMatchers("/images/**").permitAll() .antMatchers("/js/**").permitAll() diff --git a/gate-core/src/main/java/com/netflix/spinnaker/gate/services/internal/ClouddriverService.java b/gate-core/src/main/java/com/netflix/spinnaker/gate/services/internal/ClouddriverService.java index 93f84a014f..ec4390716f 100644 --- a/gate-core/src/main/java/com/netflix/spinnaker/gate/services/internal/ClouddriverService.java +++ b/gate-core/src/main/java/com/netflix/spinnaker/gate/services/internal/ClouddriverService.java @@ -12,6 +12,7 @@ import java.util.Map; import retrofit.client.Response; import retrofit.http.Body; +import retrofit.http.DELETE; import retrofit.http.GET; import retrofit.http.Headers; import retrofit.http.POST; @@ -31,6 +32,21 @@ public interface ClouddriverService { @GET("/credentials/{account}") AccountDetails getAccount(@Path("account") String account); + @GET("/credentials/type/{type}") + List getAccountDefinitionsByType( + @Path("type") String type, + @Query("limit") Integer limit, + @Query("startingAccountName") String startingAccountName); + + @POST("/credentials") + AccountDefinition createAccountDefinition(@Body AccountDefinition accountDefinition); + + @PUT("/credentials") + AccountDefinition updateAccountDefinition(@Body AccountDefinition accountDefinition); + + @DELETE("/credentials/{account}") + Response deleteAccountDefinition(@Path("account") String account); + @GET("/task/{taskDetailsId}") Map getTaskDetails(@Path("taskDetailsId") String taskDetailsId); @@ -493,4 +509,46 @@ public void setCloudProvider(String cloudProvider) { private String cloudProvider; private final Map details = new HashMap(); } + + /** + * Wrapper type for Clouddriver account definitions. Clouddriver account definitions implement + * {@code CredentialsDefinition}, and its type discriminator is present in a property named + * {@code @type}. An instance of an account definition may have fairly different properties than + * its corresponding {@code AccountCredentials} instance. Account definitions must store all the + * relevant properties unchanged while {@link Account} and {@link AccountDetails} may summarize + * and remove data returned from their corresponding APIs. Account definitions must be transformed + * by a {@code CredentialsParser} before their corresponding credentials may be used by + * Clouddriver. + */ + class AccountDefinition { + private final Map details = new HashMap<>(); + private String type; + private String name; + + @JsonAnyGetter + public Map details() { + return details; + } + + @JsonAnySetter + public void set(String name, Object value) { + details.put(name, value); + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } } diff --git a/gate-plugins-test/src/test/kotlin/com/netflix/spinnaker/gate/plugins/test/GatePluginsFixture.kt b/gate-plugins-test/src/test/kotlin/com/netflix/spinnaker/gate/plugins/test/GatePluginsFixture.kt index 540f9971c3..af93403da2 100644 --- a/gate-plugins-test/src/test/kotlin/com/netflix/spinnaker/gate/plugins/test/GatePluginsFixture.kt +++ b/gate-plugins-test/src/test/kotlin/com/netflix/spinnaker/gate/plugins/test/GatePluginsFixture.kt @@ -63,7 +63,8 @@ class GatePluginsFixture : PluginsTckFixture, GateTestService() { plugins.mkdir() enabledPlugin = buildPlugin("com.netflix.gate.enabled.plugin", ">=1.0.0") disabledPlugin = buildPlugin("com.netflix.gate.disabled.plugin", ">=1.0.0") - versionNotSupportedPlugin = buildPlugin("com.netflix.gate.version.not.supported.plugin", ">=2.0.0") + // Make it very unlikely that the version of gate satisfies this requirement + versionNotSupportedPlugin = buildPlugin("com.netflix.gate.version.not.supported.plugin", "=0.0.9") } } diff --git a/gate-web/config/gate.yml b/gate-web/config/gate.yml index 2272e1f0cb..0b4aa4a6b0 100644 --- a/gate-web/config/gate.yml +++ b/gate-web/config/gate.yml @@ -44,7 +44,7 @@ services: enabled: true keel: baseUrl: http://localhost:8087 - enabled: true + enabled: false fiat: enabled: false flapjack: diff --git a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/AuthController.groovy b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/AuthController.groovy index 986ef0e6a2..353aba1e03 100644 --- a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/AuthController.groovy +++ b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/AuthController.groovy @@ -42,13 +42,13 @@ class AuthController { static List LOGOUT_MESSAGES = [ "Hasta la vista, baby.", - "Frankly my dear, I don't give a damn.", + "Frankly, my dear, I don't give a damn.", "For the Watch.", "Go ahead, make my day.", - "Louis, I think this is a start of a beautiful friendship!", + "Louis, I think this is the beginning of a beautiful friendship.", "Roads? Where we're going we don't need roads!", "Say hello to my little friend!", - "I wish we could chat longer, but I'm having an old friend for dinner. Bye!", + "I do wish we could chat longer, but... I'm having an old friend for dinner. Bye.", "Hodor. :(", ] private final Random r = new Random() diff --git a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/CredentialsController.groovy b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/CredentialsController.groovy index f6e77592d9..1b7093b79b 100644 --- a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/CredentialsController.groovy +++ b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/CredentialsController.groovy @@ -24,10 +24,17 @@ import com.netflix.spinnaker.gate.services.AccountLookupService import com.netflix.spinnaker.gate.services.internal.ClouddriverService import com.netflix.spinnaker.gate.services.internal.ClouddriverService.Account import com.netflix.spinnaker.gate.services.internal.ClouddriverService.AccountDetails +import com.netflix.spinnaker.kork.annotations.Alpha import com.netflix.spinnaker.security.User import io.swagger.annotations.ApiOperation +import io.swagger.annotations.ApiParam import org.springframework.beans.factory.annotation.Autowired +import org.springframework.web.bind.annotation.DeleteMapping +import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.PutMapping +import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestHeader import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestMethod @@ -44,6 +51,9 @@ class CredentialsController { @Autowired AllowedAccountsSupport allowedAccountsSupport + @Autowired + ClouddriverService clouddriverService + @Autowired ObjectMapper objectMapper @@ -79,4 +89,48 @@ class CredentialsController { @RequestHeader(value = "X-RateLimit-App", required = false) String sourceApp) { return getAccountDetailsWithAuthorizedFlag(user).find { it.name == account } } + + @GetMapping('/type/{accountType}') + @ApiOperation('Looks up account definitions by type.') + @Alpha + List getAccountsByType( + @ApiParam(value = 'Value of the "@type" key for accounts to search for.', example = 'kubernetes') + @PathVariable String accountType, + @ApiParam('Maximum number of entries to return in results. Used for pagination.') + @RequestParam OptionalInt limit, + @ApiParam('Account name to start account definition listing from. Used for pagination.') + @RequestParam Optional startingAccountName + ) { + clouddriverService.getAccountDefinitionsByType(accountType, limit.isPresent() ? limit.getAsInt() : null, startingAccountName.orElse(null)) + } + + @PostMapping + @ApiOperation('Creates a new account definition.') + @Alpha + ClouddriverService.AccountDefinition createAccount( + @ApiParam('Account definition body including a discriminator field named "type" with the account type.') + @RequestBody ClouddriverService.AccountDefinition accountDefinition + ) { + clouddriverService.createAccountDefinition(accountDefinition) + } + + @PutMapping + @ApiOperation('Updates an existing account definition.') + @Alpha + ClouddriverService.AccountDefinition updateAccount( + @ApiParam('Account definition body including a discriminator field named "type" with the account type.') + @RequestBody ClouddriverService.AccountDefinition accountDefinition + ) { + clouddriverService.updateAccountDefinition(accountDefinition) + } + + @DeleteMapping('/{accountName}') + @ApiOperation('Deletes an account definition by name.') + @Alpha + void deleteAccount( + @ApiParam('Name of account definition to delete.') + @PathVariable String accountName + ) { + clouddriverService.deleteAccountDefinition(accountName) + } } diff --git a/gradle.properties b/gradle.properties index f4686852e6..46647d8dc1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,10 +1,10 @@ enablePublishing=false -fiatVersion=1.28.0 +fiatVersion=1.30.0 includeProviders=basic,iap,ldap,oauth2,saml,x509 -korkVersion=7.126.0 +korkVersion=7.134.0 kotlinVersion=1.4.0 org.gradle.parallel=true -spinnakerGradleVersion=8.16.0 +spinnakerGradleVersion=8.23.0 targetJava11=true # To enable a composite reference to a project, set the diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index be52383ef4..28ff446a21 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists