Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for using env vars to override cassandra datastax driver configuration properties #389

Merged
merged 10 commits into from
Nov 2, 2023

Conversation

wetted
Copy link
Contributor

@wetted wetted commented Oct 27, 2023

fixes #240

@wetted wetted self-assigned this Oct 27, 2023
@wetted
Copy link
Contributor Author

wetted commented Oct 27, 2023

Still needs new documentation, and I need to consider if there are corner cases to handle.

@wetted wetted added the type: bug Something isn't working label Oct 27, 2023
@wetted wetted requested a review from timyates October 30, 2023 16:55
@wetted wetted marked this pull request as ready for review October 30, 2023 16:55
@wetted wetted requested a review from sdelamo October 30, 2023 17:29
Copy link
Contributor

@timyates timyates left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not 100% but I think we can change direction here...

Declaring properties in tests (with ApplicationContext.run) do not seem to be overridden by env variables.

I suspect this is why we need to pull the Environment into the session factory.

But this is not how applications are configured in the real world 🤔

If we create a file in test/resources/application-envprops.properties containing:

configuration=props
micronaut.metrics.enabled=true
cassandra.default.basic.contact-points[0]=localhost:${cassandra.port}
cassandra.default.basic.session-name=defaultSession
cassandra.default.advanced.metrics.factory.class=MicrometerMetricsFactory
cassandra.default.advanced.metrics.session.enabled[0]=connected-nodes
cassandra.default.advanced.metrics.session.enabled[1]=cql-requests
cassandra.default.basic.load-balancing-policy.local-datacenter=datacenter1

and a file test/resources/application-envyaml.yaml containing:

micronaut:
  metrics:
    enabled: true
configuration: yaml
cassandra:
  default:
    basic:
      contact-points: ["localhost:${cassandra.port}"]
      session-name: defaultSession
      load-balancing-policy:
        local-datacenter: datacenter1
    advanced:
      metrics:
        factory:
          class: MicrometerMetricsFactory
        session:
          enabled: [connected-nodes, cql-requests]

We can change the test to: (after adding testRuntimeOnly(mn.snakeyaml) to the build)

    @Issue("https://github.com/micronaut-projects/micronaut-cassandra/issues/240")
    void "test metrics with overriding #configStyle with env vars"() {
        given:
        CassandraContainer cassandra = new CassandraContainer(DockerImageName.parse('cassandra:latest'))
        cassandra.start()

        // override: cassandra.default.basic.session-name=defaultSession
        def env = new EnvironmentVariables(
                "CASSANDRA_DEFAULT_BASIC_SESSION_NAME", "envSession",
                "CASSANDRA_PORT", "${cassandra.firstMappedPort}"
        )
        env.setup()

        ApplicationContext applicationContext = ApplicationContext.run("env$configStyle")

        when:
        CqlSession defaultCluster = applicationContext.getBean(CqlSession)
        PropertyResolver resolver = applicationContext.getBean(PropertyResolver)
        MeterRegistry meterRegistry = applicationContext.getBean(MeterRegistry)

        then:
        resolver.getRequiredProperty("configuration", String) == configStyle
        defaultCluster
        meterRegistry

        and:
        meterRegistry.meters.id.name.findAll { it.contains("envSession") } ==~ ['envSession.connected-nodes', 'envSession.cql-requests']

        cleanup:
        env.teardown()
        cassandra.stop()
        applicationContext.close()

        where:
        configStyle << ['props', 'yaml']
    }

I think we are a lot closer to testing how the application is run in the wild...

Doing it this way, we can change the session method to something like:

    /**
     * Creates the {@link CqlSessionBuilder} bean for the given configuration.
     *
     * @param configuration The cassandra configuration bean
     * @return A {@link CqlSession} bean
     */
    @EachBean(CassandraConfiguration.class)
    public CqlSessionBuilder session(CassandraConfiguration configuration) {
        try {
            return CqlSession.builder().withConfigLoader(new DefaultDriverConfigLoader(() -> {
                ConfigFactory.invalidateCaches();
                String prefix = configuration.getName();
                Map<String, Object> properties = this.resolver.getProperties(CassandraConfiguration.PREFIX + "." + prefix);
                // translate indexed properties for list values from Micronaut array index notation (i.e. foo[0]=bar)
                // to Datastax driver decimal notation (i.e. foo.0=bar)
                properties = properties.entrySet().stream()
                    .filter(e -> !(e.getValue() instanceof Collection<?>))
                    .collect((Collectors.toMap(e -> e.getKey().replaceAll("\\[(\\d+)]", ".$1"), Map.Entry::getValue)));
                return ConfigFactory.parseMap(properties).withFallback(ConfigFactory.load().getConfig(DefaultDriverConfigLoader.DEFAULT_ROOT_PATH));
            }));
        } catch (Exception e) {
            LOG.error(String.format("Failed to instantiate CQL session: %s", e.getMessage()), e);
            throw e;
        }
    }

This uses the evaluated properties instead of the RAW ones, and filters out any which have been converted into a collection by the micronaut properties consolodation

I think this works...

Let me know what you think?

@wetted
Copy link
Contributor Author

wetted commented Oct 31, 2023

Thanks @timyates great suggestions, and I think everything is resolved.

@wetted wetted requested a review from timyates October 31, 2023 17:14
src/main/docs/guide/additional.adoc Outdated Show resolved Hide resolved
@wetted wetted requested a review from timyates November 1, 2023 14:00
Copy link

sonarqubecloud bot commented Nov 1, 2023

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

100.0% 100.0% Coverage
0.0% 0.0% Duplication

@sdelamo sdelamo merged commit 6b0ee34 into master Nov 2, 2023
11 checks passed
@sdelamo sdelamo deleted the support-env-vars branch November 2, 2023 15:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug Something isn't working
Projects
No open projects
Status: Done
Development

Successfully merging this pull request may close these issues.

Cannot override default properties from application.yaml with environment variables.
3 participants