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 config to disable upsert behavior #3000

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 12 additions & 11 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

- Fix Release behavior to deep merge `valueYamlFiles` to match Helm. (https://github.com/pulumi/pulumi-kubernetes/pull/2963)
- Add field manager's name to server-side apply conflict errors. (https://github.com/pulumi/pulumi-kubernetes/pull/2983)
- Add `enableUpsert` provider configuration to control apply behavior on resource creation. (https://github.com/pulumi/pulumi-kubernetes/pull/3000)

## 4.11.0 (April 17, 2024)

Expand Down Expand Up @@ -57,32 +58,32 @@ Note that transformations aren't supported in this release (see https://github.c
- Fix option propagation in component resources (Go SDK) (https://github.com/pulumi/pulumi-kubernetes/pull/2709)

### Breaking Changes
In previous versions of the pulumi-kubernetes .NET SDK, the `ConfigFile` and `ConfigGroup` component resources inadvertently assigned the wrong parent to the child resource(s).
In previous versions of the pulumi-kubernetes .NET SDK, the `ConfigFile` and `ConfigGroup` component resources inadvertently assigned the wrong parent to the child resource(s).
This would happen when the component resource itself had a parent; the child would be assigned that same parent. This also had the effect of disregarding the component resource's provider in favor of the parent's provider.

For example, here's a before/after look at the component hierarchy:

Before:

```
├─ pkg:index:MyComponent parent
│ ├─ kubernetes:core/v1:ConfigMap cg-options-cg-options-cm-1
├─ pkg:index:MyComponent parent
│ ├─ kubernetes:core/v1:ConfigMap cg-options-cg-options-cm-1
│ ├─ kubernetes:yaml:ConfigFile cg-options-testdata/options/configgroup/manifest.yaml
│ ├─ kubernetes:core/v1:ConfigMap cg-options-configgroup-cm-1
│ ├─ kubernetes:yaml:ConfigFile cg-options-testdata/options/configgroup/empty.yaml
│ └─ kubernetes:yaml:ConfigGroup cg-options
│ ├─ kubernetes:core/v1:ConfigMap cg-options-configgroup-cm-1
│ ├─ kubernetes:yaml:ConfigFile cg-options-testdata/options/configgroup/empty.yaml
│ └─ kubernetes:yaml:ConfigGroup cg-options
```

After:

```
└─ pkg:index:MyComponent parent
└─ kubernetes:yaml:ConfigGroup cg-options
└─ pkg:index:MyComponent parent
└─ kubernetes:yaml:ConfigGroup cg-options
├─ kubernetes:yaml:ConfigFile cg-options-testdata/options/configgroup/manifest.yaml
│ └─ kubernetes:core/v1:ConfigMap cg-options-configgroup-cm-1
└─ kubernetes:core/v1:ConfigMap cg-options-cg-options-cm-1
│ └─ kubernetes:core/v1:ConfigMap cg-options-configgroup-cm-1
└─ kubernetes:core/v1:ConfigMap cg-options-cg-options-cm-1
```

This release addresses this issue and attempts to heal existing stacks using aliases. This is effective at avoiding a replacement except in the case where the child was created with the wrong provider. In this case, __Pulumi will suggest a replacement of the child resource(s), such that they use the correct provider__.

## 4.6.1 (December 14, 2023)
Expand Down
283 changes: 151 additions & 132 deletions provider/cmd/pulumi-resource-kubernetes/schema.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion provider/pkg/await/await.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ type ProviderConfig struct {
FieldManager string
ClusterVersion *cluster.ServerVersion
ServerSideApply bool
EnableUpsert bool

ClientSet *clients.DynamicClientSet
DedupLogger *logging.DedupLogger
Expand Down Expand Up @@ -192,7 +193,7 @@ func Creation(c CreateConfig) (*unstructured.Unstructured, error) {
}
}

if c.ServerSideApply {
if c.ServerSideApply && c.EnableUpsert {
force := patchForce(c.Inputs, nil, c.Preview)
options := metav1.PatchOptions{
FieldManager: c.FieldManager,
Expand Down
2 changes: 2 additions & 0 deletions provider/pkg/await/await_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ func Test_Creation(t *testing.T) {
DedupLogger: logging.NewLogger(context.Background(), host, urn),
Resources: resources,
ServerSideApply: tt.args.serverSideApply,
EnableUpsert: tt.args.serverSideApply,
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
EnableUpsert: tt.args.serverSideApply,
EnableUpsert: tt.args.enableUpsert,

awaiters: map[string]awaitSpec{},
},
Inputs: tt.args.inputs,
Expand Down Expand Up @@ -365,6 +366,7 @@ func TestAwaitSSAConflict(t *testing.T) {
FieldManager: "test",
ClientSet: client,
ServerSideApply: true,
EnableUpsert: true,
}
config := CreateConfig{
ProviderConfig: pconfig,
Expand Down
23 changes: 21 additions & 2 deletions provider/pkg/gen/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,16 @@ func PulumiSchema(swagger map[string]any) pschema.PackageSpec {
Description: "BETA FEATURE - If present and set to true, allow ConfigMaps to be mutated.\nThis feature is in developer preview, and is disabled by default.\n\nThis config can be specified in the following ways using this precedence:\n1. This `enableConfigMapMutable` parameter.\n2. The `PULUMI_K8S_ENABLE_CONFIGMAP_MUTABLE` environment variable.",
TypeSpec: pschema.TypeSpec{Type: "boolean"},
},
"enableUpsert": {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we reverse the polarity of this field, so that it is false by default?

Description: "If present and set to false, the provider will surface errors if a create operation would overwrite existing resources in the cluster.",
TypeSpec: pschema.TypeSpec{Type: "boolean"},
Default: true,
DefaultInfo: &pschema.DefaultSpec{
Environment: []string{
"PULUMI_K8S_ENABLE_UPSERT",
},
},
},
"renderYamlToDirectory": {
Description: "BETA FEATURE - If present, render resource manifests to this directory. In this mode, resources will not\nbe created on a Kubernetes cluster, but the rendered manifests will be kept in sync with changes\nto the Pulumi program. This feature is in developer preview, and is disabled by default.\n\nNote that some computed Outputs such as status fields will not be populated\nsince the resources are not created on a Kubernetes cluster. These Output values will remain undefined,\nand may result in an error if they are referenced by other resources. Also note that any secret values\nused in these resources will be rendered in plaintext to the resulting YAML.",
TypeSpec: pschema.TypeSpec{Type: "string"},
Expand Down Expand Up @@ -177,6 +187,15 @@ func PulumiSchema(swagger map[string]any) pschema.PackageSpec {
Description: "BETA FEATURE - If present and set to true, allow ConfigMaps to be mutated.\nThis feature is in developer preview, and is disabled by default.\n\nThis config can be specified in the following ways using this precedence:\n1. This `enableConfigMapMutable` parameter.\n2. The `PULUMI_K8S_ENABLE_CONFIGMAP_MUTABLE` environment variable.",
TypeSpec: pschema.TypeSpec{Type: "boolean"},
},
"enableUpsert": {
Description: "If present and set to false, the provider will surface errors if a create operation would overwrite existing resources in the cluster.",
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe should mention that this is for ssa mode only.

TypeSpec: pschema.TypeSpec{Type: "boolean"},
DefaultInfo: &pschema.DefaultSpec{
Environment: []string{
"PULUMI_K8S_ENABLE_UPSERT",
},
},
},
"renderYamlToDirectory": {
Description: "BETA FEATURE - If present, render resource manifests to this directory. In this mode, resources will not\nbe created on a Kubernetes cluster, but the rendered manifests will be kept in sync with changes\nto the Pulumi program. This feature is in developer preview, and is disabled by default.\n\nNote that some computed Outputs such as status fields will not be populated\nsince the resources are not created on a Kubernetes cluster. These Output values will remain undefined,\nand may result in an error if they are referenced by other resources. Also note that any secret values\nused in these resources will be rendered in plaintext to the resulting YAML.",
TypeSpec: pschema.TypeSpec{Type: "string"},
Expand Down Expand Up @@ -425,8 +444,8 @@ func PulumiSchema(swagger map[string]any) pschema.PackageSpec {
}

patchDescription := `Patch resources are used to modify existing Kubernetes resources by using
Server-Side Apply updates. The name of the resource must be specified, but all other properties are optional. More than
one patch may be applied to the same resource, and a random FieldManager name will be used for each Patch resource.
Server-Side Apply updates. The name of the resource must be specified, but all other properties are optional. More than
one patch may be applied to the same resource, and a random FieldManager name will be used for each Patch resource.
Conflicts will result in an error by default, but can be forced using the "pulumi.com/patchForce" annotation. See the
[Server-Side Apply Docs](https://www.pulumi.com/registry/packages/kubernetes/how-to-guides/managing-resources-with-server-side-apply/) for
additional information about using Server-Side Apply to manage Kubernetes resources with Pulumi.`
Expand Down
17 changes: 17 additions & 0 deletions provider/pkg/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ type kubeProvider struct {
skipUpdateUnreachable bool
enableConfigMapMutable bool
enableSecrets bool
enableUpsert bool
suppressDeprecationWarnings bool
suppressHelmHookWarnings bool
serverSideApplyMode bool
Expand Down Expand Up @@ -543,6 +544,17 @@ func (k *kubeProvider) Configure(_ context.Context, req *pulumirpc.ConfigureRequ
k.enableConfigMapMutable = true
}

enableUpsert := func() bool {
if enabled, exists := vars["kubernetes:config:enableUpsert"]; exists {
return enabled == trueStr
}
if enabled, exists := os.LookupEnv("PULUMI_K8S_ENABLE_UPSERT"); exists {
return enabled == trueStr
}
return true
}
k.enableUpsert = enableUpsert()

suppressDeprecationWarnings := func() bool {
// If the provider flag is set, use that value to determine behavior. This will override the ENV var.
if enabled, exists := vars["kubernetes:config:suppressDeprecationWarnings"]; exists {
Expand Down Expand Up @@ -1849,6 +1861,7 @@ func (k *kubeProvider) Create(
DedupLogger: logging.NewLogger(k.canceler.context, k.host, urn),
Resources: resources,
ServerSideApply: k.serverSideApplyMode,
EnableUpsert: k.enableUpsert,
},
Inputs: newInputs,
Timeout: req.Timeout,
Expand Down Expand Up @@ -2096,6 +2109,8 @@ func (k *kubeProvider) Read(ctx context.Context, req *pulumirpc.ReadRequest) (*p
ClientSet: k.clientSet,
DedupLogger: logging.NewLogger(k.canceler.context, k.host, urn),
Resources: resources,
ServerSideApply: k.serverSideApplyMode,
EnableUpsert: k.enableUpsert,
},
Inputs: oldInputs,
ReadFromCluster: readFromCluster,
Expand Down Expand Up @@ -2344,6 +2359,7 @@ func (k *kubeProvider) Update(
DedupLogger: logging.NewLogger(k.canceler.context, k.host, urn),
Resources: resources,
ServerSideApply: k.serverSideApplyMode,
EnableUpsert: k.enableUpsert,
},
OldInputs: oldLivePruned,
OldOutputs: oldLive,
Expand Down Expand Up @@ -2502,6 +2518,7 @@ func (k *kubeProvider) Delete(ctx context.Context, req *pulumirpc.DeleteRequest)
DedupLogger: logging.NewLogger(k.canceler.context, k.host, urn),
Resources: resources,
ServerSideApply: k.serverSideApplyMode,
EnableUpsert: k.enableUpsert,
},
Inputs: oldInputs,
Outputs: current,
Expand Down
10 changes: 10 additions & 0 deletions sdk/dotnet/Config/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,16 @@ public static bool? EnableServerSideApply
set => _enableServerSideApply.Set(value);
}

private static readonly __Value<bool?> _enableUpsert = new __Value<bool?>(() => __config.GetBoolean("enableUpsert") ?? Utilities.GetEnvBoolean("PULUMI_K8S_ENABLE_UPSERT") ?? true);
/// <summary>
/// If present and set to false, the provider will surface errors if a create operation would overwrite existing resources in the cluster.
/// </summary>
public static bool? EnableUpsert
{
get => _enableUpsert.Get();
set => _enableUpsert.Set(value);
}

private static readonly __Value<string?> _kubeconfig = new __Value<string?>(() => __config.Get("kubeconfig"));
/// <summary>
/// The contents of a kubeconfig file or the path to a kubeconfig file. If this is set, this config will be used instead of $KUBECONFIG.
Expand Down
7 changes: 7 additions & 0 deletions sdk/dotnet/Provider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@
[Input("enableServerSideApply", json: true)]
public Input<bool>? EnableServerSideApply { get; set; }

/// <summary>
/// If present and set to false, the provider will surface errors if a create operation would overwrite existing resources in the cluster.
/// </summary>
[Input("enableUpsert", json: true)]
public Input<bool>? EnableUpsert { get; set; }

/// <summary>
/// Options to configure the Helm Release resource.
/// </summary>
Expand Down Expand Up @@ -147,7 +153,8 @@
DeleteUnreachable = Utilities.GetEnvBoolean("PULUMI_K8S_DELETE_UNREACHABLE");
EnableConfigMapMutable = Utilities.GetEnvBoolean("PULUMI_K8S_ENABLE_CONFIGMAP_MUTABLE");
EnableServerSideApply = Utilities.GetEnvBoolean("PULUMI_K8S_ENABLE_SERVER_SIDE_APPLY");
EnableUpsert = Utilities.GetEnvBoolean("PULUMI_K8S_ENABLE_UPSERT");
KubeConfig = Utilities.GetEnv("KUBECONFIG");

Check warning on line 157 in sdk/dotnet/Provider.cs

View workflow job for this annotation

GitHub Actions / build_sdks (dotnet)

Possible null reference argument for parameter 'value' in 'Input<string>.implicit operator Input<string>(string value)'.
SkipUpdateUnreachable = Utilities.GetEnvBoolean("PULUMI_K8S_SKIP_UPDATE_UNREACHABLE");
SuppressDeprecationWarnings = Utilities.GetEnvBoolean("PULUMI_K8S_SUPPRESS_DEPRECATION_WARNINGS");
SuppressHelmHookWarnings = Utilities.GetEnvBoolean("PULUMI_K8S_SUPPRESS_HELM_HOOK_WARNINGS");
Expand Down
13 changes: 13 additions & 0 deletions sdk/go/kubernetes/config/config.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions sdk/go/kubernetes/provider.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions sdk/java/src/main/java/com/pulumi/kubernetes/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ public Optional<Boolean> enableReplaceCRD() {
public Optional<Boolean> enableServerSideApply() {
return Codegen.booleanProp("enableServerSideApply").config(config).get();
}
/**
* If present and set to false, the provider will surface errors if a create operation would overwrite existing resources in the cluster.
*
*/
public Optional<Boolean> enableUpsert() {
return Codegen.booleanProp("enableUpsert").config(config).env("PULUMI_K8S_ENABLE_UPSERT").def(true).get();
}
/**
* The contents of a kubeconfig file or the path to a kubeconfig file. If this is set, this config will be used instead of $KUBECONFIG.
*
Expand Down
38 changes: 38 additions & 0 deletions sdk/java/src/main/java/com/pulumi/kubernetes/ProviderArgs.java
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,21 @@ public Optional<Output<Boolean>> enableServerSideApply() {
return Optional.ofNullable(this.enableServerSideApply);
}

/**
* If present and set to false, the provider will surface errors if a create operation would overwrite existing resources in the cluster.
*
*/
@Import(name="enableUpsert", json=true)
private @Nullable Output<Boolean> enableUpsert;

/**
* @return If present and set to false, the provider will surface errors if a create operation would overwrite existing resources in the cluster.
*
*/
public Optional<Output<Boolean>> enableUpsert() {
return Optional.ofNullable(this.enableUpsert);
}

/**
* Options to configure the Helm Release resource.
*
Expand Down Expand Up @@ -258,6 +273,7 @@ private ProviderArgs(ProviderArgs $) {
this.deleteUnreachable = $.deleteUnreachable;
this.enableConfigMapMutable = $.enableConfigMapMutable;
this.enableServerSideApply = $.enableServerSideApply;
this.enableUpsert = $.enableUpsert;
this.helmReleaseSettings = $.helmReleaseSettings;
this.kubeClientSettings = $.kubeClientSettings;
this.kubeconfig = $.kubeconfig;
Expand Down Expand Up @@ -403,6 +419,27 @@ public Builder enableServerSideApply(Boolean enableServerSideApply) {
return enableServerSideApply(Output.of(enableServerSideApply));
}

/**
* @param enableUpsert If present and set to false, the provider will surface errors if a create operation would overwrite existing resources in the cluster.
*
* @return builder
*
*/
public Builder enableUpsert(@Nullable Output<Boolean> enableUpsert) {
$.enableUpsert = enableUpsert;
return this;
}

/**
* @param enableUpsert If present and set to false, the provider will surface errors if a create operation would overwrite existing resources in the cluster.
*
* @return builder
*
*/
public Builder enableUpsert(Boolean enableUpsert) {
return enableUpsert(Output.of(enableUpsert));
}

/**
* @param helmReleaseSettings Options to configure the Helm Release resource.
*
Expand Down Expand Up @@ -599,6 +636,7 @@ public ProviderArgs build() {
$.deleteUnreachable = Codegen.booleanProp("deleteUnreachable").output().arg($.deleteUnreachable).env("PULUMI_K8S_DELETE_UNREACHABLE").getNullable();
$.enableConfigMapMutable = Codegen.booleanProp("enableConfigMapMutable").output().arg($.enableConfigMapMutable).env("PULUMI_K8S_ENABLE_CONFIGMAP_MUTABLE").getNullable();
$.enableServerSideApply = Codegen.booleanProp("enableServerSideApply").output().arg($.enableServerSideApply).env("PULUMI_K8S_ENABLE_SERVER_SIDE_APPLY").getNullable();
$.enableUpsert = Codegen.booleanProp("enableUpsert").output().arg($.enableUpsert).env("PULUMI_K8S_ENABLE_UPSERT").getNullable();
$.kubeconfig = Codegen.stringProp("kubeconfig").output().arg($.kubeconfig).env("KUBECONFIG").getNullable();
$.skipUpdateUnreachable = Codegen.booleanProp("skipUpdateUnreachable").output().arg($.skipUpdateUnreachable).env("PULUMI_K8S_SKIP_UPDATE_UNREACHABLE").getNullable();
$.suppressDeprecationWarnings = Codegen.booleanProp("suppressDeprecationWarnings").output().arg($.suppressDeprecationWarnings).env("PULUMI_K8S_SUPPRESS_DEPRECATION_WARNINGS").getNullable();
Expand Down
Loading
Loading