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

Expose overlay flag #1361

Merged
merged 3 commits into from
May 15, 2024
Merged

Expose overlay flag #1361

merged 3 commits into from
May 15, 2024

Conversation

EronWright
Copy link
Contributor

@EronWright EronWright commented May 15, 2024

Description

Fixes #1360

Checklist

  • I have added tests that prove my fix is effective or that my feature works
  • Yes, there are changes in this PR that warrants bumping the Pulumi Service API version

@EronWright EronWright requested a review from t0yv0 May 15, 2024 18:20
@t0yv0
Copy link
Member

t0yv0 commented May 15, 2024

Looks fairly straightforward, I don't see why not expose it. Hope you had a chance to try it end to end to confirm it works as expected 👍

Copy link
Member

@t0yv0 t0yv0 left a comment

Choose a reason for hiding this comment

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

Looks fairly straightforward, thank you!

I

@EronWright EronWright merged commit c0f1a50 into main May 15, 2024
22 checks passed
@EronWright EronWright deleted the issue-1360-overlays branch May 15, 2024 21:28
EronWright added a commit to pulumi/pulumi-kubernetes that referenced this pull request May 24, 2024
<!--Thanks for your contribution. See [CONTRIBUTING](CONTRIBUTING.md)
    for Pulumi's contribution guidelines.

    Help us merge your changes more quickly by adding more details such
    as labels, milestones, and reviewers.-->

### Proposed changes

This PR updates the pulumi-java dependency from v0.8.0 to v0.12.0.

Notable changes:
- pulumi/pulumi-java#1356
- pulumi/pulumi-java#1361
- pulumi/pulumi-java#1228

### Related issues (optional)

<!--Refer to related PRs or issues: #1234, or 'Fixes #1234' or 'Closes
#1234'.
Or link to full URLs to issues or pull requests in other GitHub
repositories. -->
EronWright added a commit to pulumi/pulumi-kubernetes that referenced this pull request May 28, 2024
<!--Thanks for your contribution. See [CONTRIBUTING](CONTRIBUTING.md)
    for Pulumi's contribution guidelines.

    Help us merge your changes more quickly by adding more details such
    as labels, milestones, and reviewers.-->

### Proposed changes

<!--Give us a brief description of what you've done and what it solves.
-->
Implements `CustomResource` for Java SDK as an overlay, for parity with
other SDKs. An overlay is necessary for [this
reason](#2787 (comment)).

#### Features
Supports two usage modes. Note that each SDK is slightly different in
which mode(s) it supports.
- **untyped**, where you use `CustomResource` directly and set arbitrary
fields on the object.
- **typed**, where you subclass `CustomResource` to create a
strongly-typed wrapper representing a CRD.

A "Patch" variant is also provided.

Provides a "getter" method in both untyped and typed mode. Note that the
patch variant doesn't have a getter in most SDKs.

#### Summary of Changes
- new example: `examples/java/customresource`
- new resource: `apiextensions.CustomResource`
- new resource: `apiextensions.CustomResourcePatch`
- new dependency: `net.bytebuddy:byte-buddy:1.14.15`
- new dependency:`com.google.guava:guava:32.1.2-jre` (used by core
already)

TODOs:

- [x] Prerequisite: Pulumi Java 0.12
(pulumi/pulumi-java#1361)
- [x] `Get` method (untyped and typed)
- [x] Patch variant
- [x] Integration test or in-tree example

### Related issues (optional)

<!--Refer to related PRs or issues: #1234, or 'Fixes #1234' or 'Closes
#1234'.
Or link to full URLs to issues or pull requests in other GitHub
repositories. -->
Closes #2787 

### API

- `CustomResource` - to be used directly or subclassed to expose typed
output properties
- `CustomResourceArgs` - the final class to be used directly in the
untyped use-case
- `CustomResourceArgsBase` - the abstract base class for custom resource
args, to expose typed input properties
- `CustomResourceArgsBase.Builder<T,B>` - the base class for your custom
args builder
- `CustomResourcePatch` - to be used directly or subclassed to expose
typed output properties
- `CustomResourcePatchArgs` - the final class to be used directly in the
untyped use-case
- `CustomResourcePatchArgsBase` - the abstract base class for custom
resource args, to expose typed input properties
- `CustomResourcePatchArgsBase.Builder<T,B>` - the base class for your
custom args builder

### Implementation Details

#### Working with Untyped Inputs
The core Pulumi Java SDK has no support for dynamic inputs; it relies
exclusively on reflection of the supplied `InputArgs` subclass (see:
[`InputArgs::toMapAsync`](https://github.com/pulumi/pulumi-java/blob/f887fbc869974ae7d9049cb4a5b62f51b1151dcb/sdk/java/pulumi/src/main/java/com/pulumi/resources/InputArgs.java#L63)).
To support the "untyped" mode, this implementation codegens a class at
runtime using bytebuddy.

#### Builder Inheritance
The Java SDK leans on the fluent builder pattern, and there are special
challenges in designing a builder that is amenable to inheritance. This
implementation uses generics as seen
[here](https://egalluzzo.blogspot.com/2010/06/using-inheritance-with-fluent.html).

## Example

Here's an example program to deploy two [cert-manager
issuers](https://cert-manager.io/docs/concepts/issuer/).
- `issuer1` is untyped, and calls `otherFields(...)` on the builder to
set the `spec`.
- `issuer2` is typed, calls `spec(...)` on a subclassed builder to set
the `spec`, and uses the typed `spec` output. Note that the `apiVersion`
and `kind` are set automatically.

The code seen in `Inputs` and `Outputs` section would, in practice, be
generated by pulumi-java-gen based on a schema file.

The untyped and typed getter variants are also demonstrated.

```java
package myproject;

import java.util.Map;
import java.util.Objects;
import java.util.Optional;

import javax.annotation.Nullable;

import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.core.annotations.CustomType;
import com.pulumi.core.annotations.Export;
import com.pulumi.core.annotations.Import;
import com.pulumi.kubernetes.apiextensions.CustomResource;
import com.pulumi.kubernetes.apiextensions.CustomResourceArgs;
import com.pulumi.kubernetes.apiextensions.CustomResourceArgsBase;
import com.pulumi.kubernetes.meta.v1.inputs.ObjectMetaArgs;

public class App {
    public static void main(String[] args) {
        Pulumi.run(ctx -> {
            var issuer1 = new CustomResource("issuer1", CustomResourceArgs.builder()
                    .apiVersion("cert-manager.io/v1")
                    .kind("Issuer")
                    .metadata(ObjectMetaArgs.builder().build())
                    .otherFields(Map.of("spec", Map.of(
                            "selfSigned", Map.of())))
                    .build());
            ctx.export("issuer1_name", issuer1.metadata().applyValue(s -> s.name()));

            var get1 = CustomResource.get("get1", "cert-manager.io/v1", "Issuer", issuer1.id(), null);
                
            var issuer2 = new Issuer("issuer2", IssuerArgs.builder()
                    .metadata(ObjectMetaArgs.builder().build())
                    .spec(Inputs.IssuerSpecArgs.builder()
                            .selfSigned(Inputs.SelfSignedArgs.builder().build())
                            .build())
                    .build());

            ctx.export("issuer2_name", issuer2.metadata().applyValue(s -> s.name()));
            ctx.export("issuer2_selfsigned", issuer2.spec().applyValue(s -> s.selfSigned().isPresent()));

            var get2 = Issuer.get("get2", issuer2.id(), null);
            ctx.export("get2_selfsigned", get2.spec().applyValue(s -> s.selfSigned().isPresent()));
        });
    }
}

class Issuer extends CustomResource {
    /**
     * The spec of the Issuer.
     */
    @export(name = "spec", refs = { Outputs.IssuerSpec.class }, tree = "[0]")
    private Output<Outputs.IssuerSpec> spec;

    public Output<Outputs.IssuerSpec> spec() {
        return this.spec;
    }

    public Issuer(String name, @nullable IssuerArgs args) {
        super(name, makeArgs(args));
    }

    public Issuer(String name, @nullable IssuerArgs args,
            @nullable com.pulumi.resources.CustomResourceOptions options) {
        super(name, makeArgs(args), options);
    }

    protected Issuer(String name, Output<String> id,
            @nullable com.pulumi.resources.CustomResourceOptions options) {
        super(name, "cert-manager.io/v1", "Issuer", id, options);
    }

    private static IssuerArgs makeArgs(@nullable IssuerArgs args) {
        var builder = args == null ? IssuerArgs.builder() : IssuerArgs.builder(args);
        return builder
            .apiVersion("cert-manager.io/v1")
            .kind("Issuer")
            .build();
    }

    public static Issuer get(String name, Output<String> id, @nullable com.pulumi.resources.CustomResourceOptions options) {
        return new Issuer(name, id, options);
    }
}

class IssuerArgs extends CustomResourceArgsBase {
    /**
     * The spec of the Issuer.
     */
    @import(name = "spec", required = true)
    @nullable 
    private Output<Inputs.IssuerSpecArgs> spec;

    public static Builder builder() {
        return new Builder();
    }

    public static Builder builder(IssuerArgs defaults) {
        return new Builder(defaults);
    }

    static class Builder extends CustomResourceArgsBase.Builder<IssuerArgs, Builder> {
        public Builder() {
            super(new IssuerArgs());
        }

        public Builder(IssuerArgs defaults) {
            super(new IssuerArgs(), defaults);
        }

        public Builder spec(@nullable Output<Inputs.IssuerSpecArgs> spec) {
            $.spec = spec;
            return this;
        }

        public Builder spec(Inputs.IssuerSpecArgs spec) {
            return spec(Output.of(spec));
        }

        @OverRide
        protected void copy(IssuerArgs args) {
            super.copy(args);
            $.spec = args.spec;
        }
    }
}

class Inputs {
    public static final class IssuerSpecArgs extends com.pulumi.resources.ResourceArgs {

        public static final IssuerSpecArgs Empty = new IssuerSpecArgs();

        @import(name = "selfSigned", required = true)
        private @nullable Output<SelfSignedArgs> selfSigned;

        public Optional<Output<SelfSignedArgs>> selfSigned() {
            return Optional.ofNullable(this.selfSigned);
        }

        private IssuerSpecArgs() {
        }

        private IssuerSpecArgs(IssuerSpecArgs $) {
            this.selfSigned = $.selfSigned;
        }

        public static Builder builder() {
            return new Builder();
        }

        public static Builder builder(IssuerSpecArgs defaults) {
            return new Builder(defaults);
        }

        public static final class Builder {
            private IssuerSpecArgs $;

            public Builder() {
                $ = new IssuerSpecArgs();
            }

            public Builder(IssuerSpecArgs defaults) {
                $ = new IssuerSpecArgs(Objects.requireNonNull(defaults));
            }

            public Builder selfSigned(@nullable Output<SelfSignedArgs> selfSigned) {
                $.selfSigned = selfSigned;
                return this;
            }

            public Builder selfSigned(SelfSignedArgs selfSigned) {
                return selfSigned(Output.of(selfSigned));
            }

            public IssuerSpecArgs build() {
                return $;
            }
        }
    }

    public static final class SelfSignedArgs extends com.pulumi.resources.ResourceArgs {

        public static final SelfSignedArgs Empty = new SelfSignedArgs();

        private SelfSignedArgs() {
        }

        private SelfSignedArgs(SelfSignedArgs $) {
        }

        public static Builder builder() {
            return new Builder();
        }

        public static Builder builder(SelfSignedArgs defaults) {
            return new Builder(defaults);
        }

        public static final class Builder {
            private SelfSignedArgs $;

            public Builder() {
                $ = new SelfSignedArgs();
            }

            public Builder(SelfSignedArgs defaults) {
                $ = new SelfSignedArgs(Objects.requireNonNull(defaults));
            }

            public SelfSignedArgs build() {
                return $;
            }
        }
    }
}

class Outputs {
    @CustomType
    static final class IssuerSpec {

        private @nullable SelfSigned selfSigned;

        private IssuerSpec() {
        }

        public Optional<SelfSigned> selfSigned() {
            return Optional.ofNullable(this.selfSigned);
        }

        public static Builder builder() {
            return new Builder();
        }

        public static Builder builder(IssuerSpec defaults) {
            return new Builder(defaults);
        }

        @CustomType.Builder
        public static final class Builder {
            private @nullable SelfSigned selfSigned;

            public Builder() {
            }

            public Builder(IssuerSpec defaults) {
                Objects.requireNonNull(defaults);
                this.selfSigned = defaults.selfSigned;
            }

            @CustomType.Setter
            public Builder selfSigned(@nullable SelfSigned selfSigned) {
                this.selfSigned = selfSigned;
                return this;
            }

            public IssuerSpec build() {
                final var _resultValue = new IssuerSpec();
                _resultValue.selfSigned = selfSigned;
                return _resultValue;
            }
        }
    }

    @CustomType
    static final class SelfSigned {

        private SelfSigned() {
        }

        public static Builder builder() {
            return new Builder();
        }

        public static Builder builder(SelfSigned defaults) {
            return new Builder(defaults);
        }

        @CustomType.Builder
        public static final class Builder {
            public Builder() {
            }

            public Builder(SelfSigned defaults) {
                Objects.requireNonNull(defaults);
            }

            public SelfSigned build() {
                final var _resultValue = new SelfSigned();
                return _resultValue;
            }
        }
    }
}

```

Gives the expected output:
```
❯ pulumi preview --diff
Previewing update (dev)

+ pulumi:pulumi:Stack: (create)
    [urn=urn:pulumi:dev::issue-2787-javaa::pulumi:pulumi:Stack::issue-2787-javaa-dev]
    + kubernetes:cert-manager.io/v1:Issuer: (create)
        [urn=urn:pulumi:dev::issue-2787-javaa::kubernetes:cert-manager.io/v1:Issuer::issuer1]
        [provider=urn:pulumi:dev::issue-2787-javaa::pulumi:providers:kubernetes::default_0_0_16_SNAPSHOT::04da6b54-80e4-46f7-96ec-b56ff0331ba9]
        apiVersion: "cert-manager.io/v1"
        kind      : "Issuer"
        metadata  : {
            annotations: {
                pulumi.com/autonamed: "true"
            }
            name       : "issuer1-dcda28b8"
            namespace  : "default"
        }
        spec      : {
            selfSigned: {}
        }
    + kubernetes:cert-manager.io/v1:Issuer: (create)
        [urn=urn:pulumi:dev::issue-2787-javaa::kubernetes:cert-manager.io/v1:Issuer::issuer2]
        [provider=urn:pulumi:dev::issue-2787-javaa::pulumi:providers:kubernetes::default_0_0_16_SNAPSHOT::04da6b54-80e4-46f7-96ec-b56ff0331ba9]
        apiVersion: "cert-manager.io/v1"
        kind      : "Issuer"
        metadata  : {
            annotations: {
                pulumi.com/autonamed: "true"
            }
            name       : "issuer2-a0d8c527"
            namespace  : "default"
        }
        spec      : {
            selfSigned: {}
        }
    --outputs:--        
    issuer1_name      : "issuer1-dcda28b8"
    issuer2_name      : "issuer2-a0d8c527"
    issuer2_selfsigned: true
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[codegen] overlay support
2 participants