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

Include API Version in input/state #3907

Draft
wants to merge 4 commits into
base: v3
Choose a base branch
from
Draft

Include API Version in input/state #3907

wants to merge 4 commits into from

Conversation

EronWright
Copy link
Contributor

@EronWright EronWright commented Jan 29, 2025

Overview

Closes #3766

To be rebased onto master and generate SDKs.

This PR is aimed at improving the experience around api versions, e.g. when upgrading a resource to a new api version. Note that major version upgrades of this provider tend to change the 'default' versions of many resources.

  1. Show some diff output and and actually update the resource state when the api version of a resource is being changed.
  2. Record the actual api version that was used in create/update/read, to inform subsequent operations.

In 2.x mode, the provider simply records the actual apiVersion as an output property, but doesn't expose it at the SDK level. The rationale is to have the information available in "old state" +before+ the major upgrade is performed; this allows the provider to avoid a spurious diff+update during the upgrade.

image

In 3.x mode, the provider introduces a new apiVersion input property, with a constant value. The input property is NOT added to the resource args; it is always defaulted. In the future, a true input property might be added to support overrides. The purpose of having an input is to produce a diff (note that the diff logic is input-input in this provider) when the api version has changed. This leads to an update call against the new apiversion, to validate the inputs and refresh the state. An output property is added to the SDK.

In a migration case, the old apiVersion isn't yet known, and this may lead to a spurious (though harmless) update because the value goes from empty to known. We mitigate this by reading apiVersion from old state during Diff. The ideal migration flow is:

  1. User upgrades to the latest 2.x with pulumi up (to update the provider version) followed by pulumi refresh (to update the resource's state.
  2. User upgrades to 3.0 with pulumi up, and the provider compares old state to new input to see whether the api version is actually changing, e.g. via the use of a "default version" resource.

For example, here's the impact to the state file after completing the migration as described. We see two affected resources: a resource group and a storage account, with the latter undergoing a change to the 'default' apiversion.

image image

Another aspect to this PR is the apiVersion output property and supporting the getFoo functions. The code generator must be careful to apply the output property to the resource type (as represented by isTopLevel), not to functions. There is, however, a special "get" function where it does make sense to return an apiVersion.

@EronWright
Copy link
Contributor Author

EronWright commented Jan 30, 2025

Here's a test program to exercise the relevant changes. In particular:

  • working with resources whose default version is changing (StorageAccount).
  • using the "getFoo" function to get an existing external resource
  • using the "get" function to register an external resource into state
import * as pulumi from "@pulumi/pulumi";
import * as resources from "@pulumi/azure-native/resources";
import * as storage from "@pulumi/azure-native/storage";
//import * as storage from "@pulumi/azure-native/storage/v20220901";
import * as storagex from "@pulumi/azure-native/storage/v20230501";

// Create an Azure Resource Group
const resourceGroup = new resources.ResourceGroup("resourceGroup");

const existing = storagex.getStorageAccountOutput({
    resourceGroupName: "pulumi-dev-shared",
    accountName: "pulumitesting",
});
export const existingId = existing.id;
export const existingApiVersion = existing.apiVersion;

const existingRes = storagex.StorageAccount.get("existing", existing.id);
export const existingResApiVersion = existingRes.apiVersion;

// Create an Azure resource (Storage Account)
const storageAccount = new storagex.StorageAccount("sa", {
    resourceGroupName: resourceGroup.name,
    sku: {
        name: storagex.SkuName.Standard_LRS,
    },
    kind: storagex.Kind.StorageV2,
    tags: {
        environment: "production",
    },
});

// Create a Storage Container
const storageContainer = new storagex.BlobContainer("container", {
    resourceGroupName: resourceGroup.name,
    accountName: storageAccount.name,
    publicAccess: storagex.PublicAccess.None,
});

// Create a Storage Blob
const storageBlob = new storage.Blob("blob", {
    resourceGroupName: resourceGroup.name,
    accountName: storageAccount.name,
    containerName: storageContainer.name,
    source: new pulumi.asset.FileAsset("./file.txt"),
});

// Export the primary key of the Storage Account
const storageAccountKeys = storagex.listStorageAccountKeysOutput({
    resourceGroupName: resourceGroup.name,
    accountName: storageAccount.name
});

export const storageAccountApiVersion = storageAccount.apiVersion;

export const storageAccountLocation = storageAccount.location;

export const primaryStorageKey = storageAccountKeys.keys[0].value;

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.

1 participant