4

Following this guide and this code example, I have this in my application-deploy.yaml:

spring:following
  cloud:
    gcp:
      project-id: ${PROJECT_ID}  # Set during build-process (Docker)
      sql:
        enabled: true
        database-name: xxxxxxxxxx
        instance-connection-name: ${sm://CLOUD_SQL_INSTANCE_CONNECTION_NAME}

  datasource:
    driver-class-name: org.postgresql.Driver
    username: ${sm://JDBC_DATABASE_USERNAME}
    password: ${sm://JDBC_DATABASE_PASSWORD}
    name: postgres

and the following dependencies in my pom.xml:

<dependency>
    <groupId>com.google.cloud</groupId>
    <artifactId>spring-cloud-gcp-starter</artifactId>
</dependency>

<dependency>
    <groupId>com.google.cloud</groupId>
    <artifactId>spring-cloud-gcp-starter-sql-postgresql</artifactId>
</dependency>

<dependency>
    <groupId>com.google.cloud</groupId>
    <artifactId>spring-cloud-gcp-starter-secretmanager</artifactId>
</dependency>

However, after running gcloud run deploy for the service, it immediatelly crahes because the environment variables are not properly resolved:

Caused by: java.lang.IllegalArgumentException: [//CLOUD_SQL_INSTANCE_CONNECTION_NAME] Cloud SQL connection name is invalid, expected string in the form of "<PROJECT_ID>:<REGION_ID>:<INSTANCE_ID>".
    at com.google.common.base.Preconditions.checkArgument(Preconditions.java:145) ~[guava-31.1-jre.jar!/:na]
    at com.google.cloud.sql.core.CloudSqlInstance.<init>(CloudSqlInstance.java:135) ~[jdbc-socket-factory-core-1.11.0.jar!/:na]
    at com.google.cloud.sql.core.CoreSocketFactory.lambda$getCloudSqlInstance$0(CoreSocketFactory.java:165) ~[jdbc-socket-factory-core-1.11.0.jar!/:na]
    at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708) ~[na:na]
    ...

The following is in my bootstrap.yaml

spring:
  cloud:
    gcp:
      project-id: ${PROJECT_ID}
      secretmanager:
        project-id: ${PROJECT_ID}

For good measure, and according to another source, I'm throwing in some additional dependency-management in order to make sure `` is available:

<!-- ... -->

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
        </dependency>

<!-- ... -->

<dependencyManagement>
    <dependencies>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring.cloud-version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>

    </dependencies>
</dependencyManagement>

I have added Secret Manager Admin roles to the SA principals:

enter image description here

What am I missing here?

Stefan Falk
  • 23,898
  • 50
  • 191
  • 378
  • Did you check the permission of the runtime service account? – guillaume blaquiere Apr 20 '23 at 14:17
  • @guillaumeblaquiere Well, I have now added the role `Secret Manager Secret Accessor` to `firebase-adminsdk-blwgo@.iam.gserviceaccount.com` and re-deployed the application but the result is the same. This does not look like a permission issue.. or I hope it isn't because `IllegalArgumentException` is not the exception I'd expect in that case.. – Stefan Falk Apr 20 '23 at 15:02
  • From https://github.com/GoogleCloudPlatform/spring-cloud-gcp/issues/1229 I followed this demo: https://github.com/JoeWang1127/demo-cloud-sql I don't see what's different on my end. – Stefan Falk Apr 20 '23 at 15:05
  • Can you try something? Renane your application.yml to bootstrap.yml, and try again. (and let me know) – guillaume blaquiere Apr 20 '23 at 15:53
  • @guillaumeblaquiere I actually have a `bootstrap.yaml` (updated the question) where I am setting the `project-id` for the Secret Manager similar to the code example I posted in my last comment. But coming back to the Service Account: _Which one_ is actually the one that needs the `Secret Manager Secret Accessor` role? I have one named "_Compute Engine default service account_". Assuming this is the right one, I went to IAM and added said role to `-compute@developer.gserviceaccount.com`. Is that correct? – Stefan Falk Apr 20 '23 at 16:03
  • By default, yes, Cloud Run use the Compte engine default service account. And this one must have the secret accessor role to access them. (it's not by default, you have to add explicitly this role). I tested long time ago the integration, and previously it worked only with the bootstrap file and not the application file. That's why I proposed you to put your config in that bootstrap and to see if it's better. If you have a doubt about the permission, have a look to the logs, you should see a 403 or 401 error if it's the case. – guillaume blaquiere Apr 20 '23 at 18:32
  • @guillaumeblaquiere How or where should I see HTTP 401/403? All I can make out is the exception from above. This is getting frustrating. What would I have to define in my `bootstrap.yaml` to make it work? Everything else looks good to me.. – Stefan Falk Apr 20 '23 at 21:34
  • Put your sm://xxx in the bootstrap to see if it solves your issue. In addition, in Cloud Logging on your service, you should see 401 or 403 is you have access issue to Secret Manager. – guillaume blaquiere Apr 21 '23 at 08:01
  • @guillaumeblaquiere I've tested it (among other things) with the example-application (https://github.com/falk-stefan/spring-boot-gcp-secret-manager) which I build for https://github.com/GoogleCloudPlatform/spring-cloud-gcp/issues/1762 without success. I am not seeing any HTTP errors. The way it looks to me the application doesn't even attempt to resolve these secrets. I'd assume that in such a case the Secret Manager plugin would throw an exception and kill the Spring Application on startup. It looks to me as if `//SECRET_NAME` is simply interpreted as the default value for env-var `sm`.. – Stefan Falk Apr 21 '23 at 08:53
  • 1
    This is one of those issues where I know the solution is going to be something stupidly obvious and I'm just not seeing it.. wasting over two days on this issue already :'D – Stefan Falk Apr 21 '23 at 08:53
  • Try taking a step back. Take the bootstrap.yml out, and set the actual GCP project id in the application.yml, instead of the token. When using secret manager in properties files, i tend to use the full URL, for example: ${sm://[project-id]/[secret-key]/latest}. Sanity checks: Are you including spring-cloud-gcp-dependencies to your dependency management? Do those secret keys exist for the provided GCP project id? – MiseryMachine Jun 02 '23 at 14:34

1 Answers1

2

It turned out to be a simple issue: I wasn't aware that we have to "import" the sm:// prefix in the application.yaml (do yourself a favor and use .yaml instead or .properties):

spring:
  config:
    import: "sm://"

You can find a full example here: spring-boot-gcp-secret-manager

Stefan Falk
  • 23,898
  • 50
  • 191
  • 378