diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 00000000..e69de29b diff --git a/Makefile b/Makefile index 0971746a..4d5d9476 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ PROVIDER_PATH := provider VERSION_PATH := ${PROVIDER_PATH}/pkg/version.Version SCHEMA_FILE := provider/cmd/pulumi-resource-pulumiservice/schema.json -GOPATH := $(shell go env GOPATH) +export GOPATH := $(shell go env GOPATH) WORKING_DIR := $(shell pwd) TESTPARALLELISM := 4 @@ -33,7 +33,15 @@ build_sdks: dotnet_sdk go_sdk nodejs_sdk python_sdk java_sdk gen_sdk_prerequisites: $(PULUMI) -provider:: +schema: $(SCHEMA_FILE) # schema is human remember-able alias for $(SCHEMA_FILE) + +.PHONY: $(SCHEMA_FILE) +$(SCHEMA_FILE): provider + $(PULUMI) package get-schema $(WORKING_DIR)/bin/${PROVIDER} | \ + jq 'del(.version)' > $(SCHEMA_FILE) + +.PHONY: provider +provider: (cd provider && VERSION=${VERSION} go generate cmd/${PROVIDER}/main.go) (cd provider && go build -o $(WORKING_DIR)/bin/${PROVIDER} -ldflags "-X ${PROJECT}/${VERSION_PATH}=${VERSION}" $(PROJECT)/${PROVIDER_PATH}/cmd/$(PROVIDER)) diff --git a/provider/cmd/pulumi-resource-pulumiservice/legacy-schema.json b/provider/cmd/pulumi-resource-pulumiservice/legacy-schema.json new file mode 100644 index 00000000..a04d1970 --- /dev/null +++ b/provider/cmd/pulumi-resource-pulumiservice/legacy-schema.json @@ -0,0 +1,1208 @@ +{ + "config": { + "variables": { + "accessToken": { + "type": "string", + "secret": true + } + } + }, + "provider": { + "type": "object", + "inputProperties": { + "accessToken": { + "description": "Access Token to authenticate with Pulumi Cloud.", + "type": "string", + "default": "", + "defaultInfo": { + "environment": [ + "PULUMI_ACCESS_TOKEN" + ] + } + } + } + }, + "types": { + "pulumiservice:index:TeamStackPermissionScope": { + "type": "number", + "enum": [ + { + "name": "read", + "description": "Grants read permissions to stack.", + "value": 101 + }, + { + "name": "edit", + "description": "Grants edit permissions to stack.", + "value": 102 + }, + { + "name": "admin", + "description": "Grants admin permissions to stack.", + "value": 103 + } + ] + }, + "pulumiservice:index:WebhookFormat": { + "type": "string", + "enum": [ + { + "description": "The default webhook format.", + "value": "raw" + }, + { + "description": "Messages formatted for consumption by Slack incoming webhooks.", + "value": "slack" + }, + { + "value": "pulumi_deployments", + "description": "Initiate deployments on a stack from a Pulumi Cloud webhook.", + "name": "PulumiDeployments" + }, + { + "value": "ms_teams", + "description": "Messages formatted for consumption by Microsoft Teams incoming webhooks.", + "name": "MicrosoftTeams" + } + ] + }, + "pulumiservice:index:WebhookFilters": { + "type": "string", + "enum": [ + { + "value": "stack_created", + "description": "Trigger a webhook when a stack is created. Only valid for org webhooks.", + "name": "StackCreated" + }, + { + "value": "stack_deleted", + "description": "Trigger a webhook when a stack is deleted. Only valid for org webhooks.", + "name": "StackDeleted" + }, + { + "value": "update_succeeded", + "description": "Trigger a webhook when a stack update succeeds.", + "name": "UpdateSucceeded" + }, + { + "value": "update_failed", + "description": "Trigger a webhook when a stack update fails.", + "name": "UpdateFailed" + }, + { + "value": "preview_succeeded", + "description": "Trigger a webhook when a stack preview succeeds.", + "name": "PreviewSucceeded" + }, + { + "value": "preview_failed", + "description": "Trigger a webhook when a stack preview fails.", + "name": "PreviewFailed" + }, + { + "value": "destroy_succeeded", + "description": "Trigger a webhook when a stack destroy succeeds.", + "name": "DestroySucceeded" + }, + { + "value": "destroy_failed", + "description": "Trigger a webhook when a stack destroy fails.", + "name": "DestroyFailed" + }, + { + "value": "refresh_succeeded", + "description": "Trigger a webhook when a stack refresh succeeds.", + "name": "RefreshSucceeded" + }, + { + "value": "refresh_failed", + "description": "Trigger a webhook when a stack refresh fails.", + "name": "RefreshFailed" + }, + { + "value": "deployment_queued", + "description": "Trigger a webhook when a deployment is queued.", + "name": "DeploymentQueued" + }, + { + "value": "deployment_started", + "description": "Trigger a webhook when a deployment starts running.", + "name": "DeploymentStarted" + }, + { + "value": "deployment_succeeded", + "description": "Trigger a webhook when a deployment succeeds.", + "name": "DeploymentSucceeded" + }, + { + "value": "deployment_failed", + "description": "Trigger a webhook when a deployment fails.", + "name": "DeploymentFailed" + }, + { + "value": "drift_detected", + "description": "Trigger a webhook when drift is detected.", + "name": "DriftDetected" + }, + { + "value": "drift_detection_succeeded", + "description": "Trigger a webhook when a drift detection run succeeds, regardless of whether drift is detected.", + "name": "DriftDetectionSucceeded" + }, + { + "value": "drift_detection_failed", + "description": "Trigger a webhook when a drift detection run fails.", + "name": "DriftDetectionFailed" + }, + { + "value": "drift_remediation_succeeded", + "description": "Trigger a webhook when a drift remediation run succeeds.", + "name": "DriftRemediationSucceeded" + }, + { + "value": "drift_remediation_failed", + "description": "Trigger a webhook when a drift remediation run fails.", + "name": "DriftRemediationFailed" + } + ] + }, + "pulumiservice:index:DeploymentSettingsExecutorContext": { + "description": "The executor context defines information about the executor where the deployment is executed. If unspecified, the default 'pulumi/pulumi' image is used.", + "properties": { + "executorImage": { + "type": "string", + "description": "Allows overriding the default executor image with a custom image. E.g. 'pulumi/pulumi-nodejs:latest'" + } + }, + "type": "object", + "required": [ + "executorImage" + ] + }, + "pulumiservice:index:DeploymentSettingsSourceContext": { + "description": "Settings related to the source of the deployment.", + "properties": { + "git": { + "$ref": "#/types/pulumiservice:index:DeploymentSettingsGitSource", + "description": "Git source settings for a deployment." + } + }, + "type": "object" + }, + "pulumiservice:index:DeploymentSettingsGitSource": { + "description": "Git source settings for a deployment.", + "properties": { + "repoUrl": { + "type": "string", + "description": "The repository URL to use for git settings. Should not be specified if there are `gitHub` settings for this deployment." + }, + "gitAuth": { + "$ref": "#/types/pulumiservice:index:DeploymentSettingsGitSourceGitAuth", + "description": "Git authentication configuration for this deployment. Should not be specified if there are `gitHub` settings for this deployment." + }, + "branch": { + "type": "string", + "description": "The branch to deploy. One of either `branch` or `commit` must be specified." + }, + "commit": { + "type": "string", + "description": "The commit to deploy. One of either `branch` or `commit` must be specified." + }, + "repoDir": { + "type": "string", + "description": "The directory within the repository where the Pulumi.yaml is located." + } + }, + "type": "object" + }, + "pulumiservice:index:DeploymentSettingsGitSourceGitAuth": { + "description": "Git source settings for a deployment.", + "properties": { + "sshAuth": { + "$ref": "#/types/pulumiservice:index:DeploymentSettingsGitAuthSSHAuth", + "description": "SSH auth for git authentication. Only one of `personalAccessToken`, `sshAuth`, or `basicAuth` must be defined." + }, + "basicAuth": { + "$ref": "#/types/pulumiservice:index:DeploymentSettingsGitAuthBasicAuth", + "description": "Basic auth for git authentication. Only one of `personalAccessToken`, `sshAuth`, or `basicAuth` must be defined." + } + }, + "type": "object" + }, + "pulumiservice:index:DeploymentSettingsGitAuthSSHAuth": { + "description": "Git source settings for a deployment.", + "type": "object", + "properties": { + "sshPrivateKey": { + "type": "string", + "secret": true, + "description": "SSH private key." + }, + "password": { + "type": "string", + "secret": true, + "description": "Optional password for SSH authentication." + } + }, + "required": [ + "sshPrivateKey" + ] + }, + "pulumiservice:index:DeploymentSettingsGitAuthBasicAuth": { + "description": "Git source settings for a deployment.", + "properties": { + "username": { + "type": "string", + "secret": true, + "description": "User name for git basic authentication." + }, + "password": { + "type": "string", + "secret": true, + "description": "Password for git basic authentication." + } + }, + "required": [ + "username", + "password" + ], + "type": "object" + }, + "pulumiservice:index:DeploymentSettingsGithub": { + "description": "GitHub settings for the deployment.", + "properties": { + "repository": { + "type": "string", + "description": "The GitHub repository in the format org/repo." + }, + "deployCommits": { + "type": "boolean", + "description": "Trigger a deployment running `pulumi up` on commit.", + "default": true + }, + "previewPullRequests": { + "type": "boolean", + "description": "Trigger a deployment running `pulumi preview` when a PR is opened.", + "default": true + }, + "pullRequestTemplate": { + "type": "boolean", + "description": "Use this stack as a template for pull request review stacks.", + "default": false + }, + "paths": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The paths within the repo that deployments should be filtered to." + } + }, + "type": "object" + }, + "pulumiservice:index:DeploymentSettingsOperationContext": { + "description": "Settings related to the Pulumi operation environment during the deployment.", + "properties": { + "preRunCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Shell commands to run before the Pulumi operation executes." + }, + "environmentVariables": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Environment variables to set for the deployment." + }, + "options": { + "$ref": "#/types/pulumiservice:index:OperationContextOptions", + "description": "Options to override default behavior during the deployment." + }, + "oidc": { + "$ref": "#/types/pulumiservice:index:OperationContextOIDC", + "description": "OIDC configuration to use during the deployment." + } + }, + "type": "object" + }, + "pulumiservice:index:OperationContextOptions": { + "type": "object", + "properties": { + "skipInstallDependencies": { + "type": "boolean", + "description": "Skip the default dependency installation step - use this to customize the dependency installation (e.g. if using yarn or poetry)" + }, + "skipIntermediateDeployments": { + "type": "boolean", + "description": "Skip intermediate deployments (Consolidate multiple deployments of the same type into one deployment)" + }, + "shell": { + "type": "string", + "description": "The shell to use to run commands during the deployment. Defaults to 'bash'." + }, + "deleteAfterDestroy": { + "type": "boolean", + "description": "Whether the stack should be deleted after it is destroyed." + } + } + }, + "pulumiservice:index:OperationContextOIDC": { + "type": "object", + "properties": { + "aws": { + "$ref": "#/types/pulumiservice:index:AWSOIDCConfiguration", + "description": "AWS-specific OIDC configuration." + }, + "gcp": { + "$ref": "#/types/pulumiservice:index:GCPOIDCConfiguration", + "description": "GCP-specific OIDC configuration." + }, + "azure": { + "$ref": "#/types/pulumiservice:index:AzureOIDCConfiguration", + "description": "Azure-specific OIDC configuration." + } + } + }, + "pulumiservice:index:AWSOIDCConfiguration": { + "type": "object", + "properties": { + "duration": { + "type": "string", + "description": "Duration of the assume-role session in “XhYmZs” format" + }, + "policyARNs": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Optional set of IAM policy ARNs that further restrict the assume-role session" + }, + "roleARN": { + "type": "string", + "description": "The ARN of the role to assume using the OIDC token." + }, + "sessionName": { + "type": "string", + "description": "The name of the assume-role session." + } + }, + "required": [ + "roleARN", + "sessionName" + ] + }, + "pulumiservice:index:GCPOIDCConfiguration": { + "type": "object", + "properties": { + "projectId": { + "type": "string", + "description": "The numerical ID of the GCP project." + }, + "region": { + "type": "string", + "description": "The region of the GCP project." + }, + "workloadPoolId": { + "type": "string", + "description": "The ID of the workload pool to use." + }, + "providerId": { + "type": "string", + "description": "The ID of the identity provider associated with the workload pool." + }, + "serviceAccount": { + "type": "string", + "description": "The email address of the service account to use." + }, + "tokenLifetime": { + "type": "string", + "description": "The lifetime of the temporary credentials in “XhYmZs” format." + } + }, + "required": [ + "projectId", + "workloadPoolId", + "providerId", + "serviceAccount" + ] + }, + "pulumiservice:index:AzureOIDCConfiguration": { + "type": "object", + "properties": { + "clientId": { + "type": "string", + "description": "The client ID of the federated workload identity." + }, + "tenantId": { + "type": "string", + "description": "The tenant ID of the federated workload identity." + }, + "subscriptionId": { + "type": "string", + "description": "The subscription ID of the federated workload identity." + } + }, + "required": [ + "clientId", + "tenantId", + "subscriptionId" + ] + }, + "pulumiservice:index:PulumiOperation": { + "type": "string", + "enum": [ + { + "description": "Analogous to `pulumi up` command.", + "value": "update" + }, + { + "description": "Analogous to `pulumi preview` command.", + "value": "preview" + }, + { + "description": "Analogous to `pulumi refresh` command.", + "value": "refresh" + }, + { + "description": "Analogous to `pulumi destroy` command.", + "value": "destroy" + } + ] + } + }, + "resources": { + "pulumiservice:index:AgentPool": { + "description": "Agent Pool for customer manager deployments", + "properties": { + "agentPoolId": { + "description": "The agent pool identifier.", + "type": "string" + }, + "name": { + "description": "The name of the agent pool.", + "type": "string" + }, + "description": { + "description": "Description of the agent pool.", + "type": "string" + }, + "organizationName": { + "description": "The organization's name.", + "type": "string" + }, + "tokenValue": { + "description": "The agent pool's token's value.", + "type": "string", + "secret": true + } + }, + "required": [ + "agentPoolId", + "name", + "organizationName", + "tokenValue" + ], + "inputProperties": { + "name": { + "description": "Name of the agent pool.", + "type": "string" + }, + "description": { + "description": "Description of the agent pool.", + "type": "string" + }, + "organizationName": { + "description": "The organization's name.", + "type": "string" + } + }, + "requiredInputs": [ + "name", + "organizationName" + ] + }, + "pulumiservice:index:AccessToken": { + "description": "Access tokens allow a user to authenticate against the Pulumi Cloud", + "properties": { + "tokenId": { + "description": "The token identifier.", + "type": "string" + }, + "description": { + "description": "Description of the access token.", + "type": "string" + }, + "value": { + "description": "The token's value.", + "type": "string", + "secret": true + } + }, + "required": [ + "tokenId", + "description", + "value" + ], + "inputProperties": { + "description": { + "description": "Description of the access token.", + "type": "string" + } + }, + "requiredInputs": [ + "description" + ] + }, + "pulumiservice:index:Team": { + "description": "The Pulumi Cloud offers role-based access control (RBAC) using teams. Teams allow organization admins to assign a set of stack permissions to a group of users.", + "properties": { + "teamType": { + "description": "The type of team. Must be either `pulumi` or `github`.", + "type": "string" + }, + "name": { + "description": "The team's name. Required for \"pulumi\" teams.", + "type": "string" + }, + "displayName": { + "description": "Optional. Team display name.", + "type": "string" + }, + "description": { + "description": "Optional. Team description.", + "type": "string" + }, + "members": { + "description": "List of team members.", + "type": "array", + "items": { + "type": "string" + } + }, + "organizationName": { + "description": "The name of the Pulumi organization the team belongs to.", + "type": "string" + }, + "githubTeamId": { + "description": "The GitHub ID of the team to mirror. Must be in the same GitHub organization that the Pulumi org is backed by. Required for \"github\" teams.", + "type": "number" + } + }, + "required": [ + "organizationName", + "teamType", + "members" + ], + "inputProperties": { + "teamType": { + "description": "The type of team. Must be either `pulumi` or `github`.", + "type": "string" + }, + "name": { + "description": "The team's name. Required for \"pulumi\" teams.", + "type": "string" + }, + "displayName": { + "description": "Optional. Team display name.", + "type": "string" + }, + "description": { + "description": "Optional. Team description.", + "type": "string" + }, + "members": { + "description": "List of team members.", + "type": "array", + "items": { + "type": "string" + } + }, + "organizationName": { + "description": "The name of the Pulumi organization the team belongs to.", + "type": "string" + }, + "githubTeamId": { + "description": "The GitHub ID of the team to mirror. Must be in the same GitHub organization that the Pulumi org is backed by. Required for \"github\" teams.", + "type": "number" + } + }, + "requiredInputs": [ + "organizationName", + "teamType" + ] + }, + "pulumiservice:index:TeamAccessToken": { + "description": "The Pulumi Cloud allows users to create access tokens scoped to team. Team access tokens is a resource to create them and assign them to a team", + "properties": { + "name": { + "description": "The name for the token. This must be unique amongst all machine tokens within your organization.", + "type": "string" + }, + "teamName": { + "description": "The team name.", + "type": "string" + }, + "description": { + "description": "Optional. Description for the token.", + "type": "string" + }, + "organizationName": { + "description": "The organization's name.", + "type": "string" + }, + "value": { + "description": "The token's value.", + "type": "string", + "secret": true + } + }, + "required": [ + "name", + "teamName", + "organizationName", + "value" + ], + "inputProperties": { + "name": { + "description": "The name for the token. This must be unique amongst all machine tokens within your organization.", + "type": "string" + }, + "teamName": { + "description": "The team name.", + "type": "string" + }, + "description": { + "description": "Optional. Team description.", + "type": "string" + }, + "organizationName": { + "description": "The organization's name.", + "type": "string" + } + }, + "requiredInputs": [ + "name", + "teamName", + "organizationName" + ] + }, + "pulumiservice:index:OrgAccessToken": { + "description": "The Pulumi Cloud allows users to create access tokens scoped to orgs. Org access tokens is a resource to create them and assign them to an org", + "properties": { + "name": { + "description": "The name for the token.", + "type": "string" + }, + "description": { + "description": "Optional. Description for the token.", + "type": "string" + }, + "organizationName": { + "description": "The organization's name.", + "type": "string" + }, + "admin": { + "description": "Optional. True if this is an admin token.", + "type": "boolean" + }, + "value": { + "description": "The token's value.", + "type": "string", + "secret": true + } + }, + "required": [ + "name", + "organizationName", + "value" + ], + "inputProperties": { + "name": { + "description": "The name for the token.", + "type": "string" + }, + "description": { + "description": "Optional. Team description.", + "type": "string" + }, + "organizationName": { + "description": "The organization's name.", + "type": "string" + }, + "admin": { + "description": "Optional. True if this is an admin token.", + "type": "boolean" + } + }, + "requiredInputs": [ + "name", + "organizationName" + ] + }, + "pulumiservice:index:Webhook": { + "description": "Pulumi Webhooks allow you to notify external services of events happening within your Pulumi organization or stack. For example, you can trigger a notification whenever a stack is updated. Whenever an event occurs, Pulumi will send an HTTP POST request to all registered webhooks. The webhook can then be used to emit some notification, start running integration tests, or even update additional stacks.\n\n### Import\n\nPulumi webhooks can be imported using the `id`, which for webhooks is `{org}/{project}/{stack}/{webhook-name}` e.g.,\n\n```sh\n $ pulumi import pulumiservice:index:Webhook my_webhook my-org/my-project/my-stack/4b0d0671\n```\n\n", + "properties": { + "name": { + "description": "Webhook identifier generated by Pulumi Cloud.", + "type": "string" + }, + "active": { + "description": "Indicates whether this webhook is enabled or not.", + "type": "boolean" + }, + "displayName": { + "description": "The friendly name displayed in the Pulumi Cloud.", + "type": "string" + }, + "payloadUrl": { + "description": "URL to send request to.", + "type": "string" + }, + "secret": { + "description": "Optional. secret used as the HMAC key. See [webhook docs](https://www.pulumi.com/docs/intro/pulumi-service/webhooks/#headers) for more information.", + "type": "string", + "secret": true + }, + "organizationName": { + "description": "Name of the organization.", + "type": "string" + }, + "projectName": { + "description": "Name of the project. Only specified if this is a stack webhook.", + "type": "string" + }, + "stackName": { + "description": "Name of the stack. Only specified if this is a stack webhook.", + "type": "string" + }, + "format": { + "description": "Format of the webhook payload. Can be either `raw`, `slack`, `ms_teams` or `pulumi_deployments`. Defaults to `raw`.", + "$ref": "#/types/pulumiservice:index:WebhookFormat" + }, + "filters": { + "description": "Optional set of filters to apply to the webhook. See [webhook docs](https://www.pulumi.com/docs/intro/pulumi-service/webhooks/#filters) for more information.", + "type": "array", + "items": { + "$ref": "#/types/pulumiservice:index:WebhookFilters" + } + } + }, + "required": [ + "active", + "displayName", + "organizationName", + "payloadUrl", + "name", + "format" + ], + "inputProperties": { + "active": { + "description": "Indicates whether this webhook is enabled or not.", + "type": "boolean" + }, + "displayName": { + "description": "The friendly name displayed in the Pulumi Cloud.", + "type": "string" + }, + "payloadUrl": { + "description": "URL to send request to.", + "type": "string" + }, + "secret": { + "description": "Optional. secret used as the HMAC key. See [webhook docs](https://www.pulumi.com/docs/intro/pulumi-service/webhooks/#headers) for more information.", + "type": "string", + "secret": true + }, + "organizationName": { + "description": "Name of the organization.", + "type": "string" + }, + "projectName": { + "description": "Name of the project. Only needed if this is a stack webhook.", + "type": "string" + }, + "stackName": { + "description": "Name of the stack. Only needed if this is a stack webhook.", + "type": "string" + }, + "format": { + "description": "Format of the webhook payload. Can be either `raw` or `slack`. Defaults to `raw`.", + "$ref": "#/types/pulumiservice:index:WebhookFormat", + "default": "raw" + }, + "filters": { + "description": "Optional set of filters to apply to the webhook. See [webhook docs](https://www.pulumi.com/docs/intro/pulumi-service/webhooks/#filters) for more information.", + "type": "array", + "items": { + "$ref": "#/types/pulumiservice:index:WebhookFilters" + } + } + }, + "requiredInputs": [ + "active", + "displayName", + "organizationName", + "payloadUrl" + ] + }, + "pulumiservice:index:DeploymentSettings": { + "description": "Deployment settings configure Pulumi Deployments for a stack.\n\n### Import\n\nDeployment settings can be imported using the `id`, which for deployment settings is `{org}/{project}/{stack}` e.g.,\n\n```sh\n $ pulumi import pulumiservice:index:DeploymentSettings my_settings my-org/my-project/my-stack\n```\n\n", + "properties": { + "organization": { + "description": "Organization name.", + "type": "string" + }, + "project": { + "description": "Project name.", + "type": "string" + }, + "stack": { + "description": "Stack name.", + "type": "string" + } + }, + "required": [ + "organization", + "project", + "stack" + ], + "inputProperties": { + "organization": { + "description": "Organization name.", + "type": "string" + }, + "project": { + "description": "Project name.", + "type": "string" + }, + "stack": { + "description": "Stack name.", + "type": "string" + }, + "executorContext": { + "$ref": "#/types/pulumiservice:index:DeploymentSettingsExecutorContext", + "description": "Settings related to the deployment executor." + }, + "sourceContext": { + "$ref": "#/types/pulumiservice:index:DeploymentSettingsSourceContext", + "description": "Settings related to the source of the deployment." + }, + "github": { + "$ref": "#/types/pulumiservice:index:DeploymentSettingsGithub", + "description": "GitHub settings for the deployment." + }, + "operationContext": { + "$ref": "#/types/pulumiservice:index:DeploymentSettingsOperationContext", + "description": "Settings related to the Pulumi operation environment during the deployment." + }, + "agentPoolId": { + "description": "The agent pool identifier to use for the deployment.", + "type": "string" + } + }, + "requiredInputs": [ + "organization", + "project", + "stack", + "sourceContext" + ] + }, + "pulumiservice:index:StackTag": { + "description": "Stacks have associated metadata in the form of tags. Each tag consists of a name and value.", + "properties": { + "organization": { + "description": "Organization name.", + "type": "string" + }, + "project": { + "description": "Project name.", + "type": "string" + }, + "stack": { + "description": "Stack name.", + "type": "string" + }, + "name": { + "description": "Name of the tag. The 'key' part of the key=value pair", + "type": "string" + }, + "value": { + "description": "Value of the tag. The 'value' part of the key=value pair", + "type": "string" + } + }, + "required": [ + "organization", + "project", + "stack", + "name", + "value" + ], + "inputProperties": { + "organization": { + "description": "Organization name.", + "type": "string" + }, + "project": { + "description": "Project name.", + "type": "string" + }, + "stack": { + "description": "Stack name.", + "type": "string" + }, + "name": { + "description": "Name of the tag. The 'key' part of the key=value pair", + "type": "string" + }, + "value": { + "description": "Value of the tag. The 'value' part of the key=value pair", + "type": "string" + } + }, + "requiredInputs": [ + "organization", + "project", + "stack", + "name", + "value" + ] + }, + "pulumiservice:index:TeamStackPermission": { + "description": "Grants a team permissions to the specified stack.", + "inputProperties": { + "team": { + "description": "The name of the team to grant this stack permissions to. This is not the display name.", + "type": "string" + }, + "organization": { + "description": "The organization or the personal account name of the stack.", + "type": "string" + }, + "project": { + "description": "The project name for this stack.", + "type": "string" + }, + "stack": { + "description": "The name of the stack that the team will be granted permissions to.", + "type": "string" + }, + "permission": { + "$ref": "#/types/pulumiservice:index:TeamStackPermissionScope", + "plain": true, + "description": "Sets the permission level that this team will be granted to the stack." + } + }, + "requiredInputs": [ + "team", + "organization", + "project", + "stack", + "permission" + ] + }, + "pulumiservice:index:DeploymentSchedule": { + "description": "A scheduled recurring or single time run of a pulumi command.", + "properties": { + "organization": { + "description": "Organization name.", + "type": "string" + }, + "project": { + "description": "Project name.", + "type": "string" + }, + "stack": { + "description": "Stack name.", + "type": "string" + }, + "scheduleCron": { + "description": "Cron expression for recurring scheduled runs. If you are suppling this, do not supply timestamp.", + "type": "string" + }, + "timestamp": { + "description": "The time at which the schedule should run, in ISO 8601 format. Eg: 2020-01-01T00:00:00Z. If you are suppling this, do not supply scheduleCron.", + "type": "string" + }, + "pulumiOperation": { + "description": "Which operation to run.", + "$ref": "#/types/pulumiservice:index:PulumiOperation" + }, + "scheduleId": { + "description": "Schedule ID of the created schedule, assigned by Pulumi Cloud.", + "type": "string" + } + }, + "required": [ + "organization", + "project", + "stack", + "pulumiOperation", + "scheduleId" + ], + "inputProperties": { + "organization": { + "description": "Organization name.", + "type": "string" + }, + "project": { + "description": "Project name.", + "type": "string" + }, + "stack": { + "description": "Stack name.", + "type": "string" + }, + "scheduleCron": { + "description": "Cron expression for recurring scheduled runs. If you are suppling this, do not supply timestamp.", + "type": "string" + }, + "timestamp": { + "description": "The time at which the schedule should run, in ISO 8601 format. Eg: 2020-01-01T00:00:00Z. If you are suppling this, do not supply scheduleCron.", + "type": "string" + }, + "pulumiOperation": { + "description": "Which command to run.", + "$ref": "#/types/pulumiservice:index:PulumiOperation" + } + }, + "requiredInputs": [ + "organization", + "project", + "stack", + "pulumiOperation" + ] + }, + "pulumiservice:index:DriftSchedule": { + "description": "A cron schedule to run drift detection.", + "properties": { + "organization": { + "description": "Organization name.", + "type": "string" + }, + "project": { + "description": "Project name.", + "type": "string" + }, + "stack": { + "description": "Stack name.", + "type": "string" + }, + "scheduleCron": { + "description": "Cron expression for when to run drift detection.", + "type": "string" + }, + "autoRemediate": { + "description": "Whether any drift detected should be remediated after a drift run.", + "type": "boolean" + }, + "scheduleId": { + "description": "Schedule ID of the created schedule, assigned by Pulumi Cloud.", + "type": "string" + } + }, + "required": [ + "organization", + "project", + "stack", + "scheduleCron", + "scheduleId" + ], + "inputProperties": { + "organization": { + "description": "Organization name.", + "type": "string" + }, + "project": { + "description": "Project name.", + "type": "string" + }, + "stack": { + "description": "Stack name.", + "type": "string" + }, + "scheduleCron": { + "description": "Cron expression for when to run drift detection.", + "type": "string" + }, + "autoRemediate": { + "description": "Whether any drift detected should be remediated after a drift run.", + "type": "boolean" + } + }, + "requiredInputs": [ + "organization", + "project", + "stack", + "scheduleCron" + ] + }, + "pulumiservice:index:TtlSchedule": { + "description": "A scheduled stack destory run.", + "properties": { + "organization": { + "description": "Organization name.", + "type": "string" + }, + "project": { + "description": "Project name.", + "type": "string" + }, + "stack": { + "description": "Stack name.", + "type": "string" + }, + "timestamp": { + "description": "The time at which the schedule should run, in ISO 8601 format. Eg: 2020-01-01T00:00:00Z.", + "type": "string" + }, + "deleteAfterDestroy": { + "description": "True if the stack and all associated history and settings should be deleted.", + "type": "boolean" + }, + "scheduleId": { + "description": "Schedule ID of the created schedule, assigned by Pulumi Cloud.", + "type": "string" + } + }, + "required": [ + "organization", + "project", + "stack", + "timestamp", + "scheduleId" + ], + "inputProperties": { + "organization": { + "description": "Organization name.", + "type": "string" + }, + "project": { + "description": "Project name.", + "type": "string" + }, + "stack": { + "description": "Stack name.", + "type": "string" + }, + "timestamp": { + "description": "The time at which the schedule should run, in ISO 8601 format. Eg: 2020-01-01T00:00:00Z.", + "type": "string" + }, + "deleteAfterDestroy": { + "description": "True if the stack and all associated history and settings should be deleted.", + "type": "boolean" + } + }, + "requiredInputs": [ + "organization", + "project", + "stack", + "timestamp" + ] + } + } +} diff --git a/provider/cmd/pulumi-resource-pulumiservice/main.go b/provider/cmd/pulumi-resource-pulumiservice/main.go index c7723458..576d9b5e 100644 --- a/provider/cmd/pulumi-resource-pulumiservice/main.go +++ b/provider/cmd/pulumi-resource-pulumiservice/main.go @@ -17,17 +17,25 @@ package main import ( _ "embed" - "github.com/pulumi/pulumi-pulumiservice/provider/pkg/provider" + p "github.com/pulumi/pulumi-go-provider" + "github.com/pulumi/pulumi/sdk/v3/go/common/util/cmdutil" + + pulumiservice "github.com/pulumi/pulumi-pulumiservice/provider/pkg/provider" "github.com/pulumi/pulumi-pulumiservice/provider/pkg/version" ) var providerName = "pulumiservice" -// embed schema.json directly into resource binary so that we can properly serve the schema -// directly from the resource provider -//go:embed schema.json -var schema string +// embed legacy-schema.json directly into resource binary so that we can properly serve +// the hand-written part of the schema directly from the resource provider. +// +//go:embed legacy-schema.json +var legacySchema string func main() { - provider.Serve(providerName, version.Version, schema) + err := p.RunProvider(providerName, version.Version, + pulumiservice.Provider(providerName, version.Version, legacySchema)) + if err != nil { + cmdutil.ExitError(err.Error()) + } } diff --git a/provider/cmd/pulumi-resource-pulumiservice/schema.json b/provider/cmd/pulumi-resource-pulumiservice/schema.json index 8dcf8e38..a83fcc70 100644 --- a/provider/cmd/pulumi-resource-pulumiservice/schema.json +++ b/provider/cmd/pulumi-resource-pulumiservice/schema.json @@ -2,180 +2,117 @@ "name": "pulumiservice", "displayName": "Pulumi Cloud", "description": "A native Pulumi package for creating and managing Pulumi Cloud constructs", - "homepage": "https://pulumi.com", - "repository": "https://github.com/pulumi/pulumi-pulumiservice", "keywords": [ "pulumi", "kind/native", "category/infrastructure" ], + "homepage": "https://pulumi.com", "license": "Apache-2.0", + "repository": "https://github.com/pulumi/pulumi-pulumiservice", "publisher": "Pulumi", - "config": { - "variables": { - "accessToken": { - "type": "string", - "secret": true + "meta": { + "moduleFormat": "(.*)" + }, + "language": { + "csharp": { + "namespaces": { + "pulumiservice": "PulumiService" + }, + "packageReferences": { + "Pulumi": "3.*" + } + }, + "go": { + "generateResourceContainerTypes": true, + "importBasePath": "github.com/pulumi/pulumi-pulumiservice/sdk/go/pulumiservice" + }, + "nodejs": { + "dependencies": { + "@pulumi/pulumi": "^3.0.0" + }, + "packageName": "@pulumi/pulumiservice" + }, + "python": { + "packageName": "pulumi_pulumiservice", + "requires": { + "pulumi": ">=3.0.0,<4.0.0" } } }, - "provider": { - "type": "object", - "inputProperties": { + "config": { + "variables": { "accessToken": { - "description": "Access Token to authenticate with Pulumi Cloud.", "type": "string", - "default": "", + "description": "Access Token to authenticate with Pulumi Cloud.", "defaultInfo": { "environment": [ "PULUMI_ACCESS_TOKEN" ] + }, + "secret": true + }, + "serviceURL": { + "type": "string", + "description": "The service URL used to reach Pulumi Cloud.", + "default": "https://api.pulumi.com", + "defaultInfo": { + "environment": [ + "PULUMI_BACKEND_URL" + ] } } } }, "types": { - "pulumiservice:index:TeamStackPermissionScope": { - "type": "number", - "enum": [ - { - "name": "read", - "description": "Grants read permissions to stack.", - "value": 101 - }, - { - "name": "edit", - "description": "Grants edit permissions to stack.", - "value": 102 - }, - { - "name": "admin", - "description": "Grants admin permissions to stack.", - "value": 103 - } - ] - }, - "pulumiservice:index:WebhookFormat": { - "type": "string", - "enum": [ - { - "description": "The default webhook format.", - "value": "raw" + "pulumiservice:index:AWSOIDCConfiguration": { + "properties": { + "duration": { + "type": "string", + "description": "Duration of the assume-role session in “XhYmZs” format" }, - { - "description": "Messages formatted for consumption by Slack incoming webhooks.", - "value": "slack" + "policyARNs": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Optional set of IAM policy ARNs that further restrict the assume-role session" }, - { - "value": "pulumi_deployments", - "description": "Initiate deployments on a stack from a Pulumi Cloud webhook.", - "name": "PulumiDeployments" + "roleARN": { + "type": "string", + "description": "The ARN of the role to assume using the OIDC token." }, - { - "value": "ms_teams", - "description": "Messages formatted for consumption by Microsoft Teams incoming webhooks.", - "name": "MicrosoftTeams" + "sessionName": { + "type": "string", + "description": "The name of the assume-role session." } + }, + "type": "object", + "required": [ + "roleARN", + "sessionName" ] }, - "pulumiservice:index:WebhookFilters": { - "type": "string", - "enum": [ - { - "value": "stack_created", - "description": "Trigger a webhook when a stack is created. Only valid for org webhooks.", - "name": "StackCreated" - }, - { - "value": "stack_deleted", - "description": "Trigger a webhook when a stack is deleted. Only valid for org webhooks.", - "name": "StackDeleted" - }, - { - "value": "update_succeeded", - "description": "Trigger a webhook when a stack update succeeds.", - "name": "UpdateSucceeded" - }, - { - "value": "update_failed", - "description": "Trigger a webhook when a stack update fails.", - "name": "UpdateFailed" - }, - { - "value": "preview_succeeded", - "description": "Trigger a webhook when a stack preview succeeds.", - "name": "PreviewSucceeded" - }, - { - "value": "preview_failed", - "description": "Trigger a webhook when a stack preview fails.", - "name": "PreviewFailed" - }, - { - "value": "destroy_succeeded", - "description": "Trigger a webhook when a stack destroy succeeds.", - "name": "DestroySucceeded" - }, - { - "value": "destroy_failed", - "description": "Trigger a webhook when a stack destroy fails.", - "name": "DestroyFailed" - }, - { - "value": "refresh_succeeded", - "description": "Trigger a webhook when a stack refresh succeeds.", - "name": "RefreshSucceeded" - }, - { - "value": "refresh_failed", - "description": "Trigger a webhook when a stack refresh fails.", - "name": "RefreshFailed" - }, - { - "value": "deployment_queued", - "description": "Trigger a webhook when a deployment is queued.", - "name": "DeploymentQueued" - }, - { - "value": "deployment_started", - "description": "Trigger a webhook when a deployment starts running.", - "name": "DeploymentStarted" - }, - { - "value": "deployment_succeeded", - "description": "Trigger a webhook when a deployment succeeds.", - "name": "DeploymentSucceeded" - }, - { - "value": "deployment_failed", - "description": "Trigger a webhook when a deployment fails.", - "name": "DeploymentFailed" - }, - { - "value": "drift_detected", - "description": "Trigger a webhook when drift is detected.", - "name": "DriftDetected" - }, - { - "value": "drift_detection_succeeded", - "description": "Trigger a webhook when a drift detection run succeeds, regardless of whether drift is detected.", - "name": "DriftDetectionSucceeded" - }, - { - "value": "drift_detection_failed", - "description": "Trigger a webhook when a drift detection run fails.", - "name": "DriftDetectionFailed" + "pulumiservice:index:AzureOIDCConfiguration": { + "properties": { + "clientId": { + "type": "string", + "description": "The client ID of the federated workload identity." }, - { - "value": "drift_remediation_succeeded", - "description": "Trigger a webhook when a drift remediation run succeeds.", - "name": "DriftRemediationSucceeded" + "subscriptionId": { + "type": "string", + "description": "The subscription ID of the federated workload identity." }, - { - "value": "drift_remediation_failed", - "description": "Trigger a webhook when a drift remediation run fails.", - "name": "DriftRemediationFailed" + "tenantId": { + "type": "string", + "description": "The tenant ID of the federated workload identity." } + }, + "type": "object", + "required": [ + "clientId", + "subscriptionId", + "tenantId" ] }, "pulumiservice:index:DeploymentSettingsExecutorContext": { @@ -191,27 +128,48 @@ "executorImage" ] }, - "pulumiservice:index:DeploymentSettingsSourceContext": { - "description": "Settings related to the source of the deployment.", + "pulumiservice:index:DeploymentSettingsGitAuthBasicAuth": { + "description": "Git source settings for a deployment.", "properties": { - "git": { - "$ref": "#/types/pulumiservice:index:DeploymentSettingsGitSource", - "description": "Git source settings for a deployment." + "password": { + "type": "string", + "description": "Password for git basic authentication.", + "secret": true + }, + "username": { + "type": "string", + "description": "User name for git basic authentication.", + "secret": true } }, - "type": "object" + "type": "object", + "required": [ + "password", + "username" + ] }, - "pulumiservice:index:DeploymentSettingsGitSource": { + "pulumiservice:index:DeploymentSettingsGitAuthSSHAuth": { "description": "Git source settings for a deployment.", "properties": { - "repoUrl": { + "password": { "type": "string", - "description": "The repository URL to use for git settings. Should not be specified if there are `gitHub` settings for this deployment." - }, - "gitAuth": { - "$ref": "#/types/pulumiservice:index:DeploymentSettingsGitSourceGitAuth", - "description": "Git authentication configuration for this deployment. Should not be specified if there are `gitHub` settings for this deployment." + "description": "Optional password for SSH authentication.", + "secret": true }, + "sshPrivateKey": { + "type": "string", + "description": "SSH private key.", + "secret": true + } + }, + "type": "object", + "required": [ + "sshPrivateKey" + ] + }, + "pulumiservice:index:DeploymentSettingsGitSource": { + "description": "Git source settings for a deployment.", + "properties": { "branch": { "type": "string", "description": "The branch to deploy. One of either `branch` or `commit` must be specified." @@ -220,9 +178,17 @@ "type": "string", "description": "The commit to deploy. One of either `branch` or `commit` must be specified." }, + "gitAuth": { + "$ref": "#/types/pulumiservice:index:DeploymentSettingsGitSourceGitAuth", + "description": "Git authentication configuration for this deployment. Should not be specified if there are `gitHub` settings for this deployment." + }, "repoDir": { "type": "string", "description": "The directory within the repository where the Pulumi.yaml is located." + }, + "repoUrl": { + "type": "string", + "description": "The repository URL to use for git settings. Should not be specified if there are `gitHub` settings for this deployment." } }, "type": "object" @@ -230,67 +196,31 @@ "pulumiservice:index:DeploymentSettingsGitSourceGitAuth": { "description": "Git source settings for a deployment.", "properties": { - "sshAuth": { - "$ref": "#/types/pulumiservice:index:DeploymentSettingsGitAuthSSHAuth", - "description": "SSH auth for git authentication. Only one of `personalAccessToken`, `sshAuth`, or `basicAuth` must be defined." - }, "basicAuth": { "$ref": "#/types/pulumiservice:index:DeploymentSettingsGitAuthBasicAuth", "description": "Basic auth for git authentication. Only one of `personalAccessToken`, `sshAuth`, or `basicAuth` must be defined." - } - }, - "type": "object" - }, - "pulumiservice:index:DeploymentSettingsGitAuthSSHAuth": { - "description": "Git source settings for a deployment.", - "type": "object", - "properties": { - "sshPrivateKey": { - "type": "string", - "secret": true, - "description": "SSH private key." }, - "password": { - "type": "string", - "secret": true, - "description": "Optional password for SSH authentication." + "sshAuth": { + "$ref": "#/types/pulumiservice:index:DeploymentSettingsGitAuthSSHAuth", + "description": "SSH auth for git authentication. Only one of `personalAccessToken`, `sshAuth`, or `basicAuth` must be defined." } }, - "required": [ - "sshPrivateKey" - ] + "type": "object" }, - "pulumiservice:index:DeploymentSettingsGitAuthBasicAuth": { - "description": "Git source settings for a deployment.", + "pulumiservice:index:DeploymentSettingsGithub": { + "description": "GitHub settings for the deployment.", "properties": { - "username": { - "type": "string", - "secret": true, - "description": "User name for git basic authentication." + "deployCommits": { + "type": "boolean", + "description": "Trigger a deployment running `pulumi up` on commit.", + "default": true }, - "password": { - "type": "string", - "secret": true, - "description": "Password for git basic authentication." - } - }, - "required": [ - "username", - "password" - ], - "type": "object" - }, - "pulumiservice:index:DeploymentSettingsGithub": { - "description": "GitHub settings for the deployment.", - "properties": { - "repository": { - "type": "string", - "description": "The GitHub repository in the format org/repo." - }, - "deployCommits": { - "type": "boolean", - "description": "Trigger a deployment running `pulumi up` on commit.", - "default": true + "paths": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The paths within the repo that deployments should be filtered to." }, "previewPullRequests": { "type": "boolean", @@ -302,12 +232,9 @@ "description": "Use this stack as a template for pull request review stacks.", "default": false }, - "paths": { - "type": "array", - "items": { - "type": "string" - }, - "description": "The paths within the repo that deployments should be filtered to." + "repository": { + "type": "string", + "description": "The GitHub repository in the format org/repo." } }, "type": "object" @@ -315,13 +242,6 @@ "pulumiservice:index:DeploymentSettingsOperationContext": { "description": "Settings related to the Pulumi operation environment during the deployment.", "properties": { - "preRunCommands": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Shell commands to run before the Pulumi operation executes." - }, "environmentVariables": { "type": "object", "additionalProperties": { @@ -329,102 +249,48 @@ }, "description": "Environment variables to set for the deployment." }, - "options": { - "$ref": "#/types/pulumiservice:index:OperationContextOptions", - "description": "Options to override default behavior during the deployment." - }, "oidc": { "$ref": "#/types/pulumiservice:index:OperationContextOIDC", "description": "OIDC configuration to use during the deployment." - } - }, - "type": "object" - }, - "pulumiservice:index:OperationContextOptions": { - "type": "object", - "properties": { - "skipInstallDependencies": { - "type": "boolean", - "description": "Skip the default dependency installation step - use this to customize the dependency installation (e.g. if using yarn or poetry)" - }, - "skipIntermediateDeployments": { - "type": "boolean", - "description": "Skip intermediate deployments (Consolidate multiple deployments of the same type into one deployment)" - }, - "shell": { - "type": "string", - "description": "The shell to use to run commands during the deployment. Defaults to 'bash'." - }, - "deleteAfterDestroy": { - "type": "boolean", - "description": "Whether the stack should be deleted after it is destroyed." - } - } - }, - "pulumiservice:index:OperationContextOIDC": { - "type": "object", - "properties": { - "aws": { - "$ref": "#/types/pulumiservice:index:AWSOIDCConfiguration", - "description": "AWS-specific OIDC configuration." - }, - "gcp": { - "$ref": "#/types/pulumiservice:index:GCPOIDCConfiguration", - "description": "GCP-specific OIDC configuration." }, - "azure": { - "$ref": "#/types/pulumiservice:index:AzureOIDCConfiguration", - "description": "Azure-specific OIDC configuration." - } - } - }, - "pulumiservice:index:AWSOIDCConfiguration": { - "type": "object", - "properties": { - "duration": { - "type": "string", - "description": "Duration of the assume-role session in “XhYmZs” format" + "options": { + "$ref": "#/types/pulumiservice:index:OperationContextOptions", + "description": "Options to override default behavior during the deployment." }, - "policyARNs": { + "preRunCommands": { "type": "array", "items": { "type": "string" }, - "description": "Optional set of IAM policy ARNs that further restrict the assume-role session" - }, - "roleARN": { - "type": "string", - "description": "The ARN of the role to assume using the OIDC token." - }, - "sessionName": { - "type": "string", - "description": "The name of the assume-role session." + "description": "Shell commands to run before the Pulumi operation executes." } }, - "required": [ - "roleARN", - "sessionName" - ] + "type": "object" + }, + "pulumiservice:index:DeploymentSettingsSourceContext": { + "description": "Settings related to the source of the deployment.", + "properties": { + "git": { + "$ref": "#/types/pulumiservice:index:DeploymentSettingsGitSource", + "description": "Git source settings for a deployment." + } + }, + "type": "object" }, "pulumiservice:index:GCPOIDCConfiguration": { - "type": "object", "properties": { "projectId": { "type": "string", "description": "The numerical ID of the GCP project." }, - "region": { - "type": "string", - "description": "The region of the GCP project." - }, - "workloadPoolId": { - "type": "string", - "description": "The ID of the workload pool to use." - }, "providerId": { "type": "string", "description": "The ID of the identity provider associated with the workload pool." }, + "region": { + "type": "string", + "description": "The region of the GCP project." + }, "serviceAccount": { "type": "string", "description": "The email address of the service account to use." @@ -432,36 +298,57 @@ "tokenLifetime": { "type": "string", "description": "The lifetime of the temporary credentials in “XhYmZs” format." + }, + "workloadPoolId": { + "type": "string", + "description": "The ID of the workload pool to use." } }, + "type": "object", "required": [ "projectId", - "workloadPoolId", "providerId", - "serviceAccount" + "serviceAccount", + "workloadPoolId" ] }, - "pulumiservice:index:AzureOIDCConfiguration": { - "type": "object", + "pulumiservice:index:OperationContextOIDC": { "properties": { - "clientId": { - "type": "string", - "description": "The client ID of the federated workload identity." + "aws": { + "$ref": "#/types/pulumiservice:index:AWSOIDCConfiguration", + "description": "AWS-specific OIDC configuration." }, - "tenantId": { - "type": "string", - "description": "The tenant ID of the federated workload identity." + "azure": { + "$ref": "#/types/pulumiservice:index:AzureOIDCConfiguration", + "description": "Azure-specific OIDC configuration." }, - "subscriptionId": { + "gcp": { + "$ref": "#/types/pulumiservice:index:GCPOIDCConfiguration", + "description": "GCP-specific OIDC configuration." + } + }, + "type": "object" + }, + "pulumiservice:index:OperationContextOptions": { + "properties": { + "deleteAfterDestroy": { + "type": "boolean", + "description": "Whether the stack should be deleted after it is destroyed." + }, + "shell": { "type": "string", - "description": "The subscription ID of the federated workload identity." + "description": "The shell to use to run commands during the deployment. Defaults to 'bash'." + }, + "skipInstallDependencies": { + "type": "boolean", + "description": "Skip the default dependency installation step - use this to customize the dependency installation (e.g. if using yarn or poetry)" + }, + "skipIntermediateDeployments": { + "type": "boolean", + "description": "Skip intermediate deployments (Consolidate multiple deployments of the same type into one deployment)" } }, - "required": [ - "clientId", - "tenantId", - "subscriptionId" - ] + "type": "object" }, "pulumiservice:index:PulumiOperation": { "type": "string", @@ -483,34 +370,258 @@ "value": "destroy" } ] + }, + "pulumiservice:index:TeamStackPermissionScope": { + "type": "number", + "enum": [ + { + "name": "read", + "description": "Grants read permissions to stack.", + "value": 101 + }, + { + "name": "edit", + "description": "Grants edit permissions to stack.", + "value": 102 + }, + { + "name": "admin", + "description": "Grants admin permissions to stack.", + "value": 103 + } + ] + }, + "pulumiservice:index:WebhookFilters": { + "type": "string", + "enum": [ + { + "name": "StackCreated", + "description": "Trigger a webhook when a stack is created. Only valid for org webhooks.", + "value": "stack_created" + }, + { + "name": "StackDeleted", + "description": "Trigger a webhook when a stack is deleted. Only valid for org webhooks.", + "value": "stack_deleted" + }, + { + "name": "UpdateSucceeded", + "description": "Trigger a webhook when a stack update succeeds.", + "value": "update_succeeded" + }, + { + "name": "UpdateFailed", + "description": "Trigger a webhook when a stack update fails.", + "value": "update_failed" + }, + { + "name": "PreviewSucceeded", + "description": "Trigger a webhook when a stack preview succeeds.", + "value": "preview_succeeded" + }, + { + "name": "PreviewFailed", + "description": "Trigger a webhook when a stack preview fails.", + "value": "preview_failed" + }, + { + "name": "DestroySucceeded", + "description": "Trigger a webhook when a stack destroy succeeds.", + "value": "destroy_succeeded" + }, + { + "name": "DestroyFailed", + "description": "Trigger a webhook when a stack destroy fails.", + "value": "destroy_failed" + }, + { + "name": "RefreshSucceeded", + "description": "Trigger a webhook when a stack refresh succeeds.", + "value": "refresh_succeeded" + }, + { + "name": "RefreshFailed", + "description": "Trigger a webhook when a stack refresh fails.", + "value": "refresh_failed" + }, + { + "name": "DeploymentQueued", + "description": "Trigger a webhook when a deployment is queued.", + "value": "deployment_queued" + }, + { + "name": "DeploymentStarted", + "description": "Trigger a webhook when a deployment starts running.", + "value": "deployment_started" + }, + { + "name": "DeploymentSucceeded", + "description": "Trigger a webhook when a deployment succeeds.", + "value": "deployment_succeeded" + }, + { + "name": "DeploymentFailed", + "description": "Trigger a webhook when a deployment fails.", + "value": "deployment_failed" + }, + { + "name": "DriftDetected", + "description": "Trigger a webhook when drift is detected.", + "value": "drift_detected" + }, + { + "name": "DriftDetectionSucceeded", + "description": "Trigger a webhook when a drift detection run succeeds, regardless of whether drift is detected.", + "value": "drift_detection_succeeded" + }, + { + "name": "DriftDetectionFailed", + "description": "Trigger a webhook when a drift detection run fails.", + "value": "drift_detection_failed" + }, + { + "name": "DriftRemediationSucceeded", + "description": "Trigger a webhook when a drift remediation run succeeds.", + "value": "drift_remediation_succeeded" + }, + { + "name": "DriftRemediationFailed", + "description": "Trigger a webhook when a drift remediation run fails.", + "value": "drift_remediation_failed" + } + ] + }, + "pulumiservice:index:WebhookFormat": { + "type": "string", + "enum": [ + { + "description": "The default webhook format.", + "value": "raw" + }, + { + "description": "Messages formatted for consumption by Slack incoming webhooks.", + "value": "slack" + }, + { + "name": "PulumiDeployments", + "description": "Initiate deployments on a stack from a Pulumi Cloud webhook.", + "value": "pulumi_deployments" + }, + { + "name": "MicrosoftTeams", + "description": "Messages formatted for consumption by Microsoft Teams incoming webhooks.", + "value": "ms_teams" + } + ] + } + }, + "provider": { + "properties": { + "accessToken": { + "type": "string", + "description": "Access Token to authenticate with Pulumi Cloud.", + "defaultInfo": { + "environment": [ + "PULUMI_ACCESS_TOKEN" + ] + }, + "secret": true + }, + "serviceURL": { + "type": "string", + "description": "The service URL used to reach Pulumi Cloud.", + "default": "https://api.pulumi.com", + "defaultInfo": { + "environment": [ + "PULUMI_BACKEND_URL" + ] + } + } + }, + "type": "object", + "inputProperties": { + "accessToken": { + "type": "string", + "description": "Access Token to authenticate with Pulumi Cloud.", + "defaultInfo": { + "environment": [ + "PULUMI_ACCESS_TOKEN" + ] + }, + "secret": true + }, + "serviceURL": { + "type": "string", + "description": "The service URL used to reach Pulumi Cloud.", + "default": "https://api.pulumi.com", + "defaultInfo": { + "environment": [ + "PULUMI_BACKEND_URL" + ] + } + } } }, "resources": { + "pulumiservice:index:AccessToken": { + "description": "Access tokens allow a user to authenticate against the Pulumi Cloud", + "properties": { + "description": { + "type": "string", + "description": "Description of the access token." + }, + "tokenId": { + "type": "string", + "description": "The token identifier." + }, + "value": { + "type": "string", + "description": "The token's value.", + "secret": true + } + }, + "type": "object", + "required": [ + "description", + "tokenId", + "value" + ], + "inputProperties": { + "description": { + "type": "string", + "description": "Description of the access token." + } + }, + "requiredInputs": [ + "description" + ] + }, "pulumiservice:index:AgentPool": { "description": "Agent Pool for customer manager deployments", "properties": { "agentPoolId": { - "description": "The agent pool identifier.", - "type": "string" - }, - "name": { - "description": "The name of the agent pool.", - "type": "string" + "type": "string", + "description": "The agent pool identifier." }, "description": { - "description": "Description of the agent pool.", - "type": "string" + "type": "string", + "description": "Description of the agent pool." + }, + "name": { + "type": "string", + "description": "The name of the agent pool." }, "organizationName": { - "description": "The organization's name.", - "type": "string" + "type": "string", + "description": "The organization's name." }, "tokenValue": { - "description": "The agent pool's token's value.", "type": "string", + "description": "The agent pool's token's value.", "secret": true } }, + "type": "object", "required": [ "agentPoolId", "name", @@ -518,17 +629,17 @@ "tokenValue" ], "inputProperties": { - "name": { - "description": "Name of the agent pool.", - "type": "string" - }, "description": { - "description": "Description of the agent pool.", - "type": "string" + "type": "string", + "description": "Description of the agent pool." + }, + "name": { + "type": "string", + "description": "Name of the agent pool." }, "organizationName": { - "description": "The organization's name.", - "type": "string" + "type": "string", + "description": "The organization's name." } }, "requiredInputs": [ @@ -536,711 +647,663 @@ "organizationName" ] }, - "pulumiservice:index:AccessToken": { - "description": "Access tokens allow a user to authenticate against the Pulumi Cloud", + "pulumiservice:index:DeploymentSchedule": { + "description": "A scheduled recurring or single time run of a pulumi command.", "properties": { - "tokenId": { - "description": "The token identifier.", - "type": "string" + "organization": { + "type": "string", + "description": "Organization name." + }, + "project": { + "type": "string", + "description": "Project name." + }, + "pulumiOperation": { + "$ref": "#/types/pulumiservice:index:PulumiOperation", + "description": "Which operation to run." + }, + "scheduleCron": { + "type": "string", + "description": "Cron expression for recurring scheduled runs. If you are suppling this, do not supply timestamp." + }, + "scheduleId": { + "type": "string", + "description": "Schedule ID of the created schedule, assigned by Pulumi Cloud." + }, + "stack": { + "type": "string", + "description": "Stack name." + }, + "timestamp": { + "type": "string", + "description": "The time at which the schedule should run, in ISO 8601 format. Eg: 2020-01-01T00:00:00Z. If you are suppling this, do not supply scheduleCron." + } + }, + "type": "object", + "required": [ + "organization", + "project", + "pulumiOperation", + "scheduleId", + "stack" + ], + "inputProperties": { + "organization": { + "type": "string", + "description": "Organization name." + }, + "project": { + "type": "string", + "description": "Project name." + }, + "pulumiOperation": { + "$ref": "#/types/pulumiservice:index:PulumiOperation", + "description": "Which command to run." + }, + "scheduleCron": { + "type": "string", + "description": "Cron expression for recurring scheduled runs. If you are suppling this, do not supply timestamp." + }, + "stack": { + "type": "string", + "description": "Stack name." + }, + "timestamp": { + "type": "string", + "description": "The time at which the schedule should run, in ISO 8601 format. Eg: 2020-01-01T00:00:00Z. If you are suppling this, do not supply scheduleCron." + } + }, + "requiredInputs": [ + "organization", + "project", + "pulumiOperation", + "stack" + ] + }, + "pulumiservice:index:DeploymentSettings": { + "description": "Deployment settings configure Pulumi Deployments for a stack.\n\n### Import\n\nDeployment settings can be imported using the `id`, which for deployment settings is `{org}/{project}/{stack}` e.g.,\n\n```sh\n $ pulumi import pulumiservice:index:DeploymentSettings my_settings my-org/my-project/my-stack\n```\n\n", + "properties": { + "organization": { + "type": "string", + "description": "Organization name." + }, + "project": { + "type": "string", + "description": "Project name." + }, + "stack": { + "type": "string", + "description": "Stack name." + } + }, + "type": "object", + "required": [ + "organization", + "project", + "stack" + ], + "inputProperties": { + "agentPoolId": { + "type": "string", + "description": "The agent pool identifier to use for the deployment." + }, + "executorContext": { + "$ref": "#/types/pulumiservice:index:DeploymentSettingsExecutorContext", + "description": "Settings related to the deployment executor." + }, + "github": { + "$ref": "#/types/pulumiservice:index:DeploymentSettingsGithub", + "description": "GitHub settings for the deployment." + }, + "operationContext": { + "$ref": "#/types/pulumiservice:index:DeploymentSettingsOperationContext", + "description": "Settings related to the Pulumi operation environment during the deployment." + }, + "organization": { + "type": "string", + "description": "Organization name." + }, + "project": { + "type": "string", + "description": "Project name." }, - "description": { - "description": "Description of the access token.", - "type": "string" + "sourceContext": { + "$ref": "#/types/pulumiservice:index:DeploymentSettingsSourceContext", + "description": "Settings related to the source of the deployment." }, - "value": { - "description": "The token's value.", + "stack": { "type": "string", - "secret": true - } - }, - "required": [ - "tokenId", - "description", - "value" - ], - "inputProperties": { - "description": { - "description": "Description of the access token.", - "type": "string" + "description": "Stack name." } }, "requiredInputs": [ - "description" + "organization", + "project", + "sourceContext", + "stack" ] }, - "pulumiservice:index:Team": { - "description": "The Pulumi Cloud offers role-based access control (RBAC) using teams. Teams allow organization admins to assign a set of stack permissions to a group of users.", + "pulumiservice:index:DriftSchedule": { + "description": "A cron schedule to run drift detection.", "properties": { - "teamType": { - "description": "The type of team. Must be either `pulumi` or `github`.", - "type": "string" - }, - "name": { - "description": "The team's name. Required for \"pulumi\" teams.", - "type": "string" + "autoRemediate": { + "type": "boolean", + "description": "Whether any drift detected should be remediated after a drift run." }, - "displayName": { - "description": "Optional. Team display name.", - "type": "string" + "organization": { + "type": "string", + "description": "Organization name." }, - "description": { - "description": "Optional. Team description.", - "type": "string" + "project": { + "type": "string", + "description": "Project name." }, - "members": { - "description": "List of team members.", - "type": "array", - "items": { - "type": "string" - } + "scheduleCron": { + "type": "string", + "description": "Cron expression for when to run drift detection." }, - "organizationName": { - "description": "The name of the Pulumi organization the team belongs to.", - "type": "string" + "scheduleId": { + "type": "string", + "description": "Schedule ID of the created schedule, assigned by Pulumi Cloud." }, - "githubTeamId": { - "description": "The GitHub ID of the team to mirror. Must be in the same GitHub organization that the Pulumi org is backed by. Required for \"github\" teams.", - "type": "number" + "stack": { + "type": "string", + "description": "Stack name." } }, + "type": "object", "required": [ - "organizationName", - "teamType", - "members" + "organization", + "project", + "scheduleCron", + "scheduleId", + "stack" ], "inputProperties": { - "teamType": { - "description": "The type of team. Must be either `pulumi` or `github`.", - "type": "string" - }, - "name": { - "description": "The team's name. Required for \"pulumi\" teams.", - "type": "string" - }, - "displayName": { - "description": "Optional. Team display name.", - "type": "string" + "autoRemediate": { + "type": "boolean", + "description": "Whether any drift detected should be remediated after a drift run." }, - "description": { - "description": "Optional. Team description.", - "type": "string" + "organization": { + "type": "string", + "description": "Organization name." }, - "members": { - "description": "List of team members.", - "type": "array", - "items": { - "type": "string" - } + "project": { + "type": "string", + "description": "Project name." }, - "organizationName": { - "description": "The name of the Pulumi organization the team belongs to.", - "type": "string" + "scheduleCron": { + "type": "string", + "description": "Cron expression for when to run drift detection." }, - "githubTeamId": { - "description": "The GitHub ID of the team to mirror. Must be in the same GitHub organization that the Pulumi org is backed by. Required for \"github\" teams.", - "type": "number" + "stack": { + "type": "string", + "description": "Stack name." } }, "requiredInputs": [ - "organizationName", - "teamType" + "organization", + "project", + "scheduleCron", + "stack" ] }, - "pulumiservice:index:TeamAccessToken": { - "description": "The Pulumi Cloud allows users to create access tokens scoped to team. Team access tokens is a resource to create them and assign them to a team", + "pulumiservice:index:OrgAccessToken": { + "description": "The Pulumi Cloud allows users to create access tokens scoped to orgs. Org access tokens is a resource to create them and assign them to an org", "properties": { - "name": { - "description": "The name for the token. This must be unique amongst all machine tokens within your organization.", - "type": "string" - }, - "teamName": { - "description": "The team name.", - "type": "string" + "admin": { + "type": "boolean", + "description": "Optional. True if this is an admin token." }, "description": { - "description": "Optional. Description for the token.", - "type": "string" + "type": "string", + "description": "Optional. Description for the token." + }, + "name": { + "type": "string", + "description": "The name for the token." }, "organizationName": { - "description": "The organization's name.", - "type": "string" + "type": "string", + "description": "The organization's name." }, "value": { - "description": "The token's value.", "type": "string", + "description": "The token's value.", "secret": true } }, + "type": "object", "required": [ "name", - "teamName", "organizationName", "value" ], "inputProperties": { - "name": { - "description": "The name for the token. This must be unique amongst all machine tokens within your organization.", - "type": "string" - }, - "teamName": { - "description": "The team name.", - "type": "string" + "admin": { + "type": "boolean", + "description": "Optional. True if this is an admin token." }, "description": { - "description": "Optional. Team description.", - "type": "string" + "type": "string", + "description": "Optional. Team description." + }, + "name": { + "type": "string", + "description": "The name for the token." }, "organizationName": { - "description": "The organization's name.", - "type": "string" + "type": "string", + "description": "The organization's name." } }, "requiredInputs": [ "name", - "teamName", "organizationName" ] }, - "pulumiservice:index:OrgAccessToken": { - "description": "The Pulumi Cloud allows users to create access tokens scoped to orgs. Org access tokens is a resource to create them and assign them to an org", + "pulumiservice:index:StackTag": { + "description": "Stacks have associated metadata in the form of tags. Each tag consists of a name and value.", "properties": { "name": { - "description": "The name for the token.", - "type": "string" + "type": "string", + "description": "Name of the tag. The 'key' part of the key=value pair" }, - "description": { - "description": "Optional. Description for the token.", - "type": "string" + "organization": { + "type": "string", + "description": "Organization name." }, - "organizationName": { - "description": "The organization's name.", - "type": "string" + "project": { + "type": "string", + "description": "Project name." }, - "admin": { - "description": "Optional. True if this is an admin token.", - "type": "boolean" + "stack": { + "type": "string", + "description": "Stack name." }, "value": { - "description": "The token's value.", "type": "string", - "secret": true + "description": "Value of the tag. The 'value' part of the key=value pair" } }, + "type": "object", "required": [ "name", - "organizationName", + "organization", + "project", + "stack", "value" ], "inputProperties": { "name": { - "description": "The name for the token.", - "type": "string" + "type": "string", + "description": "Name of the tag. The 'key' part of the key=value pair" }, - "description": { - "description": "Optional. Team description.", - "type": "string" + "organization": { + "type": "string", + "description": "Organization name." }, - "organizationName": { - "description": "The organization's name.", - "type": "string" + "project": { + "type": "string", + "description": "Project name." }, - "admin": { - "description": "Optional. True if this is an admin token.", - "type": "boolean" + "stack": { + "type": "string", + "description": "Stack name." + }, + "value": { + "type": "string", + "description": "Value of the tag. The 'value' part of the key=value pair" } }, "requiredInputs": [ "name", - "organizationName" + "organization", + "project", + "stack", + "value" ] }, - "pulumiservice:index:Webhook": { - "description": "Pulumi Webhooks allow you to notify external services of events happening within your Pulumi organization or stack. For example, you can trigger a notification whenever a stack is updated. Whenever an event occurs, Pulumi will send an HTTP POST request to all registered webhooks. The webhook can then be used to emit some notification, start running integration tests, or even update additional stacks.\n\n### Import\n\nPulumi webhooks can be imported using the `id`, which for webhooks is `{org}/{project}/{stack}/{webhook-name}` e.g.,\n\n```sh\n $ pulumi import pulumiservice:index:Webhook my_webhook my-org/my-project/my-stack/4b0d0671\n```\n\n", + "pulumiservice:index:Team": { + "description": "The Pulumi Cloud offers role-based access control (RBAC) using teams. Teams allow organization admins to assign a set of stack permissions to a group of users.", "properties": { - "name": { - "description": "Webhook identifier generated by Pulumi Cloud.", - "type": "string" - }, - "active": { - "description": "Indicates whether this webhook is enabled or not.", - "type": "boolean" + "description": { + "type": "string", + "description": "Optional. Team description." }, "displayName": { - "description": "The friendly name displayed in the Pulumi Cloud.", - "type": "string" - }, - "payloadUrl": { - "description": "URL to send request to.", - "type": "string" - }, - "secret": { - "description": "Optional. secret used as the HMAC key. See [webhook docs](https://www.pulumi.com/docs/intro/pulumi-service/webhooks/#headers) for more information.", "type": "string", - "secret": true + "description": "Optional. Team display name." }, - "organizationName": { - "description": "Name of the organization.", - "type": "string" + "githubTeamId": { + "type": "number", + "description": "The GitHub ID of the team to mirror. Must be in the same GitHub organization that the Pulumi org is backed by. Required for \"github\" teams." }, - "projectName": { - "description": "Name of the project. Only specified if this is a stack webhook.", - "type": "string" + "members": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of team members." }, - "stackName": { - "description": "Name of the stack. Only specified if this is a stack webhook.", - "type": "string" + "name": { + "type": "string", + "description": "The team's name. Required for \"pulumi\" teams." }, - "format": { - "description": "Format of the webhook payload. Can be either `raw`, `slack`, `ms_teams` or `pulumi_deployments`. Defaults to `raw`.", - "$ref": "#/types/pulumiservice:index:WebhookFormat" + "organizationName": { + "type": "string", + "description": "The name of the Pulumi organization the team belongs to." }, - "filters": { - "description": "Optional set of filters to apply to the webhook. See [webhook docs](https://www.pulumi.com/docs/intro/pulumi-service/webhooks/#filters) for more information.", - "type": "array", - "items": { - "$ref": "#/types/pulumiservice:index:WebhookFilters" - } + "teamType": { + "type": "string", + "description": "The type of team. Must be either `pulumi` or `github`." } }, + "type": "object", "required": [ - "active", - "displayName", + "members", "organizationName", - "payloadUrl", - "name", - "format" + "teamType" ], "inputProperties": { - "active": { - "description": "Indicates whether this webhook is enabled or not.", - "type": "boolean" + "description": { + "type": "string", + "description": "Optional. Team description." }, "displayName": { - "description": "The friendly name displayed in the Pulumi Cloud.", - "type": "string" - }, - "payloadUrl": { - "description": "URL to send request to.", - "type": "string" - }, - "secret": { - "description": "Optional. secret used as the HMAC key. See [webhook docs](https://www.pulumi.com/docs/intro/pulumi-service/webhooks/#headers) for more information.", "type": "string", - "secret": true - }, - "organizationName": { - "description": "Name of the organization.", - "type": "string" - }, - "projectName": { - "description": "Name of the project. Only needed if this is a stack webhook.", - "type": "string" - }, - "stackName": { - "description": "Name of the stack. Only needed if this is a stack webhook.", - "type": "string" + "description": "Optional. Team display name." }, - "format": { - "description": "Format of the webhook payload. Can be either `raw` or `slack`. Defaults to `raw`.", - "$ref": "#/types/pulumiservice:index:WebhookFormat", - "default": "raw" + "githubTeamId": { + "type": "number", + "description": "The GitHub ID of the team to mirror. Must be in the same GitHub organization that the Pulumi org is backed by. Required for \"github\" teams." }, - "filters": { - "description": "Optional set of filters to apply to the webhook. See [webhook docs](https://www.pulumi.com/docs/intro/pulumi-service/webhooks/#filters) for more information.", + "members": { "type": "array", "items": { - "$ref": "#/types/pulumiservice:index:WebhookFilters" - } - } - }, - "requiredInputs": [ - "active", - "displayName", - "organizationName", - "payloadUrl" - ] - }, - "pulumiservice:index:DeploymentSettings": { - "description": "Deployment settings configure Pulumi Deployments for a stack.\n\n### Import\n\nDeployment settings can be imported using the `id`, which for deployment settings is `{org}/{project}/{stack}` e.g.,\n\n```sh\n $ pulumi import pulumiservice:index:DeploymentSettings my_settings my-org/my-project/my-stack\n```\n\n", - "properties": { - "organization": { - "description": "Organization name.", - "type": "string" - }, - "project": { - "description": "Project name.", - "type": "string" - }, - "stack": { - "description": "Stack name.", - "type": "string" - } - }, - "required": [ - "organization", - "project", - "stack" - ], - "inputProperties": { - "organization": { - "description": "Organization name.", - "type": "string" - }, - "project": { - "description": "Project name.", - "type": "string" - }, - "stack": { - "description": "Stack name.", - "type": "string" - }, - "executorContext": { - "$ref": "#/types/pulumiservice:index:DeploymentSettingsExecutorContext", - "description": "Settings related to the deployment executor." - }, - "sourceContext": { - "$ref": "#/types/pulumiservice:index:DeploymentSettingsSourceContext", - "description": "Settings related to the source of the deployment." + "type": "string" + }, + "description": "List of team members." }, - "github": { - "$ref": "#/types/pulumiservice:index:DeploymentSettingsGithub", - "description": "GitHub settings for the deployment." + "name": { + "type": "string", + "description": "The team's name. Required for \"pulumi\" teams." }, - "operationContext": { - "$ref": "#/types/pulumiservice:index:DeploymentSettingsOperationContext", - "description": "Settings related to the Pulumi operation environment during the deployment." + "organizationName": { + "type": "string", + "description": "The name of the Pulumi organization the team belongs to." }, - "agentPoolId": { - "description": "The agent pool identifier to use for the deployment.", - "type": "string" + "teamType": { + "type": "string", + "description": "The type of team. Must be either `pulumi` or `github`." } }, "requiredInputs": [ - "organization", - "project", - "stack", - "sourceContext" + "organizationName", + "teamType" ] }, - "pulumiservice:index:StackTag": { - "description": "Stacks have associated metadata in the form of tags. Each tag consists of a name and value.", + "pulumiservice:index:TeamAccessToken": { + "description": "The Pulumi Cloud allows users to create access tokens scoped to team. Team access tokens is a resource to create them and assign them to a team", "properties": { - "organization": { - "description": "Organization name.", - "type": "string" + "description": { + "type": "string", + "description": "Optional. Description for the token." }, - "project": { - "description": "Project name.", - "type": "string" + "name": { + "type": "string", + "description": "The name for the token. This must be unique amongst all machine tokens within your organization." }, - "stack": { - "description": "Stack name.", - "type": "string" + "organizationName": { + "type": "string", + "description": "The organization's name." }, - "name": { - "description": "Name of the tag. The 'key' part of the key=value pair", - "type": "string" + "teamName": { + "type": "string", + "description": "The team name." }, "value": { - "description": "Value of the tag. The 'value' part of the key=value pair", - "type": "string" + "type": "string", + "description": "The token's value.", + "secret": true } }, + "type": "object", "required": [ - "organization", - "project", - "stack", "name", + "organizationName", + "teamName", "value" ], "inputProperties": { - "organization": { - "description": "Organization name.", - "type": "string" - }, - "project": { - "description": "Project name.", - "type": "string" - }, - "stack": { - "description": "Stack name.", - "type": "string" + "description": { + "type": "string", + "description": "Optional. Team description." }, "name": { - "description": "Name of the tag. The 'key' part of the key=value pair", - "type": "string" + "type": "string", + "description": "The name for the token. This must be unique amongst all machine tokens within your organization." }, - "value": { - "description": "Value of the tag. The 'value' part of the key=value pair", - "type": "string" + "organizationName": { + "type": "string", + "description": "The organization's name." + }, + "teamName": { + "type": "string", + "description": "The team name." } }, "requiredInputs": [ - "organization", - "project", - "stack", "name", - "value" + "organizationName", + "teamName" ] }, "pulumiservice:index:TeamStackPermission": { "description": "Grants a team permissions to the specified stack.", + "type": "object", "inputProperties": { - "team": { - "description": "The name of the team to grant this stack permissions to. This is not the display name.", - "type": "string" - }, "organization": { - "description": "The organization or the personal account name of the stack.", - "type": "string" - }, - "project": { - "description": "The project name for this stack.", - "type": "string" - }, - "stack": { - "description": "The name of the stack that the team will be granted permissions to.", - "type": "string" + "type": "string", + "description": "The organization or the personal account name of the stack." }, "permission": { "$ref": "#/types/pulumiservice:index:TeamStackPermissionScope", "plain": true, "description": "Sets the permission level that this team will be granted to the stack." + }, + "project": { + "type": "string", + "description": "The project name for this stack." + }, + "stack": { + "type": "string", + "description": "The name of the stack that the team will be granted permissions to." + }, + "team": { + "type": "string", + "description": "The name of the team to grant this stack permissions to. This is not the display name." } }, "requiredInputs": [ - "team", "organization", + "permission", "project", "stack", - "permission" + "team" ] }, - "pulumiservice:index:DeploymentSchedule": { - "description": "A scheduled recurring or single time run of a pulumi command.", + "pulumiservice:index:TtlSchedule": { + "description": "A scheduled stack destory run.", "properties": { + "deleteAfterDestroy": { + "type": "boolean", + "description": "True if the stack and all associated history and settings should be deleted." + }, "organization": { - "description": "Organization name.", - "type": "string" + "type": "string", + "description": "Organization name." }, "project": { - "description": "Project name.", - "type": "string" + "type": "string", + "description": "Project name." }, - "stack": { - "description": "Stack name.", - "type": "string" + "scheduleId": { + "type": "string", + "description": "Schedule ID of the created schedule, assigned by Pulumi Cloud." }, - "scheduleCron": { - "description": "Cron expression for recurring scheduled runs. If you are suppling this, do not supply timestamp.", - "type": "string" + "stack": { + "type": "string", + "description": "Stack name." }, "timestamp": { - "description": "The time at which the schedule should run, in ISO 8601 format. Eg: 2020-01-01T00:00:00Z. If you are suppling this, do not supply scheduleCron.", - "type": "string" - }, - "pulumiOperation": { - "description": "Which operation to run.", - "$ref": "#/types/pulumiservice:index:PulumiOperation" - }, - "scheduleId": { - "description": "Schedule ID of the created schedule, assigned by Pulumi Cloud.", - "type": "string" + "type": "string", + "description": "The time at which the schedule should run, in ISO 8601 format. Eg: 2020-01-01T00:00:00Z." } }, + "type": "object", "required": [ "organization", "project", + "scheduleId", "stack", - "pulumiOperation", - "scheduleId" + "timestamp" ], "inputProperties": { + "deleteAfterDestroy": { + "type": "boolean", + "description": "True if the stack and all associated history and settings should be deleted." + }, "organization": { - "description": "Organization name.", - "type": "string" + "type": "string", + "description": "Organization name." }, "project": { - "description": "Project name.", - "type": "string" + "type": "string", + "description": "Project name." }, "stack": { - "description": "Stack name.", - "type": "string" - }, - "scheduleCron": { - "description": "Cron expression for recurring scheduled runs. If you are suppling this, do not supply timestamp.", - "type": "string" + "type": "string", + "description": "Stack name." }, "timestamp": { - "description": "The time at which the schedule should run, in ISO 8601 format. Eg: 2020-01-01T00:00:00Z. If you are suppling this, do not supply scheduleCron.", - "type": "string" - }, - "pulumiOperation": { - "description": "Which command to run.", - "$ref": "#/types/pulumiservice:index:PulumiOperation" + "type": "string", + "description": "The time at which the schedule should run, in ISO 8601 format. Eg: 2020-01-01T00:00:00Z." } }, "requiredInputs": [ "organization", "project", "stack", - "pulumiOperation" + "timestamp" ] }, - "pulumiservice:index:DriftSchedule": { - "description": "A cron schedule to run drift detection.", + "pulumiservice:index:Webhook": { + "description": "Pulumi Webhooks allow you to notify external services of events happening within your Pulumi organization or stack. For example, you can trigger a notification whenever a stack is updated. Whenever an event occurs, Pulumi will send an HTTP POST request to all registered webhooks. The webhook can then be used to emit some notification, start running integration tests, or even update additional stacks.\n\n### Import\n\nPulumi webhooks can be imported using the `id`, which for webhooks is `{org}/{project}/{stack}/{webhook-name}` e.g.,\n\n```sh\n $ pulumi import pulumiservice:index:Webhook my_webhook my-org/my-project/my-stack/4b0d0671\n```\n\n", "properties": { - "organization": { - "description": "Organization name.", - "type": "string" + "active": { + "type": "boolean", + "description": "Indicates whether this webhook is enabled or not." }, - "project": { - "description": "Project name.", - "type": "string" + "displayName": { + "type": "string", + "description": "The friendly name displayed in the Pulumi Cloud." }, - "stack": { - "description": "Stack name.", - "type": "string" + "filters": { + "type": "array", + "items": { + "$ref": "#/types/pulumiservice:index:WebhookFilters" + }, + "description": "Optional set of filters to apply to the webhook. See [webhook docs](https://www.pulumi.com/docs/intro/pulumi-service/webhooks/#filters) for more information." }, - "scheduleCron": { - "description": "Cron expression for when to run drift detection.", - "type": "string" + "format": { + "$ref": "#/types/pulumiservice:index:WebhookFormat", + "description": "Format of the webhook payload. Can be either `raw`, `slack`, `ms_teams` or `pulumi_deployments`. Defaults to `raw`." }, - "autoRemediate": { - "description": "Whether any drift detected should be remediated after a drift run.", - "type": "boolean" + "name": { + "type": "string", + "description": "Webhook identifier generated by Pulumi Cloud." }, - "scheduleId": { - "description": "Schedule ID of the created schedule, assigned by Pulumi Cloud.", - "type": "string" - } - }, - "required": [ - "organization", - "project", - "stack", - "scheduleCron", - "scheduleId" - ], - "inputProperties": { - "organization": { - "description": "Organization name.", - "type": "string" + "organizationName": { + "type": "string", + "description": "Name of the organization." }, - "project": { - "description": "Project name.", - "type": "string" + "payloadUrl": { + "type": "string", + "description": "URL to send request to." }, - "stack": { - "description": "Stack name.", - "type": "string" + "projectName": { + "type": "string", + "description": "Name of the project. Only specified if this is a stack webhook." }, - "scheduleCron": { - "description": "Cron expression for when to run drift detection.", - "type": "string" + "secret": { + "type": "string", + "description": "Optional. secret used as the HMAC key. See [webhook docs](https://www.pulumi.com/docs/intro/pulumi-service/webhooks/#headers) for more information.", + "secret": true }, - "autoRemediate": { - "description": "Whether any drift detected should be remediated after a drift run.", - "type": "boolean" + "stackName": { + "type": "string", + "description": "Name of the stack. Only specified if this is a stack webhook." } }, - "requiredInputs": [ - "organization", - "project", - "stack", - "scheduleCron" - ] - }, - "pulumiservice:index:TtlSchedule": { - "description": "A scheduled stack destory run.", - "properties": { - "organization": { - "description": "Organization name.", - "type": "string" - }, - "project": { - "description": "Project name.", - "type": "string" + "type": "object", + "required": [ + "active", + "displayName", + "format", + "name", + "organizationName", + "payloadUrl" + ], + "inputProperties": { + "active": { + "type": "boolean", + "description": "Indicates whether this webhook is enabled or not." }, - "stack": { - "description": "Stack name.", - "type": "string" + "displayName": { + "type": "string", + "description": "The friendly name displayed in the Pulumi Cloud." }, - "timestamp": { - "description": "The time at which the schedule should run, in ISO 8601 format. Eg: 2020-01-01T00:00:00Z.", - "type": "string" + "filters": { + "type": "array", + "items": { + "$ref": "#/types/pulumiservice:index:WebhookFilters" + }, + "description": "Optional set of filters to apply to the webhook. See [webhook docs](https://www.pulumi.com/docs/intro/pulumi-service/webhooks/#filters) for more information." }, - "deleteAfterDestroy": { - "description": "True if the stack and all associated history and settings should be deleted.", - "type": "boolean" + "format": { + "$ref": "#/types/pulumiservice:index:WebhookFormat", + "description": "Format of the webhook payload. Can be either `raw` or `slack`. Defaults to `raw`.", + "default": "raw" }, - "scheduleId": { - "description": "Schedule ID of the created schedule, assigned by Pulumi Cloud.", - "type": "string" - } - }, - "required": [ - "organization", - "project", - "stack", - "timestamp", - "scheduleId" - ], - "inputProperties": { - "organization": { - "description": "Organization name.", - "type": "string" + "organizationName": { + "type": "string", + "description": "Name of the organization." }, - "project": { - "description": "Project name.", - "type": "string" + "payloadUrl": { + "type": "string", + "description": "URL to send request to." }, - "stack": { - "description": "Stack name.", - "type": "string" + "projectName": { + "type": "string", + "description": "Name of the project. Only needed if this is a stack webhook." }, - "timestamp": { - "description": "The time at which the schedule should run, in ISO 8601 format. Eg: 2020-01-01T00:00:00Z.", - "type": "string" + "secret": { + "type": "string", + "description": "Optional. secret used as the HMAC key. See [webhook docs](https://www.pulumi.com/docs/intro/pulumi-service/webhooks/#headers) for more information.", + "secret": true }, - "deleteAfterDestroy": { - "description": "True if the stack and all associated history and settings should be deleted.", - "type": "boolean" + "stackName": { + "type": "string", + "description": "Name of the stack. Only needed if this is a stack webhook." } }, "requiredInputs": [ - "organization", - "project", - "stack", - "timestamp" + "active", + "displayName", + "organizationName", + "payloadUrl" ] } - }, - "language": { - "csharp": { - "namespaces": { - "pulumiservice": "PulumiService" - }, - "packageReferences": { - "Pulumi": "3.*" - } - }, - "go": { - "generateResourceContainerTypes": true, - "importBasePath": "github.com/pulumi/pulumi-pulumiservice/sdk/go/pulumiservice" - }, - "nodejs": { - "packageName": "@pulumi/pulumiservice", - "dependencies": { - "@pulumi/pulumi": "^3.0.0" - } - }, - "python": { - "packageName": "pulumi_pulumiservice", - "requires": { - "pulumi": ">=3.0.0,<4.0.0" - } - } } } diff --git a/provider/go.mod b/provider/go.mod index 7f3384ad..c65d7fd4 100644 --- a/provider/go.mod +++ b/provider/go.mod @@ -3,6 +3,7 @@ module github.com/pulumi/pulumi-pulumiservice/provider go 1.21.0 require ( + github.com/pulumi/pulumi-go-provider v0.16.1-0.20240425170556-58eb7eb94c02 github.com/pulumi/pulumi/pkg/v3 v3.112.0 github.com/pulumi/pulumi/sdk/v3 v3.112.0 github.com/stretchr/testify v1.9.0 @@ -17,7 +18,7 @@ require ( github.com/ProtonMail/go-crypto v1.0.0 // indirect github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect github.com/agext/levenshtein v1.2.3 // indirect - github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect + github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect; inqdirect github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/blang/semver v3.5.1+incompatible // indirect @@ -84,10 +85,10 @@ require ( github.com/uber/jaeger-lib v2.4.1+incompatible // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/zclconf/go-cty v1.13.2 // indirect - go.uber.org/atomic v1.9.0 // indirect + go.uber.org/atomic v1.10.0 // indirect golang.org/x/crypto v0.21.0 // indirect golang.org/x/mod v0.14.0 // indirect - golang.org/x/net v0.22.0 // indirect + golang.org/x/net v0.23.0 // indirect golang.org/x/sync v0.5.0 // indirect golang.org/x/sys v0.18.0 // indirect golang.org/x/term v0.18.0 // indirect diff --git a/provider/go.sum b/provider/go.sum index dc7a16fc..77a60d9f 100644 --- a/provider/go.sum +++ b/provider/go.sum @@ -158,6 +158,8 @@ github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 h1:vkHw5I/plNdTr435 github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231/go.mod h1:murToZ2N9hNJzewjHBgfFdXhZKjY3z5cYC1VXk+lbFE= github.com/pulumi/esc v0.6.2 h1:+z+l8cuwIauLSwXQS0uoI3rqB+YG4SzsZYtHfNoXBvw= github.com/pulumi/esc v0.6.2/go.mod h1:jNnYNjzsOgVTjCp0LL24NsCk8ZJxq4IoLQdCT0X7l8k= +github.com/pulumi/pulumi-go-provider v0.16.1-0.20240425170556-58eb7eb94c02 h1:Jfdytp2dMjISwLMFN/sLTVZwlkkBDD1R0mgLkeLL0Lk= +github.com/pulumi/pulumi-go-provider v0.16.1-0.20240425170556-58eb7eb94c02/go.mod h1:sJUl1EjF3BQXAkrOeRhS3kZq17lz2o2QnTQd+c1FepU= github.com/pulumi/pulumi/pkg/v3 v3.112.0 h1:vhoM6sx1eegJntIeUZENtck3VeMtK1zBiQ2E3EPOnHw= github.com/pulumi/pulumi/pkg/v3 v3.112.0/go.mod h1:GQhNr0v5E8TACF8j0p6UQqyr7mZreUpoMfVjLeu6eY0= github.com/pulumi/pulumi/sdk/v3 v3.112.0 h1:cq2x5N6iuYhSLdeOdRs+LIq0EneB0Cb54WOlD/VaX3E= @@ -211,8 +213,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zclconf/go-cty v1.13.2 h1:4GvrUxe/QUDYuJKAav4EYqdM47/kZa672LwmXFmEKT0= github.com/zclconf/go-cty v1.13.2/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0= -go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -243,8 +245,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= -golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -327,5 +329,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= lukechampine.com/frand v1.4.2 h1:RzFIpOvkMXuPMBb9maa4ND4wjBn71E1Jpf8BzJHMaVw= lukechampine.com/frand v1.4.2/go.mod h1:4S/TM2ZgrKejMcKMbeLjISpJMO+/eZ1zu3vYX9dtj3s= -pgregory.net/rapid v0.6.1 h1:4eyrDxyht86tT4Ztm+kvlyNBLIk071gR+ZQdhphc9dQ= -pgregory.net/rapid v0.6.1/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= +pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= +pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= diff --git a/provider/pkg/provider/access_token.go b/provider/pkg/provider/access_token.go index f3d7662a..af2853ca 100644 --- a/provider/pkg/provider/access_token.go +++ b/provider/pkg/provider/access_token.go @@ -12,9 +12,7 @@ import ( pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go" ) -type PulumiServiceAccessTokenResource struct { - client *pulumiapi.Client -} +type PulumiServiceAccessTokenResource struct{} type PulumiServiceAccessTokenInput struct { Description string @@ -40,12 +38,11 @@ func (at *PulumiServiceAccessTokenResource) Name() string { return "pulumiservice:index:AccessToken" } -func (at *PulumiServiceAccessTokenResource) Diff(req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) { +func (at *PulumiServiceAccessTokenResource) Diff(_ context.Context, req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) { return diffAccessTokenProperties(req, []string{"description"}) } -func (at *PulumiServiceAccessTokenResource) Delete(req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) { - ctx := context.Background() +func (at *PulumiServiceAccessTokenResource) Delete(ctx context.Context, req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) { err := at.deleteAccessToken(ctx, req.Id) if err != nil { return &pbempty.Empty{}, err @@ -54,8 +51,7 @@ func (at *PulumiServiceAccessTokenResource) Delete(req *pulumirpc.DeleteRequest) return &pbempty.Empty{}, nil } -func (at *PulumiServiceAccessTokenResource) Create(req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) { - ctx := context.Background() +func (at *PulumiServiceAccessTokenResource) Create(ctx context.Context, req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) { inputs, err := plugin.UnmarshalProperties(req.GetProperties(), plugin.MarshalOptions{KeepUnknowns: true, SkipNulls: true}) if err != nil { return nil, err @@ -86,20 +82,18 @@ func (at *PulumiServiceAccessTokenResource) Create(req *pulumirpc.CreateRequest) } -func (at *PulumiServiceAccessTokenResource) Check(req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) { +func (at *PulumiServiceAccessTokenResource) Check(_ context.Context, req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) { return &pulumirpc.CheckResponse{Inputs: req.News, Failures: nil}, nil } -func (at *PulumiServiceAccessTokenResource) Update(_ *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) { +func (at *PulumiServiceAccessTokenResource) Update(_ context.Context, _ *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) { // all updates are destructive, so we just call Create. return nil, fmt.Errorf("unexpected call to update, expected create to be called instead") } -func (at *PulumiServiceAccessTokenResource) Read(req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) { - ctx := context.Background() - +func (at *PulumiServiceAccessTokenResource) Read(ctx context.Context, req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) { // the access token is immutable; if we get nil it got deleted, otherwise all data is the same - accessToken, err := at.client.GetAccessToken(ctx, req.GetId()) + accessToken, err := GetClient[*pulumiapi.Client](ctx).GetAccessToken(ctx, req.GetId()) if err != nil { return nil, err } @@ -117,12 +111,8 @@ func (at *PulumiServiceAccessTokenResource) Invoke(_ *pulumiserviceProvider, req return &pulumirpc.InvokeResponse{Return: nil}, fmt.Errorf("unknown function '%s'", req.Tok) } -func (at *PulumiServiceAccessTokenResource) Configure(_ PulumiServiceConfig) { -} - func (at *PulumiServiceAccessTokenResource) createAccessToken(ctx context.Context, input PulumiServiceAccessTokenInput) (*pulumiapi.AccessToken, error) { - - accessToken, err := at.client.CreateAccessToken(ctx, input.Description) + accessToken, err := GetClient[*pulumiapi.Client](ctx).CreateAccessToken(ctx, input.Description) if err != nil { return nil, err } @@ -131,7 +121,7 @@ func (at *PulumiServiceAccessTokenResource) createAccessToken(ctx context.Contex } func (at *PulumiServiceAccessTokenResource) deleteAccessToken(ctx context.Context, tokenId string) error { - return at.client.DeleteAccessToken(ctx, tokenId) + return GetClient[*pulumiapi.Client](ctx).DeleteAccessToken(ctx, tokenId) } func diffAccessTokenProperties(req *pulumirpc.DiffRequest, replaceProps []string) (*pulumirpc.DiffResponse, error) { diff --git a/provider/pkg/provider/agent_pool.go b/provider/pkg/provider/agent_pool.go index 3b8e7c28..0035f584 100644 --- a/provider/pkg/provider/agent_pool.go +++ b/provider/pkg/provider/agent_pool.go @@ -13,9 +13,7 @@ import ( pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go" ) -type PulumiServiceAgentPoolResource struct { - client pulumiapi.AgentPoolClient -} +type PulumiServiceAgentPoolResource struct{} type PulumiServiceAgentPoolInput struct { OrgName string @@ -53,7 +51,7 @@ func (ap *PulumiServiceAgentPoolResource) Name() string { return "pulumiservice:index:AgentPool" } -func (ap *PulumiServiceAgentPoolResource) Diff(req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) { +func (ap *PulumiServiceAgentPoolResource) Diff(_ context.Context, req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) { olds, err := plugin.UnmarshalProperties(req.GetOlds(), plugin.MarshalOptions{KeepUnknowns: false, SkipNulls: true}) if err != nil { return nil, err @@ -105,8 +103,7 @@ func (ap *PulumiServiceAgentPoolResource) Diff(req *pulumirpc.DiffRequest) (*pul }, nil } -func (ap *PulumiServiceAgentPoolResource) Delete(req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) { - ctx := context.Background() +func (ap *PulumiServiceAgentPoolResource) Delete(ctx context.Context, req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) { err := ap.deleteAgentPool(ctx, req.Id) if err != nil { @@ -116,8 +113,7 @@ func (ap *PulumiServiceAgentPoolResource) Delete(req *pulumirpc.DeleteRequest) ( return &pbempty.Empty{}, nil } -func (ap *PulumiServiceAgentPoolResource) Create(req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) { - ctx := context.Background() +func (ap *PulumiServiceAgentPoolResource) Create(ctx context.Context, req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) { inputs, err := plugin.UnmarshalProperties(req.GetProperties(), plugin.MarshalOptions{KeepUnknowns: true, SkipNulls: true}) if err != nil { return nil, err @@ -154,12 +150,11 @@ func (ap *PulumiServiceAgentPoolResource) Create(req *pulumirpc.CreateRequest) ( } -func (ap *PulumiServiceAgentPoolResource) Check(req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) { +func (ap *PulumiServiceAgentPoolResource) Check(_ context.Context, req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) { return &pulumirpc.CheckResponse{Inputs: req.News, Failures: nil}, nil } -func (ap *PulumiServiceAgentPoolResource) Update(req *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) { - ctx := context.Background() +func (ap *PulumiServiceAgentPoolResource) Update(ctx context.Context, req *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) { // ignore orgName because if that changed, we would have done a replace, so update would never have been called _, _, agentPoolId, err := splitAgentPoolId(req.GetId()) @@ -200,8 +195,7 @@ func (ap *PulumiServiceAgentPoolResource) Update(req *pulumirpc.UpdateRequest) ( }, nil } -func (ap *PulumiServiceAgentPoolResource) Read(req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) { - ctx := context.Background() +func (ap *PulumiServiceAgentPoolResource) Read(ctx context.Context, req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) { urn := req.GetId() orgName, _, agentPoolId, err := splitAgentPoolId(urn) @@ -210,7 +204,7 @@ func (ap *PulumiServiceAgentPoolResource) Read(req *pulumirpc.ReadRequest) (*pul } // the agent id is immutable; if we get nil it got deleted, otherwise all data is the same - agentPool, err := ap.client.GetAgentPool(ctx, agentPoolId, orgName) + agentPool, err := GetClient[pulumiapi.AgentPoolClient](ctx).GetAgentPool(ctx, agentPoolId, orgName) if err != nil { return nil, err } @@ -228,11 +222,8 @@ func (ap *PulumiServiceAgentPoolResource) Invoke(_ *pulumiserviceProvider, req * return &pulumirpc.InvokeResponse{Return: nil}, fmt.Errorf("unknown function '%s'", req.Tok) } -func (ap *PulumiServiceAgentPoolResource) Configure(_ PulumiServiceConfig) { -} - func (ap *PulumiServiceAgentPoolResource) createAgentPool(ctx context.Context, input PulumiServiceAgentPoolInput) (*pulumiapi.AgentPool, error) { - agentPool, err := ap.client.CreateAgentPool(ctx, input.OrgName, input.Name, input.Description) + agentPool, err := GetClient[pulumiapi.AgentPoolClient](ctx).CreateAgentPool(ctx, input.OrgName, input.Name, input.Description) if err != nil { return nil, err } @@ -241,7 +232,7 @@ func (ap *PulumiServiceAgentPoolResource) createAgentPool(ctx context.Context, i } func (ap *PulumiServiceAgentPoolResource) updateAgentPool(ctx context.Context, agentPoolId string, input PulumiServiceAgentPoolInput) error { - return ap.client.UpdateAgentPool(ctx, agentPoolId, input.OrgName, input.Name, input.Description) + return GetClient[pulumiapi.AgentPoolClient](ctx).UpdateAgentPool(ctx, agentPoolId, input.OrgName, input.Name, input.Description) } func (ap *PulumiServiceAgentPoolResource) deleteAgentPool(ctx context.Context, id string) error { @@ -250,7 +241,7 @@ func (ap *PulumiServiceAgentPoolResource) deleteAgentPool(ctx context.Context, i if err != nil { return err } - return ap.client.DeleteAgentPool(ctx, agentPoolId, orgName) + return GetClient[pulumiapi.AgentPoolClient](ctx).DeleteAgentPool(ctx, agentPoolId, orgName) } diff --git a/provider/pkg/provider/agent_pool_test.go b/provider/pkg/provider/agent_pool_test.go index 199cf1f9..796c5a29 100644 --- a/provider/pkg/provider/agent_pool_test.go +++ b/provider/pkg/provider/agent_pool_test.go @@ -40,16 +40,15 @@ func TestAgentPool(t *testing.T) { func() (*pulumiapi.AgentPool, error) { return nil, nil }, ) - provider := PulumiServiceAgentPoolResource{ - client: mockedClient, - } + provider := PulumiServiceAgentPoolResource{} req := pulumirpc.ReadRequest{ Id: "org/abc/123", Urn: "urn:123", } - resp, err := provider.Read(&req) + resp, err := provider.Read(context.WithValue(context.Background(), + TestClientKey, mockedClient), &req) assert.NoError(t, err) assert.Equal(t, resp.Id, "") @@ -66,16 +65,15 @@ func TestAgentPool(t *testing.T) { }, ) - provider := PulumiServiceAgentPoolResource{ - client: mockedClient, - } + provider := PulumiServiceAgentPoolResource{} req := pulumirpc.ReadRequest{ Id: "org/abc/123", Urn: "urn:123", } - resp, err := provider.Read(&req) + resp, err := provider.Read(context.WithValue(context.Background(), + TestClientKey, mockedClient), &req) assert.NoError(t, err) assert.Equal(t, resp.Id, "org/abc/123") diff --git a/provider/pkg/provider/config.go b/provider/pkg/provider/config.go index c1ea13b2..7d61f248 100644 --- a/provider/pkg/provider/config.go +++ b/provider/pkg/provider/config.go @@ -1,57 +1,80 @@ package provider import ( + "context" "fmt" - "os" + "net/http" + "time" + "github.com/pulumi/pulumi-go-provider/infer" "github.com/pulumi/pulumi/sdk/v3/go/common/workspace" + + "github.com/pulumi/pulumi-pulumiservice/provider/pkg/internal/pulumiapi" ) const ( EnvVarPulumiAccessToken = "PULUMI_ACCESS_TOKEN" - EnvVarPulumiBackendUrl = "PULUMI_BACKEND_URL" + EnvVarPulumiBackendUrl = "PULUMI_BACKEND_URL" ) var ErrAccessTokenNotFound = fmt.Errorf("pulumi access token not found") -type PulumiServiceConfig struct { - Config map[string]string -} +type testClientKey struct{} + +// TestClientKey is the key used during client lookups to override the normal key. +var TestClientKey = testClientKey{} -func (pc *PulumiServiceConfig) getConfig(configName, envName string) string { - if val, ok := pc.Config[configName]; ok { - return val +// GetClient gets the client for a particular service, or a test client if it is +// available. +func GetClient[T any](ctx context.Context) T { + if v := ctx.Value(TestClientKey); v != nil { + return v.(T) } - return os.Getenv(envName) + return any(GetConfig(ctx).client).(T) } -func (pc *PulumiServiceConfig) getPulumiAccessToken() (*string, error) { - token := pc.getConfig("accessToken", EnvVarPulumiAccessToken) +// GetConfig accesses the config associated with the current request. +func GetConfig(ctx context.Context) Config { return *infer.GetConfig[*Config](ctx) } - if len(token) > 0 { - // found the token - return &token, nil - } - - // attempt to grab credentials directly from the pulumi configuration on the machine - creds, err := workspace.GetStoredCredentials() - if err != nil { - return nil, ErrAccessTokenNotFound - } - if token, ok := creds.AccessTokens[creds.Current]; ok { - return &token, nil - } - return nil, ErrAccessTokenNotFound +type Config struct { + AccessToken string `pulumi:"accessToken,optional" provider:"secret"` + ServiceURL string `pulumi:"serviceURL,optional"` + client *pulumiapi.Client } -func (pc *PulumiServiceConfig) getPulumiServiceUrl() (*string, error) { - url := pc.getConfig("apiUrl", EnvVarPulumiBackendUrl) - baseurl := "https://api.pulumi.com" +var ( + _ infer.Annotated = (*Config)(nil) + _ infer.CustomConfigure = (*Config)(nil) +) + +func (c *Config) Annotate(a infer.Annotator) { + a.Describe(&c.AccessToken, "Access Token to authenticate with Pulumi Cloud.") + a.SetDefault(&c.AccessToken, nil, EnvVarPulumiAccessToken) - if len(url) == 0 { - url = baseurl + a.Describe(&c.ServiceURL, "The service URL used to reach Pulumi Cloud.") + a.SetDefault(&c.ServiceURL, "https://api.pulumi.com", EnvVarPulumiBackendUrl) +} + +func (c *Config) Configure(context.Context) error { + // Ensure that we have an access token + if len(c.AccessToken) == 0 { + creds, err := workspace.GetStoredCredentials() + if err != nil { + return ErrAccessTokenNotFound + } + if token, ok := creds.AccessTokens[creds.Current]; ok { + c.AccessToken = token + } else { + return ErrAccessTokenNotFound + } } - return &url, nil + // Construct the PulumiService client + client, err := pulumiapi.NewClient(&http.Client{ + Timeout: 60 * time.Second, + }, c.AccessToken, c.ServiceURL) + + c.client = client + return err } diff --git a/provider/pkg/provider/config_test.go b/provider/pkg/provider/config_test.go index e4506bf6..3d447bb3 100644 --- a/provider/pkg/provider/config_test.go +++ b/provider/pkg/provider/config_test.go @@ -1,7 +1,7 @@ package provider import ( - "os" + "context" "testing" "github.com/pulumi/pulumi/sdk/v3/go/common/workspace" @@ -12,49 +12,21 @@ const ( EnvVarPulumiHome = "PULUMI_HOME" ) +func WithClient[T any](client T) context.Context { + return context.WithValue(context.Background(), TestClientKey, client) +} + func TestGetPulumiAccessToken(t *testing.T) { wantToken := "pul-1234abcd" - // setEnv sets environment variable and returns a function to reset - // environment variable back to previous value - setEnv := func(envVar, val string) func() { - oldVal := os.Getenv(envVar) - os.Setenv(envVar, val) - return func() { - os.Setenv(envVar, oldVal) - } - } - - t.Run("Uses Config Variable", func(t *testing.T) { - defer setEnv(EnvVarPulumiAccessToken, "")() - c := PulumiServiceConfig{ - Config: map[string]string{ - "accessToken": wantToken, - }, - } - gotToken, err := c.getPulumiAccessToken() - assert.NoError(t, err) - assert.Equal(t, wantToken, *gotToken) - - }) - - t.Run("Uses Environment Variable", func(t *testing.T) { - c := PulumiServiceConfig{} - defer setEnv(EnvVarPulumiAccessToken, wantToken)() - - gotToken, err := c.getPulumiAccessToken() - assert.NoError(t, err) - assert.Equal(t, wantToken, *gotToken) - }) - t.Run("Uses Saved Credential", func(t *testing.T) { - c := PulumiServiceConfig{} + c := Config{} // ensure env var isn't set - defer setEnv(EnvVarPulumiAccessToken, "")() + t.Setenv(EnvVarPulumiAccessToken, "") dir := t.TempDir() // set home directory so that workspace writes to temp dir - defer setEnv(EnvVarPulumiHome, dir)() + t.Setenv(EnvVarPulumiHome, dir) account := "https://api.pulumi.com" @@ -67,23 +39,22 @@ func TestGetPulumiAccessToken(t *testing.T) { if err != nil { t.Fatalf("failed to store test credentials: %v", err) } - gotToken, err := c.getPulumiAccessToken() + err = c.Configure(context.Background()) assert.NoError(t, err) - assert.Equal(t, wantToken, *gotToken) + assert.Equal(t, wantToken, c.AccessToken) }) t.Run("Returns Error", func(t *testing.T) { - c := PulumiServiceConfig{} + c := Config{} // point PULUMI_HOME to empty dir so there's no credentials available dir := t.TempDir() - defer setEnv(EnvVarPulumiHome, dir)() + t.Setenv(EnvVarPulumiHome, dir) // explicitly unset access token variable - defer setEnv(EnvVarPulumiAccessToken, "")() - - gotToken, err := c.getPulumiAccessToken() + t.Setenv(EnvVarPulumiAccessToken, "") + err := c.Configure(context.Background()) - assert.Nil(t, gotToken) + assert.Empty(t, c.AccessToken) assert.Equal(t, ErrAccessTokenNotFound, err) }) } diff --git a/provider/pkg/provider/deployment_schedules.go b/provider/pkg/provider/deployment_schedules.go index e1c9ee64..063c6671 100644 --- a/provider/pkg/provider/deployment_schedules.go +++ b/provider/pkg/provider/deployment_schedules.go @@ -14,9 +14,7 @@ import ( "google.golang.org/protobuf/types/known/structpb" ) -type PulumiServiceDeploymentScheduleResource struct { - client pulumiapi.ScheduleClient -} +type PulumiServiceDeploymentScheduleResource struct{} type PulumiServiceDeploymentScheduleInput struct { Stack pulumiapi.StackName @@ -30,6 +28,10 @@ type PulumiServiceSharedScheduleOutput struct { ScheduleID string `pulumi:"scheduleId"` } +func (*PulumiServiceDeploymentScheduleResource) client(ctx context.Context) pulumiapi.ScheduleClient { + return GetClient[pulumiapi.ScheduleClient](ctx) +} + func StackToPropertyMap(stack pulumiapi.StackName) resource.PropertyMap { propertyMap := resource.PropertyMap{} propertyMap["organization"] = resource.NewPropertyValue(stack.OrgName) @@ -192,7 +194,7 @@ func ScheduleSharedDiff(req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, er }, nil } -func (st *PulumiServiceDeploymentScheduleResource) Diff(req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) { +func (st *PulumiServiceDeploymentScheduleResource) Diff(_ context.Context, req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) { return ScheduleSharedDiff(req) } @@ -209,11 +211,11 @@ func ScheduleSharedDelete(req *pulumirpc.DeleteRequest, client pulumiapi.Schedul return &pbempty.Empty{}, nil } -func (st *PulumiServiceDeploymentScheduleResource) Delete(req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) { - return ScheduleSharedDelete(req, st.client) +func (st *PulumiServiceDeploymentScheduleResource) Delete(ctx context.Context, req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) { + return ScheduleSharedDelete(req, st.client(ctx)) } -func (st *PulumiServiceDeploymentScheduleResource) Create(req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) { +func (st *PulumiServiceDeploymentScheduleResource) Create(ctx context.Context, req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) { input, err := ToPulumiServiceDeploymentScheduleInput(req.GetProperties()) if err != nil { return nil, err @@ -226,7 +228,7 @@ func (st *PulumiServiceDeploymentScheduleResource) Create(req *pulumirpc.CreateR PulumiOperation: input.PulumiOperation, }, } - scheduleID, err := st.client.CreateDeploymentSchedule(context.Background(), input.Stack, scheduleReq) + scheduleID, err := st.client(ctx).CreateDeploymentSchedule(ctx, input.Stack, scheduleReq) if err != nil { return nil, err } @@ -248,7 +250,7 @@ func (st *PulumiServiceDeploymentScheduleResource) Create(req *pulumirpc.CreateR }, nil } -func (st *PulumiServiceDeploymentScheduleResource) Check(req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) { +func (st *PulumiServiceDeploymentScheduleResource) Check(ctx context.Context, req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) { inputMap, err := plugin.UnmarshalProperties(req.GetNews(), plugin.MarshalOptions{KeepUnknowns: true, SkipNulls: true}) if err != nil { return nil, err @@ -285,7 +287,7 @@ func (st *PulumiServiceDeploymentScheduleResource) Check(req *pulumirpc.CheckReq return &pulumirpc.CheckResponse{Inputs: req.GetNews(), Failures: failures}, nil } -func (st *PulumiServiceDeploymentScheduleResource) Update(req *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) { +func (st *PulumiServiceDeploymentScheduleResource) Update(ctx context.Context, req *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) { previousOutput, err := ToPulumiServiceSharedScheduleOutput(req.GetOlds()) if err != nil { return nil, err @@ -302,7 +304,7 @@ func (st *PulumiServiceDeploymentScheduleResource) Update(req *pulumirpc.UpdateR PulumiOperation: input.PulumiOperation, }, } - scheduleID, err := st.client.UpdateDeploymentSchedule(context.Background(), input.Stack, updateReq, previousOutput.ScheduleID) + scheduleID, err := st.client(ctx).UpdateDeploymentSchedule(ctx, input.Stack, updateReq, previousOutput.ScheduleID) if err != nil { return nil, err } @@ -322,7 +324,7 @@ func (st *PulumiServiceDeploymentScheduleResource) Update(req *pulumirpc.UpdateR }, nil } -func (st *PulumiServiceDeploymentScheduleResource) Read(req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) { +func (st *PulumiServiceDeploymentScheduleResource) Read(ctx context.Context, req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) { output, err := ToPulumiServiceSharedScheduleOutput(req.GetProperties()) if err != nil { return nil, err @@ -332,7 +334,7 @@ func (st *PulumiServiceDeploymentScheduleResource) Read(req *pulumirpc.ReadReque return nil, err } - scheduleID, err := st.client.GetSchedule(context.Background(), output.Stack, output.ScheduleID) + scheduleID, err := st.client(ctx).GetSchedule(ctx, output.Stack, output.ScheduleID) if err != nil { return nil, fmt.Errorf("failed to read DeploymentSchedule (%q): %w", req.Id, err) } @@ -362,6 +364,3 @@ func (st *PulumiServiceDeploymentScheduleResource) Read(req *pulumirpc.ReadReque func (st *PulumiServiceDeploymentScheduleResource) Name() string { return "pulumiservice:index:DeploymentSchedule" } - -func (st *PulumiServiceDeploymentScheduleResource) Configure(_ PulumiServiceConfig) { -} diff --git a/provider/pkg/provider/deployment_setting_test.go b/provider/pkg/provider/deployment_setting_test.go index bdf45e91..62fe4cd8 100644 --- a/provider/pkg/provider/deployment_setting_test.go +++ b/provider/pkg/provider/deployment_setting_test.go @@ -39,16 +39,14 @@ func TestDeploymentSettings(t *testing.T) { func() (*pulumiapi.DeploymentSettings, error) { return nil, nil }, ) - provider := PulumiServiceDeploymentSettingsResource{ - client: mockedClient, - } + provider := PulumiServiceDeploymentSettingsResource{} req := pulumirpc.ReadRequest{ Id: "abc/def/123", Urn: "urn:123", } - resp, err := provider.Read(&req) + resp, err := provider.Read(WithClient(mockedClient), &req) assert.NoError(t, err) assert.Equal(t, resp.Id, "") @@ -67,16 +65,14 @@ func TestDeploymentSettings(t *testing.T) { }, ) - provider := PulumiServiceDeploymentSettingsResource{ - client: mockedClient, - } + provider := PulumiServiceDeploymentSettingsResource{} req := pulumirpc.ReadRequest{ Id: "abc/def/123", Urn: "urn:123", } - resp, err := provider.Read(&req) + resp, err := provider.Read(WithClient(mockedClient), &req) assert.NoError(t, err) assert.Equal(t, resp.Id, "abc/def/123") diff --git a/provider/pkg/provider/deployment_settings.go b/provider/pkg/provider/deployment_settings.go index b8ebe9e0..8e8b0620 100644 --- a/provider/pkg/provider/deployment_settings.go +++ b/provider/pkg/provider/deployment_settings.go @@ -186,9 +186,7 @@ func (ds *PulumiServiceDeploymentSettingsInput) ToPropertyMap() resource.Propert return pm } -type PulumiServiceDeploymentSettingsResource struct { - client pulumiapi.DeploymentSettingsClient -} +type PulumiServiceDeploymentSettingsResource struct{} func (ds *PulumiServiceDeploymentSettingsResource) ToPulumiServiceDeploymentSettingsInput(inputMap resource.PropertyMap) PulumiServiceDeploymentSettingsInput { input := PulumiServiceDeploymentSettingsInput{} @@ -484,11 +482,11 @@ func getSecretOrStringValue(prop resource.PropertyValue) string { } } -func (ds *PulumiServiceDeploymentSettingsResource) Diff(req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) { +func (ds *PulumiServiceDeploymentSettingsResource) Diff(_ context.Context, req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) { return considerAllChangesReplaces(req) } -func (ds *PulumiServiceDeploymentSettingsResource) Check(req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) { +func (ds *PulumiServiceDeploymentSettingsResource) Check(_ context.Context, req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) { news, err := plugin.UnmarshalProperties(req.GetNews(), plugin.MarshalOptions{KeepUnknowns: true, SkipNulls: true}) if err != nil { return nil, err @@ -507,16 +505,12 @@ func (ds *PulumiServiceDeploymentSettingsResource) Check(req *pulumirpc.CheckReq return &pulumirpc.CheckResponse{Inputs: req.News, Failures: failures}, nil } -func (ds *PulumiServiceDeploymentSettingsResource) Configure(_ PulumiServiceConfig) {} - -func (ds *PulumiServiceDeploymentSettingsResource) Read(req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) { - ctx := context.Background() - +func (ds *PulumiServiceDeploymentSettingsResource) Read(ctx context.Context, req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) { var stack pulumiapi.StackName if err := stack.FromID(req.Id); err != nil { return nil, err } - settings, err := ds.client.GetDeploymentSettings(ctx, stack) + settings, err := GetClient[pulumiapi.DeploymentSettingsClient](ctx).GetDeploymentSettings(ctx, stack) if err != nil { return nil, err } @@ -547,8 +541,7 @@ func (ds *PulumiServiceDeploymentSettingsResource) Read(req *pulumirpc.ReadReque }, nil } -func (ds *PulumiServiceDeploymentSettingsResource) Delete(req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) { - ctx := context.Background() +func (ds *PulumiServiceDeploymentSettingsResource) Delete(ctx context.Context, req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) { inputsMap, err := plugin.UnmarshalProperties(req.GetProperties(), plugin.MarshalOptions{KeepUnknowns: true, SkipNulls: true}) if err != nil { return nil, err @@ -557,15 +550,14 @@ func (ds *PulumiServiceDeploymentSettingsResource) Delete(req *pulumirpc.DeleteR if err != nil { return nil, err } - err = ds.client.DeleteDeploymentSettings(ctx, inputs.Stack) + err = GetClient[pulumiapi.DeploymentSettingsClient](ctx).DeleteDeploymentSettings(ctx, inputs.Stack) if err != nil { return nil, err } return &pbempty.Empty{}, nil } -func (ds *PulumiServiceDeploymentSettingsResource) Create(req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) { - ctx := context.Background() +func (ds *PulumiServiceDeploymentSettingsResource) Create(ctx context.Context, req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) { inputsMap, err := plugin.UnmarshalProperties(req.GetProperties(), plugin.MarshalOptions{KeepUnknowns: true, SkipNulls: true, KeepSecrets: true}) if err != nil { @@ -573,7 +565,7 @@ func (ds *PulumiServiceDeploymentSettingsResource) Create(req *pulumirpc.CreateR } inputs := ds.ToPulumiServiceDeploymentSettingsInput(inputsMap) settings := inputs.DeploymentSettings - err = ds.client.CreateDeploymentSettings(ctx, inputs.Stack, settings) + err = GetClient[pulumiapi.DeploymentSettingsClient](ctx).CreateDeploymentSettings(ctx, inputs.Stack, settings) if err != nil { return nil, err } @@ -583,7 +575,7 @@ func (ds *PulumiServiceDeploymentSettingsResource) Create(req *pulumirpc.CreateR }, nil } -func (ds *PulumiServiceDeploymentSettingsResource) Update(_ *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) { +func (ds *PulumiServiceDeploymentSettingsResource) Update(context.Context, *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) { // For simplicity, all updates are destructive, so we just call Create. return nil, fmt.Errorf("unexpected call to update, expected create to be called instead") } diff --git a/provider/pkg/provider/drift_schedules.go b/provider/pkg/provider/drift_schedules.go index a250e9b8..44756114 100644 --- a/provider/pkg/provider/drift_schedules.go +++ b/provider/pkg/provider/drift_schedules.go @@ -13,9 +13,7 @@ import ( "google.golang.org/protobuf/types/known/structpb" ) -type PulumiServiceDriftScheduleResource struct { - client pulumiapi.ScheduleClient -} +type PulumiServiceDriftScheduleResource struct{} type PulumiServiceDriftScheduleInput struct { Stack pulumiapi.StackName @@ -28,6 +26,10 @@ type PulumiServiceDriftScheduleOutput struct { ScheduleID string `pulumi:"scheduleId"` } +func (*PulumiServiceDriftScheduleResource) client(ctx context.Context) pulumiapi.ScheduleClient { + return GetClient[pulumiapi.ScheduleClient](ctx) +} + func (i *PulumiServiceDriftScheduleInput) ToPropertyMap() resource.PropertyMap { propertyMap := StackToPropertyMap(i.Stack) @@ -62,15 +64,15 @@ func ToPulumiServiceDriftScheduleInput(properties *structpb.Struct) (*PulumiServ return &input, nil } -func (st *PulumiServiceDriftScheduleResource) Diff(req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) { +func (st *PulumiServiceDriftScheduleResource) Diff(_ context.Context, req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) { return ScheduleSharedDiff(req) } -func (st *PulumiServiceDriftScheduleResource) Delete(req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) { - return ScheduleSharedDelete(req, st.client) +func (st *PulumiServiceDriftScheduleResource) Delete(ctx context.Context, req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) { + return ScheduleSharedDelete(req, st.client(ctx)) } -func (st *PulumiServiceDriftScheduleResource) Create(req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) { +func (st *PulumiServiceDriftScheduleResource) Create(ctx context.Context, req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) { input, err := ToPulumiServiceDriftScheduleInput(req.GetProperties()) if err != nil { return nil, err @@ -80,7 +82,7 @@ func (st *PulumiServiceDriftScheduleResource) Create(req *pulumirpc.CreateReques ScheduleCron: input.ScheduleCron, AutoRemediate: input.AutoRemediate, } - scheduleID, err := st.client.CreateDriftSchedule(context.Background(), input.Stack, scheduleReq) + scheduleID, err := st.client(ctx).CreateDriftSchedule(ctx, input.Stack, scheduleReq) if err != nil { return nil, err } @@ -102,7 +104,7 @@ func (st *PulumiServiceDriftScheduleResource) Create(req *pulumirpc.CreateReques }, nil } -func (st *PulumiServiceDriftScheduleResource) Check(req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) { +func (st *PulumiServiceDriftScheduleResource) Check(ctx context.Context, req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) { inputMap, err := plugin.UnmarshalProperties(req.GetNews(), plugin.MarshalOptions{KeepUnknowns: true, SkipNulls: true}) if err != nil { return nil, err @@ -128,7 +130,7 @@ func (st *PulumiServiceDriftScheduleResource) Check(req *pulumirpc.CheckRequest) return &pulumirpc.CheckResponse{Inputs: req.GetNews(), Failures: failures}, nil } -func (st *PulumiServiceDriftScheduleResource) Update(req *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) { +func (st *PulumiServiceDriftScheduleResource) Update(ctx context.Context, req *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) { previousOutput, err := ToPulumiServiceSharedScheduleOutput(req.GetOlds()) if err != nil { return nil, err @@ -142,7 +144,7 @@ func (st *PulumiServiceDriftScheduleResource) Update(req *pulumirpc.UpdateReques ScheduleCron: input.ScheduleCron, AutoRemediate: input.AutoRemediate, } - scheduleID, err := st.client.UpdateDriftSchedule(context.Background(), input.Stack, updateReq, previousOutput.ScheduleID) + scheduleID, err := st.client(ctx).UpdateDriftSchedule(ctx, input.Stack, updateReq, previousOutput.ScheduleID) if err != nil { return nil, err } @@ -162,7 +164,7 @@ func (st *PulumiServiceDriftScheduleResource) Update(req *pulumirpc.UpdateReques }, nil } -func (st *PulumiServiceDriftScheduleResource) Read(req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) { +func (st *PulumiServiceDriftScheduleResource) Read(ctx context.Context, req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) { output, err := ToPulumiServiceSharedScheduleOutput(req.GetProperties()) if err != nil { return nil, err @@ -172,7 +174,7 @@ func (st *PulumiServiceDriftScheduleResource) Read(req *pulumirpc.ReadRequest) ( return nil, err } - scheduleID, err := st.client.GetSchedule(context.Background(), output.Stack, output.ScheduleID) + scheduleID, err := st.client(ctx).GetSchedule(ctx, output.Stack, output.ScheduleID) if err != nil { return nil, fmt.Errorf("failed to read DriftSchedule (%q): %w", req.Id, err) } @@ -202,6 +204,3 @@ func (st *PulumiServiceDriftScheduleResource) Read(req *pulumirpc.ReadRequest) ( func (st *PulumiServiceDriftScheduleResource) Name() string { return "pulumiservice:index:DriftSchedule" } - -func (st *PulumiServiceDriftScheduleResource) Configure(_ PulumiServiceConfig) { -} diff --git a/provider/pkg/provider/org_access_token.go b/provider/pkg/provider/org_access_token.go index 6ecb4ede..d593f0b7 100644 --- a/provider/pkg/provider/org_access_token.go +++ b/provider/pkg/provider/org_access_token.go @@ -13,9 +13,7 @@ import ( pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go" ) -type PulumiServiceOrgAccessTokenResource struct { - client *pulumiapi.Client -} +type PulumiServiceOrgAccessTokenResource struct{} type PulumiServiceOrgAccessTokenInput struct { OrgName string @@ -59,12 +57,11 @@ func (ot *PulumiServiceOrgAccessTokenResource) Name() string { return "pulumiservice:index:OrgAccessToken" } -func (ot *PulumiServiceOrgAccessTokenResource) Diff(req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) { +func (ot *PulumiServiceOrgAccessTokenResource) Diff(_ context.Context, req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) { return diffAccessTokenProperties(req, []string{"name", "organizationName", "description", "admin"}) } -func (ot *PulumiServiceOrgAccessTokenResource) Delete(req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) { - ctx := context.Background() +func (ot *PulumiServiceOrgAccessTokenResource) Delete(ctx context.Context, req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) { err := ot.deleteOrgAccessToken(ctx, req.Id) if err != nil { @@ -74,8 +71,7 @@ func (ot *PulumiServiceOrgAccessTokenResource) Delete(req *pulumirpc.DeleteReque return &pbempty.Empty{}, nil } -func (ot *PulumiServiceOrgAccessTokenResource) Create(req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) { - ctx := context.Background() +func (ot *PulumiServiceOrgAccessTokenResource) Create(ctx context.Context, req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) { inputs, err := plugin.UnmarshalProperties(req.GetProperties(), plugin.MarshalOptions{KeepUnknowns: true, SkipNulls: true}) if err != nil { return nil, err @@ -113,23 +109,22 @@ func (ot *PulumiServiceOrgAccessTokenResource) Create(req *pulumirpc.CreateReque } -func (ot *PulumiServiceOrgAccessTokenResource) Check(req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) { +func (ot *PulumiServiceOrgAccessTokenResource) Check(_ context.Context, req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) { return &pulumirpc.CheckResponse{Inputs: req.News, Failures: nil}, nil } -func (ot *PulumiServiceOrgAccessTokenResource) Update(_ *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) { +func (ot *PulumiServiceOrgAccessTokenResource) Update(context.Context, *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) { // all updates are destructive, so we just call Create. return nil, fmt.Errorf("unexpected call to update, expected create to be called instead") } -func (ot *PulumiServiceOrgAccessTokenResource) Read(req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) { - ctx := context.Background() +func (ot *PulumiServiceOrgAccessTokenResource) Read(ctx context.Context, req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) { urn := req.GetId() orgName, _, tokenId, err := splitOrgAccessTokenId(urn) // the org access token is immutable; if we get nil it got deleted, otherwise all data is the same - accessToken, err := ot.client.GetOrgAccessToken(ctx, tokenId, orgName) + accessToken, err := GetClient[*pulumiapi.Client](ctx).GetOrgAccessToken(ctx, tokenId, orgName) if err != nil { return nil, err } @@ -147,12 +142,9 @@ func (ot *PulumiServiceOrgAccessTokenResource) Invoke(_ *pulumiserviceProvider, return &pulumirpc.InvokeResponse{Return: nil}, fmt.Errorf("unknown function '%s'", req.Tok) } -func (ot *PulumiServiceOrgAccessTokenResource) Configure(_ PulumiServiceConfig) { -} - func (ot *PulumiServiceOrgAccessTokenResource) createOrgAccessToken(ctx context.Context, input PulumiServiceOrgAccessTokenInput) (*pulumiapi.AccessToken, error) { - accessToken, err := ot.client.CreateOrgAccessToken(ctx, input.Name, input.OrgName, input.Description, input.Admin) + accessToken, err := GetClient[*pulumiapi.Client](ctx).CreateOrgAccessToken(ctx, input.Name, input.OrgName, input.Description, input.Admin) if err != nil { return nil, err } @@ -166,7 +158,7 @@ func (ot *PulumiServiceOrgAccessTokenResource) deleteOrgAccessToken(ctx context. if err != nil { return err } - return ot.client.DeleteOrgAccessToken(ctx, tokenId, orgName) + return GetClient[*pulumiapi.Client](ctx).DeleteOrgAccessToken(ctx, tokenId, orgName) } diff --git a/provider/pkg/provider/provider.go b/provider/pkg/provider/provider.go index 6e8e208e..8634c041 100644 --- a/provider/pkg/provider/provider.go +++ b/provider/pkg/provider/provider.go @@ -18,29 +18,78 @@ import ( "context" "encoding/json" "fmt" - "net/http" - "strings" - "time" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" pbempty "google.golang.org/protobuf/types/known/emptypb" - "github.com/pulumi/pulumi-pulumiservice/provider/pkg/internal/pulumiapi" + "github.com/pulumi/pulumi-go-provider/infer" + inferSchema "github.com/pulumi/pulumi-go-provider/middleware/schema" "github.com/pulumi/pulumi/pkg/v3/codegen/schema" "github.com/pulumi/pulumi/pkg/v3/resource/provider" "github.com/pulumi/pulumi/sdk/v3/go/common/resource" + "github.com/pulumi/pulumi/sdk/v3/go/common/tokens" pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go" ) +func inferProvider() infer.Options { + return infer.Options{ + Metadata: inferSchema.Metadata{ + DisplayName: "Pulumi Cloud", + Description: "A native Pulumi package for creating and managing Pulumi Cloud constructs", + LanguageMap: map[string]any{ + "csharp": map[string]any{ + "namespaces": map[string]any{ + "pulumiservice": "PulumiService", + }, + "packageReferences": map[string]any{ + "Pulumi": "3.*", + }, + }, + "go": map[string]any{ + "generateResourceContainerTypes": true, + "importBasePath": "github.com/pulumi/pulumi-pulumiservice/sdk/go/pulumiservice", + }, + "nodejs": map[string]any{ + "packageName": "@pulumi/pulumiservice", + "dependencies": map[string]any{ + "@pulumi/pulumi": "^3.0.0", + }, + }, + "python": map[string]any{ + "packageName": "pulumi_pulumiservice", + "requires": map[string]any{ + "pulumi": ">=3.0.0,<4.0.0", + }, + }, + }, + Keywords: []string{ + "pulumi", + "kind/native", + "category/infrastructure", + }, + Homepage: "https://pulumi.com", + Repository: "https://github.com/pulumi/pulumi-pulumiservice", + Publisher: "Pulumi", + License: "Apache-2.0", + }, + Resources: []infer.InferredResource{}, + Components: []infer.InferredComponent{}, + Functions: []infer.InferredFunction{}, + Config: infer.Config[*Config](), + ModuleMap: map[tokens.ModuleName]tokens.ModuleName{ + "provider": "index", + }, + } +} + type PulumiServiceResource interface { - Configure(config PulumiServiceConfig) - Diff(req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) - Create(req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) - Delete(req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) - Check(req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) - Update(req *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) - Read(req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) + Diff(context.Context, *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) + Create(context.Context, *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) + Delete(context.Context, *pulumirpc.DeleteRequest) (*pbempty.Empty, error) + Check(context.Context, *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) + Update(context.Context, *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) + Read(context.Context, *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) Name() string } @@ -55,19 +104,6 @@ type pulumiserviceProvider struct { AccessToken string } -func makeProvider(host *provider.HostClient, name, version, schema string) (pulumirpc.ResourceProviderServer, error) { - // inject version into schema - versionedSchema := mustSetSchemaVersion(schema, version) - // Return the new provider - return &pulumiserviceProvider{ - host: host, - name: name, - schema: versionedSchema, - version: version, - AccessToken: "", - }, nil -} - // Call dynamically executes a method in the provider associated with a component resource. func (k *pulumiserviceProvider) Call(ctx context.Context, req *pulumirpc.CallRequest) (*pulumirpc.CallResponse, error) { return nil, status.Error(codes.Unimplemented, "Call is not yet implemented") @@ -99,77 +135,8 @@ func (k *pulumiserviceProvider) DiffConfig(ctx context.Context, req *pulumirpc.D } // Configure configures the resource provider with "globals" that control its behavior. -func (k *pulumiserviceProvider) Configure(_ context.Context, req *pulumirpc.ConfigureRequest) (*pulumirpc.ConfigureResponse, error) { - - sc := PulumiServiceConfig{} - sc.Config = make(map[string]string) - for key, val := range req.GetVariables() { - sc.Config[strings.TrimPrefix(key, "pulumiservice:config:")] = val - } - - httpClient := http.Client{ - Timeout: 60 * time.Second, - } - token, err := sc.getPulumiAccessToken() - if err != nil { - return nil, err - } - url, err := sc.getPulumiServiceUrl() - if err != nil { - return nil, err - } - client, err := pulumiapi.NewClient(&httpClient, *token, *url) - - if err != nil { - return nil, err - } - - k.pulumiResources = []PulumiServiceResource{ - &PulumiServiceTeamResource{ - client: client, - }, - &PulumiServiceAccessTokenResource{ - client: client, - }, - &PulumiServiceWebhookResource{ - client: client, - }, - &PulumiServiceStackTagResource{ - client: client, - }, - &TeamStackPermissionResource{ - client: client, - }, - &PulumiServiceTeamAccessTokenResource{ - client: client, - }, - &PulumiServiceOrgAccessTokenResource{ - client: client, - }, - &PulumiServiceDeploymentSettingsResource{ - client: client, - }, - &PulumiServiceAgentPoolResource{ - client: client, - }, - &PulumiServiceDeploymentScheduleResource{ - client: client, - }, - &PulumiServiceDriftScheduleResource{ - client: client, - }, - &PulumiServiceTtlScheduleResource{ - client: client, - }, - } - - for _, sr := range k.pulumiResources { - sr.Configure(sc) - } - - return &pulumirpc.ConfigureResponse{ - AcceptSecrets: true, - }, nil +func (k *pulumiserviceProvider) Configure(context.Context, *pulumirpc.ConfigureRequest) (*pulumirpc.ConfigureResponse, error) { + return &pulumirpc.ConfigureResponse{AcceptSecrets: true}, nil } // Invoke dynamically executes a built-in function in the provider. @@ -194,35 +161,35 @@ func (k *pulumiserviceProvider) StreamInvoke(req *pulumirpc.InvokeRequest, serve func (k *pulumiserviceProvider) Check(ctx context.Context, req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) { rn := getResourceNameFromRequest(req) res := k.getPulumiServiceResource(rn) - return res.Check(req) + return res.Check(ctx, req) } // Diff checks what impacts a hypothetical update will have on the resource's properties. func (k *pulumiserviceProvider) Diff(ctx context.Context, req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) { rn := getResourceNameFromRequest(req) res := k.getPulumiServiceResource(rn) - return res.Diff(req) + return res.Diff(ctx, req) } // Create allocates a new instance of the provided resource and returns its unique ID afterwards. func (k *pulumiserviceProvider) Create(ctx context.Context, req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) { rn := getResourceNameFromRequest(req) res := k.getPulumiServiceResource(rn) - return res.Create(req) + return res.Create(ctx, req) } // Read the current live state associated with a resource. func (k *pulumiserviceProvider) Read(ctx context.Context, req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) { rn := getResourceNameFromRequest(req) res := k.getPulumiServiceResource(rn) - return res.Read(req) + return res.Read(ctx, req) } // Update updates an existing resource with new values. func (k *pulumiserviceProvider) Update(ctx context.Context, req *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) { rn := getResourceNameFromRequest(req) res := k.getPulumiServiceResource(rn) - return res.Update(req) + return res.Update(ctx, req) } // Delete tears down an existing resource with the given ID. If it fails, the resource is assumed @@ -230,7 +197,7 @@ func (k *pulumiserviceProvider) Update(ctx context.Context, req *pulumirpc.Updat func (k *pulumiserviceProvider) Delete(ctx context.Context, req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) { rn := getResourceNameFromRequest(req) res := k.getPulumiServiceResource(rn) - return res.Delete(req) + return res.Delete(ctx, req) } // GetPluginInfo returns generic information about this plugin, like its version. @@ -242,9 +209,7 @@ func (k *pulumiserviceProvider) GetPluginInfo(context.Context, *pbempty.Empty) ( // GetSchema returns the JSON-serialized schema for the provider. func (k *pulumiserviceProvider) GetSchema(ctx context.Context, req *pulumirpc.GetSchemaRequest) (*pulumirpc.GetSchemaResponse, error) { - return &pulumirpc.GetSchemaResponse{ - Schema: k.schema, - }, nil + return &pulumirpc.GetSchemaResponse{Schema: k.schema}, nil } // Cancel signals the provider to gracefully shut down and abort any ongoing resource operations. diff --git a/provider/pkg/provider/schedules_test.go b/provider/pkg/provider/schedules_test.go index 99179c62..ad4a96c1 100644 --- a/provider/pkg/provider/schedules_test.go +++ b/provider/pkg/provider/schedules_test.go @@ -60,9 +60,7 @@ func TestDeploymentSchedule(t *testing.T) { func() (*string, error) { return nil, nil }, ) - provider := PulumiServiceDeploymentScheduleResource{ - client: mockedClient, - } + provider := PulumiServiceDeploymentScheduleResource{} input := PulumiServiceDeploymentScheduleInput{ Stack: pulumiapi.StackName{ @@ -88,7 +86,7 @@ func TestDeploymentSchedule(t *testing.T) { Properties: outputProperties, } - resp, err := provider.Read(&req) + resp, err := provider.Read(WithClient(mockedClient), &req) assert.NoError(t, err) assert.Equal(t, resp.Id, "") @@ -103,9 +101,7 @@ func TestDeploymentSchedule(t *testing.T) { }, ) - provider := PulumiServiceDeploymentScheduleResource{ - client: mockedClient, - } + provider := PulumiServiceDeploymentScheduleResource{} input := PulumiServiceDeploymentScheduleInput{ Stack: pulumiapi.StackName{ @@ -131,7 +127,7 @@ func TestDeploymentSchedule(t *testing.T) { Properties: outputProperties, } - resp, err := provider.Read(&req) + resp, err := provider.Read(WithClient(mockedClient), &req) assert.NoError(t, err) assert.Equal(t, resp.Id, "org/project/stack/fake-schedule-id") diff --git a/provider/pkg/provider/serve.go b/provider/pkg/provider/serve.go index 6d48b779..c0cd8705 100644 --- a/provider/pkg/provider/serve.go +++ b/provider/pkg/provider/serve.go @@ -15,18 +15,30 @@ package provider import ( - "github.com/pulumi/pulumi/pkg/v3/resource/provider" - "github.com/pulumi/pulumi/sdk/v3/go/common/util/cmdutil" - rpc "github.com/pulumi/pulumi/sdk/v3/proto/go" + p "github.com/pulumi/pulumi-go-provider" + "github.com/pulumi/pulumi-go-provider/infer" + "github.com/pulumi/pulumi-go-provider/middleware/rpc" ) -// Serve launches the gRPC server for the resource provider. -func Serve(providerName, version string, schema string) { - // Start gRPC service. - err := provider.Main(providerName, func(host *provider.HostClient) (rpc.ResourceProviderServer, error) { - return makeProvider(host, providerName, version, schema) - }) - if err != nil { - cmdutil.ExitError(err.Error()) - } +func Provider(name, version, legacySchema string) p.Provider { + return infer.Wrap(rpc.Provider(&pulumiserviceProvider{ + name: name, + schema: legacySchema, + version: version, + AccessToken: "", + pulumiResources: []PulumiServiceResource{ + &PulumiServiceTeamResource{}, + &PulumiServiceAccessTokenResource{}, + &PulumiServiceWebhookResource{}, + &PulumiServiceStackTagResource{}, + &TeamStackPermissionResource{}, + &PulumiServiceTeamAccessTokenResource{}, + &PulumiServiceOrgAccessTokenResource{}, + &PulumiServiceDeploymentSettingsResource{}, + &PulumiServiceAgentPoolResource{}, + &PulumiServiceDeploymentScheduleResource{}, + &PulumiServiceDriftScheduleResource{}, + &PulumiServiceTtlScheduleResource{}, + }, + }), inferProvider()) } diff --git a/provider/pkg/provider/stack_tags.go b/provider/pkg/provider/stack_tags.go index e093932e..d8d88e55 100644 --- a/provider/pkg/provider/stack_tags.go +++ b/provider/pkg/provider/stack_tags.go @@ -14,9 +14,7 @@ import ( pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go" ) -type PulumiServiceStackTagResource struct { - client *pulumiapi.Client -} +type PulumiServiceStackTagResource struct{} type PulumiServiceStackTagInput struct { Organization string `pulumi:"organization"` @@ -42,7 +40,7 @@ func (st *PulumiServiceStackTagResource) Name() string { return "pulumiservice:index:StackTag" } -func (st *PulumiServiceStackTagResource) Diff(req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) { +func (st *PulumiServiceStackTagResource) Diff(_ context.Context, req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) { olds, err := plugin.UnmarshalProperties(req.GetOlds(), plugin.MarshalOptions{KeepUnknowns: false, SkipNulls: true}) if err != nil { return nil, err @@ -79,8 +77,7 @@ func (st *PulumiServiceStackTagResource) Diff(req *pulumirpc.DiffRequest) (*pulu }, nil } -func (st *PulumiServiceStackTagResource) Delete(req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) { - ctx := context.Background() +func (st *PulumiServiceStackTagResource) Delete(ctx context.Context, req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) { var inputs PulumiServiceStackTagInput err := serde.FromProperties(req.GetProperties(), structTagKey, &inputs) if err != nil { @@ -91,15 +88,14 @@ func (st *PulumiServiceStackTagResource) Delete(req *pulumirpc.DeleteRequest) (* ProjectName: inputs.Project, StackName: inputs.Stack, } - err = st.client.DeleteStackTag(ctx, stackName, inputs.Name) + err = GetClient[*pulumiapi.Client](ctx).DeleteStackTag(ctx, stackName, inputs.Name) if err != nil { return nil, err } return &pbempty.Empty{}, nil } -func (st *PulumiServiceStackTagResource) Create(req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) { - ctx := context.Background() +func (st *PulumiServiceStackTagResource) Create(ctx context.Context, req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) { var inputs PulumiServiceStackTagInput err := serde.FromProperties(req.GetProperties(), structTagKey, &inputs) if err != nil { @@ -114,7 +110,7 @@ func (st *PulumiServiceStackTagResource) Create(req *pulumirpc.CreateRequest) (* Name: inputs.Name, Value: inputs.Value, } - err = st.client.CreateTag(ctx, stackName, stackTag) + err = GetClient[*pulumiapi.Client](ctx).CreateTag(ctx, stackName, stackTag) if err != nil { return nil, err } @@ -124,17 +120,16 @@ func (st *PulumiServiceStackTagResource) Create(req *pulumirpc.CreateRequest) (* }, nil } -func (st *PulumiServiceStackTagResource) Check(req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) { +func (st *PulumiServiceStackTagResource) Check(_ context.Context, req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) { return &pulumirpc.CheckResponse{Inputs: req.News, Failures: nil}, nil } -func (st *PulumiServiceStackTagResource) Update(req *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) { +func (st *PulumiServiceStackTagResource) Update(_ context.Context, req *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) { // all updates are destructive, so we just call Create. return nil, fmt.Errorf("unexpected call to update, expected create to be called instead") } -func (st *PulumiServiceStackTagResource) Read(req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) { - ctx := context.Background() +func (st *PulumiServiceStackTagResource) Read(ctx context.Context, req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) { var inputs PulumiServiceStackTagInput err := serde.FromProperties(req.GetProperties(), structTagKey, &inputs) if err != nil { @@ -145,7 +140,7 @@ func (st *PulumiServiceStackTagResource) Read(req *pulumirpc.ReadRequest) (*pulu ProjectName: inputs.Project, StackName: inputs.Stack, } - tag, err := st.client.GetStackTag(ctx, stackName, inputs.Name) + tag, err := GetClient[*pulumiapi.Client](ctx).GetStackTag(ctx, stackName, inputs.Name) if err != nil { return nil, fmt.Errorf("failed to read StackTag (%q): %w", req.Id, err) } @@ -164,6 +159,3 @@ func (st *PulumiServiceStackTagResource) Read(req *pulumirpc.ReadRequest) (*pulu Inputs: props, }, nil } - -func (st *PulumiServiceStackTagResource) Configure(_ PulumiServiceConfig) { -} diff --git a/provider/pkg/provider/stack_tags_test.go b/provider/pkg/provider/stack_tags_test.go index 9b08affe..b5804893 100644 --- a/provider/pkg/provider/stack_tags_test.go +++ b/provider/pkg/provider/stack_tags_test.go @@ -26,9 +26,7 @@ func TestStackTagsUpdate(t *testing.T) { t.Fatal(err) } - st := &PulumiServiceStackTagResource{ - client: apiClient, - } + st := &PulumiServiceStackTagResource{} input := PulumiServiceStackTagInput{ Organization: "org", @@ -49,7 +47,7 @@ func TestStackTagsUpdate(t *testing.T) { News: properties, } - _, err = st.Update(&upReq) + _, err = st.Update(WithClient(apiClient), &upReq) assert.ErrorContains(t, err, "unexpected call to update, expected create to be called instead") }) diff --git a/provider/pkg/provider/team.go b/provider/pkg/provider/team.go index e2955bb0..9df52074 100644 --- a/provider/pkg/provider/team.go +++ b/provider/pkg/provider/team.go @@ -19,10 +19,7 @@ import ( pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go" ) -type PulumiServiceTeamResource struct { - config PulumiServiceConfig - client pulumiapi.TeamClient -} +type PulumiServiceTeamResource struct{} type PulumiServiceTeamStackPermission struct { ProjectName string @@ -109,11 +106,7 @@ func (t *PulumiServiceTeamResource) Name() string { return "pulumiservice:index:Team" } -func (t *PulumiServiceTeamResource) Configure(config PulumiServiceConfig) { - t.config = config -} - -func (t *PulumiServiceTeamResource) Check(req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) { +func (t *PulumiServiceTeamResource) Check(_ context.Context, req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) { news := req.GetNews() newsMap, err := plugin.UnmarshalProperties(news, plugin.MarshalOptions{KeepUnknowns: true, SkipNulls: true}) if err != nil { @@ -151,8 +144,8 @@ func (t *PulumiServiceTeamResource) Check(req *pulumirpc.CheckRequest) (*pulumir return &pulumirpc.CheckResponse{Inputs: news, Failures: failures}, nil } -func (t *PulumiServiceTeamResource) Delete(req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) { - err := t.deleteTeam(context.Background(), req.Id) +func (t *PulumiServiceTeamResource) Delete(ctx context.Context, req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) { + err := t.deleteTeam(ctx, req.Id) if err != nil { return &pbempty.Empty{}, err } @@ -160,7 +153,7 @@ func (t *PulumiServiceTeamResource) Delete(req *pulumirpc.DeleteRequest) (*pbemp return &pbempty.Empty{}, nil } -func (t *PulumiServiceTeamResource) Diff(req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) { +func (t *PulumiServiceTeamResource) Diff(_ context.Context, req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) { olds, err := plugin.UnmarshalProperties(req.GetOlds(), plugin.MarshalOptions{KeepUnknowns: false, SkipNulls: true}) if err != nil { return nil, err @@ -188,15 +181,13 @@ func (t *PulumiServiceTeamResource) Diff(req *pulumirpc.DiffRequest) (*pulumirpc }, nil } -func (t *PulumiServiceTeamResource) Read(req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) { - ctx := context.Background() - +func (t *PulumiServiceTeamResource) Read(ctx context.Context, req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) { orgName, teamName, err := splitSingleSlashString(req.Id) if err != nil { return nil, err } - team, err := t.client.GetTeam(ctx, orgName, teamName) + team, err := GetClient[pulumiapi.TeamClient](ctx).GetTeam(ctx, orgName, teamName) if err != nil { return nil, fmt.Errorf("failed to read Team (%q): %w", req.Id, err) } @@ -226,8 +217,7 @@ func (t *PulumiServiceTeamResource) Read(req *pulumirpc.ReadRequest) (*pulumirpc }, nil } -func (t *PulumiServiceTeamResource) Update(req *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) { - ctx := context.Background() +func (t *PulumiServiceTeamResource) Update(ctx context.Context, req *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) { inputsOld, err := plugin.UnmarshalProperties(req.GetOlds(), plugin.MarshalOptions{KeepUnknowns: true, SkipNulls: true}) if err != nil { return nil, err @@ -246,7 +236,7 @@ func (t *PulumiServiceTeamResource) Update(req *pulumirpc.UpdateRequest) (*pulum inputsChanged.Description = teamNew.Description inputsChanged.DisplayName = teamNew.DisplayName - err = t.updateTeam(context.Background(), inputsChanged) + err = t.updateTeam(ctx, inputsChanged) if err != nil { return nil, err } @@ -283,8 +273,7 @@ func (t *PulumiServiceTeamResource) Update(req *pulumirpc.UpdateRequest) (*pulum }, nil } -func (t *PulumiServiceTeamResource) Create(req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) { - ctx := context.Background() +func (t *PulumiServiceTeamResource) Create(ctx context.Context, req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) { inputs, err := plugin.UnmarshalProperties(req.GetProperties(), plugin.MarshalOptions{KeepUnknowns: true, SkipNulls: true}) if err != nil { return nil, err @@ -326,7 +315,7 @@ func (t *PulumiServiceTeamResource) Create(req *pulumirpc.CreateRequest) (*pulum } func (t *PulumiServiceTeamResource) updateTeam(ctx context.Context, input PulumiServiceTeamInput) error { - err := t.client.UpdateTeam(ctx, input.OrganizationName, input.Name, input.DisplayName, input.Description) + err := GetClient[pulumiapi.TeamClient](ctx).UpdateTeam(ctx, input.OrganizationName, input.Name, input.DisplayName, input.Description) if err != nil { return err } @@ -334,7 +323,7 @@ func (t *PulumiServiceTeamResource) updateTeam(ctx context.Context, input Pulumi } func (t *PulumiServiceTeamResource) createTeam(ctx context.Context, input PulumiServiceTeamInput) (*string, error) { - team, err := t.client.CreateTeam(ctx, input.OrganizationName, input.Name, input.Type, input.DisplayName, input.Description, input.GitHubTeamID) + team, err := GetClient[pulumiapi.TeamClient](ctx).CreateTeam(ctx, input.OrganizationName, input.Name, input.Type, input.DisplayName, input.Description, input.GitHubTeamID) if err != nil { return nil, err } @@ -356,11 +345,11 @@ func (t *PulumiServiceTeamResource) deleteFromTeam(ctx context.Context, orgName return errors.New("username must not be empty") } - return t.client.DeleteMemberFromTeam(ctx, orgName, teamName, userName) + return GetClient[pulumiapi.TeamClient](ctx).DeleteMemberFromTeam(ctx, orgName, teamName, userName) } func (t *PulumiServiceTeamResource) addToTeam(ctx context.Context, orgName string, teamName string, userName string) error { - return t.client.AddMemberToTeam(ctx, orgName, teamName, userName) + return GetClient[pulumiapi.TeamClient](ctx).AddMemberToTeam(ctx, orgName, teamName, userName) } func (t *PulumiServiceTeamResource) deleteTeam(ctx context.Context, id string) error { @@ -368,7 +357,7 @@ func (t *PulumiServiceTeamResource) deleteTeam(ctx context.Context, id string) e if err != nil { return err } - err = t.client.DeleteTeam(ctx, orgName, teamName) + err = GetClient[pulumiapi.TeamClient](ctx).DeleteTeam(ctx, orgName, teamName) if err != nil { return err } diff --git a/provider/pkg/provider/team_access_token.go b/provider/pkg/provider/team_access_token.go index 4e67a9af..08a811f8 100644 --- a/provider/pkg/provider/team_access_token.go +++ b/provider/pkg/provider/team_access_token.go @@ -13,9 +13,7 @@ import ( pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go" ) -type PulumiServiceTeamAccessTokenResource struct { - client *pulumiapi.Client -} +type PulumiServiceTeamAccessTokenResource struct{} type PulumiServiceTeamAccessTokenInput struct { Name string @@ -59,12 +57,11 @@ func (t *PulumiServiceTeamAccessTokenResource) Name() string { return "pulumiservice:index:TeamAccessToken" } -func (t *PulumiServiceTeamAccessTokenResource) Diff(req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) { +func (t *PulumiServiceTeamAccessTokenResource) Diff(_ context.Context, req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) { return diffAccessTokenProperties(req, []string{"name", "organizationName", "teamName", "description"}) } -func (t *PulumiServiceTeamAccessTokenResource) Delete(req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) { - ctx := context.Background() +func (t *PulumiServiceTeamAccessTokenResource) Delete(ctx context.Context, req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) { err := t.deleteTeamAccessToken(ctx, req.Id) if err != nil { @@ -74,8 +71,7 @@ func (t *PulumiServiceTeamAccessTokenResource) Delete(req *pulumirpc.DeleteReque return &pbempty.Empty{}, nil } -func (t *PulumiServiceTeamAccessTokenResource) Create(req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) { - ctx := context.Background() +func (t *PulumiServiceTeamAccessTokenResource) Create(ctx context.Context, req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) { inputs, err := plugin.UnmarshalProperties(req.GetProperties(), plugin.MarshalOptions{KeepUnknowns: true, SkipNulls: true}) if err != nil { return nil, err @@ -109,23 +105,22 @@ func (t *PulumiServiceTeamAccessTokenResource) Create(req *pulumirpc.CreateReque } -func (t *PulumiServiceTeamAccessTokenResource) Check(req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) { +func (t *PulumiServiceTeamAccessTokenResource) Check(_ context.Context, req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) { return &pulumirpc.CheckResponse{Inputs: req.News, Failures: nil}, nil } -func (t *PulumiServiceTeamAccessTokenResource) Update(_ *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) { +func (t *PulumiServiceTeamAccessTokenResource) Update(context.Context, *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) { // all updates are destructive, so we just call Create. return nil, fmt.Errorf("unexpected call to update, expected create to be called instead") } -func (t *PulumiServiceTeamAccessTokenResource) Read(req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) { - ctx := context.Background() +func (t *PulumiServiceTeamAccessTokenResource) Read(ctx context.Context, req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) { urn := req.GetId() orgName, teamName, _, tokenId, err := splitTeamAccessTokenId(urn) // the team access token is immutable; if we get nil it got deleted, otherwise all data is the same - accessToken, err := t.client.GetTeamAccessToken(ctx, tokenId, orgName, teamName) + accessToken, err := GetClient[*pulumiapi.Client](ctx).GetTeamAccessToken(ctx, tokenId, orgName, teamName) if err != nil { return nil, err } @@ -143,12 +138,9 @@ func (t *PulumiServiceTeamAccessTokenResource) Invoke(_ *pulumiserviceProvider, return &pulumirpc.InvokeResponse{Return: nil}, fmt.Errorf("unknown function '%s'", req.Tok) } -func (t *PulumiServiceTeamAccessTokenResource) Configure(_ PulumiServiceConfig) { -} - func (t *PulumiServiceTeamAccessTokenResource) createTeamAccessToken(ctx context.Context, input PulumiServiceTeamAccessTokenInput) (*pulumiapi.AccessToken, error) { - accessToken, err := t.client.CreateTeamAccessToken(ctx, input.Name, input.OrgName, input.TeamName, input.Description) + accessToken, err := GetClient[*pulumiapi.Client](ctx).CreateTeamAccessToken(ctx, input.Name, input.OrgName, input.TeamName, input.Description) if err != nil { return nil, err } @@ -161,7 +153,7 @@ func (t *PulumiServiceTeamAccessTokenResource) deleteTeamAccessToken(ctx context if err != nil { return err } - return t.client.DeleteTeamAccessToken(ctx, tokenId, orgName, teamName) + return GetClient[*pulumiapi.Client](ctx).DeleteTeamAccessToken(ctx, tokenId, orgName, teamName) } diff --git a/provider/pkg/provider/team_stack_perm.go b/provider/pkg/provider/team_stack_perm.go index 6de07c65..71e9c4bf 100644 --- a/provider/pkg/provider/team_stack_perm.go +++ b/provider/pkg/provider/team_stack_perm.go @@ -14,9 +14,7 @@ import ( pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go" ) -type TeamStackPermissionResource struct { - client *pulumiapi.Client -} +type TeamStackPermissionResource struct{} type TeamStackPermissionInput struct { Organization string `pulumi:"organization"` @@ -39,18 +37,11 @@ func (tp *TeamStackPermissionResource) Name() string { return "pulumiservice:index:TeamStackPermission" } -func (tp *TeamStackPermissionResource) Check(req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) { - return &pulumirpc.CheckResponse{ - Inputs: req.GetNews(), - }, nil -} - -func (tp *TeamStackPermissionResource) Configure(_ PulumiServiceConfig) { - +func (tp *TeamStackPermissionResource) Check(_ context.Context, req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) { + return &pulumirpc.CheckResponse{Inputs: req.GetNews()}, nil } -func (tp *TeamStackPermissionResource) Read(req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) { - ctx := context.Background() +func (tp *TeamStackPermissionResource) Read(ctx context.Context, req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) { id := req.GetId() permId, err := splitTeamStackPermissionId(id) @@ -65,7 +56,7 @@ func (tp *TeamStackPermissionResource) Read(req *pulumirpc.ReadRequest) (*pulumi return nil, err } - permission, err := tp.client.GetTeamStackPermission(ctx, pulumiapi.StackName{ + permission, err := GetClient[*pulumiapi.Client](ctx).GetTeamStackPermission(ctx, pulumiapi.StackName{ OrgName: permId.Organization, ProjectName: permId.Project, StackName: permId.Stack, @@ -96,8 +87,7 @@ func (tp *TeamStackPermissionResource) Read(req *pulumirpc.ReadRequest) (*pulumi }, nil } -func (tp *TeamStackPermissionResource) Create(req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) { - ctx := context.Background() +func (tp *TeamStackPermissionResource) Create(ctx context.Context, req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) { var inputs TeamStackPermissionInput err := serde.FromProperties(req.GetProperties(), structTagKey, &inputs) if err != nil { @@ -109,7 +99,7 @@ func (tp *TeamStackPermissionResource) Create(req *pulumirpc.CreateRequest) (*pu StackName: inputs.Stack, } - err = tp.client.AddStackPermission(ctx, stackName, inputs.Team, inputs.Permission) + err = GetClient[*pulumiapi.Client](ctx).AddStackPermission(ctx, stackName, inputs.Team, inputs.Permission) if err != nil { return nil, err } @@ -122,8 +112,7 @@ func (tp *TeamStackPermissionResource) Create(req *pulumirpc.CreateRequest) (*pu }, nil } -func (tp *TeamStackPermissionResource) Delete(req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) { - ctx := context.Background() +func (tp *TeamStackPermissionResource) Delete(ctx context.Context, req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) { var inputs TeamStackPermissionInput err := serde.FromProperties(req.GetProperties(), structTagKey, &inputs) if err != nil { @@ -134,14 +123,14 @@ func (tp *TeamStackPermissionResource) Delete(req *pulumirpc.DeleteRequest) (*pb ProjectName: inputs.Project, StackName: inputs.Stack, } - err = tp.client.RemoveStackPermission(ctx, stackName, inputs.Team) + err = GetClient[*pulumiapi.Client](ctx).RemoveStackPermission(ctx, stackName, inputs.Team) if err != nil { return nil, err } return &pbempty.Empty{}, nil } -func (tp *TeamStackPermissionResource) Diff(req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) { +func (tp *TeamStackPermissionResource) Diff(_ context.Context, req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) { changedKeys, err := serde.DiffOldsAndNews(req) if err != nil { return nil, err @@ -157,7 +146,7 @@ func (tp *TeamStackPermissionResource) Diff(req *pulumirpc.DiffRequest) (*pulumi } // Update does nothing because we always replace on changes, never an update -func (tp *TeamStackPermissionResource) Update(_ *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) { +func (tp *TeamStackPermissionResource) Update(context.Context, *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) { return nil, fmt.Errorf("unexpected call to update, expected create to be called instead") } diff --git a/provider/pkg/provider/team_test.go b/provider/pkg/provider/team_test.go index d03d0e49..2f99d6da 100644 --- a/provider/pkg/provider/team_test.go +++ b/provider/pkg/provider/team_test.go @@ -54,16 +54,14 @@ func TestTeam(t *testing.T) { func() (*pulumiapi.Team, error) { return nil, nil }, ) - provider := PulumiServiceTeamResource{ - client: mockedClient, - } + provider := PulumiServiceTeamResource{} req := pulumirpc.ReadRequest{ Id: "abc/123", Urn: "urn:123", } - resp, err := provider.Read(&req) + resp, err := provider.Read(WithClient(mockedClient), &req) assert.NoError(t, err) assert.Equal(t, resp.Id, "") @@ -86,16 +84,14 @@ func TestTeam(t *testing.T) { }, ) - provider := PulumiServiceTeamResource{ - client: mockedClient, - } + provider := PulumiServiceTeamResource{} req := pulumirpc.ReadRequest{ Id: "abc/123", Urn: "urn:123", } - resp, err := provider.Read(&req) + resp, err := provider.Read(WithClient(mockedClient), &req) assert.NoError(t, err) assert.Equal(t, resp.Id, "abc/123") diff --git a/provider/pkg/provider/ttl_schedules.go b/provider/pkg/provider/ttl_schedules.go index 9f95405b..21f8b67a 100644 --- a/provider/pkg/provider/ttl_schedules.go +++ b/provider/pkg/provider/ttl_schedules.go @@ -14,8 +14,10 @@ import ( "google.golang.org/protobuf/types/known/structpb" ) -type PulumiServiceTtlScheduleResource struct { - client pulumiapi.ScheduleClient +type PulumiServiceTtlScheduleResource struct{} + +func (st PulumiServiceTtlScheduleResource) client(ctx context.Context) pulumiapi.ScheduleClient { + return GetClient[pulumiapi.ScheduleClient](ctx) } type PulumiServiceTtlScheduleInput struct { @@ -66,15 +68,15 @@ func ToPulumiServiceTtlScheduleInput(properties *structpb.Struct) (*PulumiServic return &input, nil } -func (st *PulumiServiceTtlScheduleResource) Diff(req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) { +func (st *PulumiServiceTtlScheduleResource) Diff(_ context.Context, req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) { return ScheduleSharedDiff(req) } -func (st *PulumiServiceTtlScheduleResource) Delete(req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) { - return ScheduleSharedDelete(req, st.client) +func (st *PulumiServiceTtlScheduleResource) Delete(ctx context.Context, req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) { + return ScheduleSharedDelete(req, st.client(ctx)) } -func (st *PulumiServiceTtlScheduleResource) Create(req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) { +func (st *PulumiServiceTtlScheduleResource) Create(ctx context.Context, req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) { input, err := ToPulumiServiceTtlScheduleInput(req.GetProperties()) if err != nil { return nil, err @@ -84,7 +86,7 @@ func (st *PulumiServiceTtlScheduleResource) Create(req *pulumirpc.CreateRequest) Timestamp: input.Timestamp, DeleteAfterDestroy: input.DeleteAfterDestroy, } - scheduleID, err := st.client.CreateTtlSchedule(context.Background(), input.Stack, scheduleReq) + scheduleID, err := st.client(ctx).CreateTtlSchedule(ctx, input.Stack, scheduleReq) if err != nil { return nil, err } @@ -106,7 +108,7 @@ func (st *PulumiServiceTtlScheduleResource) Create(req *pulumirpc.CreateRequest) }, nil } -func (st *PulumiServiceTtlScheduleResource) Check(req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) { +func (st *PulumiServiceTtlScheduleResource) Check(_ context.Context, req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) { inputMap, err := plugin.UnmarshalProperties(req.GetNews(), plugin.MarshalOptions{KeepUnknowns: true, SkipNulls: true}) if err != nil { return nil, err @@ -132,7 +134,7 @@ func (st *PulumiServiceTtlScheduleResource) Check(req *pulumirpc.CheckRequest) ( return &pulumirpc.CheckResponse{Inputs: req.GetNews(), Failures: failures}, nil } -func (st *PulumiServiceTtlScheduleResource) Update(req *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) { +func (st *PulumiServiceTtlScheduleResource) Update(ctx context.Context, req *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) { previousOutput, err := ToPulumiServiceSharedScheduleOutput(req.GetOlds()) if err != nil { return nil, err @@ -146,7 +148,7 @@ func (st *PulumiServiceTtlScheduleResource) Update(req *pulumirpc.UpdateRequest) Timestamp: input.Timestamp, DeleteAfterDestroy: input.DeleteAfterDestroy, } - scheduleID, err := st.client.UpdateTtlSchedule(context.Background(), input.Stack, updateReq, previousOutput.ScheduleID) + scheduleID, err := st.client(ctx).UpdateTtlSchedule(ctx, input.Stack, updateReq, previousOutput.ScheduleID) if err != nil { return nil, err } @@ -166,7 +168,7 @@ func (st *PulumiServiceTtlScheduleResource) Update(req *pulumirpc.UpdateRequest) }, nil } -func (st *PulumiServiceTtlScheduleResource) Read(req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) { +func (st *PulumiServiceTtlScheduleResource) Read(ctx context.Context, req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) { output, err := ToPulumiServiceSharedScheduleOutput(req.GetProperties()) if err != nil { return nil, err @@ -176,7 +178,7 @@ func (st *PulumiServiceTtlScheduleResource) Read(req *pulumirpc.ReadRequest) (*p return nil, err } - scheduleID, err := st.client.GetSchedule(context.Background(), output.Stack, output.ScheduleID) + scheduleID, err := st.client(ctx).GetSchedule(ctx, output.Stack, output.ScheduleID) if err != nil { return nil, fmt.Errorf("failed to read TtlSchedule (%q): %w", req.Id, err) } @@ -206,6 +208,3 @@ func (st *PulumiServiceTtlScheduleResource) Read(req *pulumirpc.ReadRequest) (*p func (st *PulumiServiceTtlScheduleResource) Name() string { return "pulumiservice:index:TtlSchedule" } - -func (st *PulumiServiceTtlScheduleResource) Configure(_ PulumiServiceConfig) { -} diff --git a/provider/pkg/provider/unknown.go b/provider/pkg/provider/unknown.go index ed4e5f6e..5a48beab 100644 --- a/provider/pkg/provider/unknown.go +++ b/provider/pkg/provider/unknown.go @@ -1,6 +1,7 @@ package provider import ( + "context" "fmt" pbempty "google.golang.org/protobuf/types/known/emptypb" @@ -15,30 +16,27 @@ func (u *PulumiServiceUnknownResource) Name() string { return "pulumiservice:index:Unknown" } -func (u *PulumiServiceUnknownResource) Configure(config PulumiServiceConfig) { -} - -func (u *PulumiServiceUnknownResource) Diff(req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) { +func (u *PulumiServiceUnknownResource) Diff(_ context.Context, req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) { return nil, createUnknownResourceErrorFromRequest(req) } -func (u *PulumiServiceUnknownResource) Delete(req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) { +func (u *PulumiServiceUnknownResource) Delete(_ context.Context, req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) { return nil, createUnknownResourceErrorFromRequest(req) } -func (u *PulumiServiceUnknownResource) Create(req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) { +func (u *PulumiServiceUnknownResource) Create(_ context.Context, req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) { return nil, createUnknownResourceErrorFromRequest(req) } -func (u *PulumiServiceUnknownResource) Check(req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) { +func (u *PulumiServiceUnknownResource) Check(_ context.Context, req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) { return nil, createUnknownResourceErrorFromRequest(req) } -func (u *PulumiServiceUnknownResource) Update(req *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) { +func (u *PulumiServiceUnknownResource) Update(_ context.Context, req *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) { return nil, createUnknownResourceErrorFromRequest(req) } -func (u *PulumiServiceUnknownResource) Read(req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) { +func (u *PulumiServiceUnknownResource) Read(_ context.Context, req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) { return nil, createUnknownResourceErrorFromRequest(req) } @@ -47,13 +45,10 @@ func createUnknownResourceErrorFromRequest(req ResourceBase) error { return fmt.Errorf("unknown resource type '%s'", rn) } -func (u *PulumiServiceUnknownResource) Invoke(s *pulumiserviceProvider, req *pulumirpc.InvokeRequest) (*pulumirpc.InvokeResponse, error) { +func (u *PulumiServiceUnknownResource) Invoke(_ context.Context, s *pulumiserviceProvider, req *pulumirpc.InvokeRequest) (*pulumirpc.InvokeResponse, error) { return &pulumirpc.InvokeResponse{Return: nil}, fmt.Errorf("unknown function '%s'", req.Tok) } func (f *PulumiServiceUnknownFunction) Name() string { return "pulumiservice:index:Unknown" } - -func (f *PulumiServiceUnknownFunction) Configure(config PulumiServiceConfig) { -} diff --git a/provider/pkg/provider/webhook.go b/provider/pkg/provider/webhook.go index 678a0965..ba6bf38d 100644 --- a/provider/pkg/provider/webhook.go +++ b/provider/pkg/provider/webhook.go @@ -13,10 +13,7 @@ import ( pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go" ) -type PulumiServiceWebhookResource struct { - config PulumiServiceConfig - client pulumiapi.WebhookClient -} +type PulumiServiceWebhookResource struct{} type PulumiServiceWebhookInput struct { Active bool @@ -126,11 +123,7 @@ func (wh *PulumiServiceWebhookResource) Name() string { return "pulumiservice:index:Webhook" } -func (wh *PulumiServiceWebhookResource) Configure(config PulumiServiceConfig) { - wh.config = config -} - -func (wh *PulumiServiceWebhookResource) Check(req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) { +func (wh *PulumiServiceWebhookResource) Check(_ context.Context, req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) { news, err := plugin.UnmarshalProperties(req.GetNews(), plugin.MarshalOptions{KeepUnknowns: true, SkipNulls: true}) if err != nil { return nil, err @@ -179,7 +172,7 @@ func (wh *PulumiServiceWebhookResource) Check(req *pulumirpc.CheckRequest) (*pul return &pulumirpc.CheckResponse{Inputs: inputNews, Failures: failures}, nil } -func (wh *PulumiServiceWebhookResource) Create(req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) { +func (wh *PulumiServiceWebhookResource) Create(ctx context.Context, req *pulumirpc.CreateRequest) (*pulumirpc.CreateResponse, error) { inputs, err := plugin.UnmarshalProperties(req.GetProperties(), plugin.MarshalOptions{KeepUnknowns: true, SkipNulls: true}) if err != nil { return nil, err @@ -187,7 +180,7 @@ func (wh *PulumiServiceWebhookResource) Create(req *pulumirpc.CreateRequest) (*p props := wh.ToPulumiServiceWebhookProperties(inputs) - idString, err := wh.createWebhook(props.PulumiServiceWebhookInput) + idString, err := wh.createWebhook(ctx, props.PulumiServiceWebhookInput) if err != nil { return nil, err } @@ -216,8 +209,7 @@ func (wh *PulumiServiceWebhookResource) Create(req *pulumirpc.CreateRequest) (*p }, nil } -func (wh *PulumiServiceWebhookResource) createWebhook(input PulumiServiceWebhookInput) (*string, error) { - ctx := context.Background() +func (wh *PulumiServiceWebhookResource) createWebhook(ctx context.Context, input PulumiServiceWebhookInput) (*string, error) { req := pulumiapi.WebhookRequest{ OrganizationName: input.OrganizationName, ProjectName: input.ProjectName, @@ -229,7 +221,7 @@ func (wh *PulumiServiceWebhookResource) createWebhook(input PulumiServiceWebhook Format: input.Format, Filters: input.Filters, } - webhook, err := wh.client.CreateWebhook(ctx, req) + webhook, err := GetClient[pulumiapi.WebhookClient](ctx).CreateWebhook(ctx, req) if err != nil { return nil, err } @@ -245,7 +237,7 @@ func (wh *PulumiServiceWebhookResource) createWebhook(input PulumiServiceWebhook return &hookID, nil } -func (wh *PulumiServiceWebhookResource) Diff(req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) { +func (wh *PulumiServiceWebhookResource) Diff(_ context.Context, req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) { olds, err := plugin.UnmarshalProperties(req.GetOlds(), plugin.MarshalOptions{KeepUnknowns: false, SkipNulls: true}) if err != nil { return nil, err @@ -306,7 +298,7 @@ func (wh *PulumiServiceWebhookResource) Diff(req *pulumirpc.DiffRequest) (*pulum }, nil } -func (wh *PulumiServiceWebhookResource) Update(req *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) { +func (wh *PulumiServiceWebhookResource) Update(ctx context.Context, req *pulumirpc.UpdateRequest) (*pulumirpc.UpdateResponse, error) { // we only care about news because we validated that everything was correctly set in Check() & Diff() inputsNew, err := plugin.UnmarshalProperties(req.GetNews(), plugin.MarshalOptions{KeepUnknowns: true, SkipNulls: true}) if err != nil { @@ -336,7 +328,7 @@ func (wh *PulumiServiceWebhookResource) Update(req *pulumirpc.UpdateRequest) (*p }, Name: webhookNew.Name, } - err = wh.client.UpdateWebhook(context.Background(), updateReq) + err = GetClient[pulumiapi.WebhookClient](ctx).UpdateWebhook(ctx, updateReq) if err != nil { return nil, err } @@ -356,22 +348,21 @@ func (wh *PulumiServiceWebhookResource) Update(req *pulumirpc.UpdateRequest) (*p } -func (wh *PulumiServiceWebhookResource) Delete(req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) { - err := wh.deleteWebhook(req.Id) - return &pbempty.Empty{}, err +func (wh *PulumiServiceWebhookResource) Delete(ctx context.Context, req *pulumirpc.DeleteRequest) (*pbempty.Empty, error) { + return &pbempty.Empty{}, wh.deleteWebhook(ctx, req.Id) } -func (wh *PulumiServiceWebhookResource) deleteWebhook(id string) error { +func (wh *PulumiServiceWebhookResource) deleteWebhook(ctx context.Context, id string) error { hookID, err := splitWebhookID(id) if err != nil { return err } - return wh.client.DeleteWebhook(context.Background(), hookID.organizationName, + return GetClient[pulumiapi.WebhookClient](ctx).DeleteWebhook(ctx, hookID.organizationName, hookID.projectName, hookID.stackName, hookID.webhookName) } -func (wh *PulumiServiceWebhookResource) Read(req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) { - webhook, err := wh.getWebhook(req.Id) +func (wh *PulumiServiceWebhookResource) Read(ctx context.Context, req *pulumirpc.ReadRequest) (*pulumirpc.ReadResponse, error) { + webhook, err := wh.getWebhook(ctx, req.Id) if err != nil { return nil, err } @@ -423,12 +414,12 @@ func (wh *PulumiServiceWebhookResource) Read(req *pulumirpc.ReadRequest) (*pulum }, nil } -func (wh *PulumiServiceWebhookResource) getWebhook(id string) (*pulumiapi.Webhook, error) { +func (wh *PulumiServiceWebhookResource) getWebhook(ctx context.Context, id string) (*pulumiapi.Webhook, error) { hookID, err := splitWebhookID(id) if err != nil { return nil, err } - webhook, err := wh.client.GetWebhook(context.Background(), + webhook, err := GetClient[pulumiapi.WebhookClient](ctx).GetWebhook(ctx, hookID.organizationName, hookID.projectName, hookID.stackName, hookID.webhookName) if err != nil { return nil, err diff --git a/provider/pkg/provider/webhook_test.go b/provider/pkg/provider/webhook_test.go index 2923724e..c8cd7e79 100644 --- a/provider/pkg/provider/webhook_test.go +++ b/provider/pkg/provider/webhook_test.go @@ -47,16 +47,14 @@ func TestWebhook(t *testing.T) { func() (*pulumiapi.Webhook, error) { return nil, nil }, ) - provider := PulumiServiceWebhookResource{ - client: mockedClient, - } + provider := PulumiServiceWebhookResource{} req := pulumirpc.ReadRequest{ Id: "abc/def/ghi/123", Urn: "urn:123", } - resp, err := provider.Read(&req) + resp, err := provider.Read(WithClient(mockedClient), &req) assert.NoError(t, err) assert.Equal(t, resp.Id, "") @@ -75,16 +73,14 @@ func TestWebhook(t *testing.T) { }, ) - provider := PulumiServiceWebhookResource{ - client: mockedClient, - } + provider := PulumiServiceWebhookResource{} req := pulumirpc.ReadRequest{ Id: "abc/def/ghi/123", Urn: "urn:123", } - resp, err := provider.Read(&req) + resp, err := provider.Read(WithClient(mockedClient), &req) assert.NoError(t, err) assert.Equal(t, resp.Id, "abc/def/ghi/123") diff --git a/sdk/dotnet/Config/Config.cs b/sdk/dotnet/Config/Config.cs index d7d36976..28fc0292 100644 --- a/sdk/dotnet/Config/Config.cs +++ b/sdk/dotnet/Config/Config.cs @@ -32,12 +32,25 @@ public void Set(T value) private static readonly global::Pulumi.Config __config = new global::Pulumi.Config("pulumiservice"); - private static readonly __Value _accessToken = new __Value(() => __config.Get("accessToken")); + private static readonly __Value _accessToken = new __Value(() => __config.Get("accessToken") ?? Utilities.GetEnv("PULUMI_ACCESS_TOKEN")); + /// + /// Access Token to authenticate with Pulumi Cloud. + /// public static string? AccessToken { get => _accessToken.Get(); set => _accessToken.Set(value); } + private static readonly __Value _serviceURL = new __Value(() => __config.Get("serviceURL") ?? Utilities.GetEnv("PULUMI_BACKEND_URL") ?? "https://api.pulumi.com"); + /// + /// The service URL used to reach Pulumi Cloud. + /// + public static string? ServiceURL + { + get => _serviceURL.Get(); + set => _serviceURL.Set(value); + } + } } diff --git a/sdk/dotnet/Provider.cs b/sdk/dotnet/Provider.cs index 9a7a465c..6a4c9d99 100644 --- a/sdk/dotnet/Provider.cs +++ b/sdk/dotnet/Provider.cs @@ -12,6 +12,19 @@ namespace Pulumi.PulumiService [PulumiServiceResourceType("pulumi:providers:pulumiservice")] public partial class Provider : global::Pulumi.ProviderResource { + /// + /// Access Token to authenticate with Pulumi Cloud. + /// + [Output("accessToken")] + public Output AccessToken { get; private set; } = null!; + + /// + /// The service URL used to reach Pulumi Cloud. + /// + [Output("serviceURL")] + public Output ServiceURL { get; private set; } = null!; + + /// /// Create a Provider resource with the given unique name, arguments, and options. /// @@ -29,6 +42,10 @@ private static CustomResourceOptions MakeResourceOptions(CustomResourceOptions? var defaultOptions = new CustomResourceOptions { Version = Utilities.Version, + AdditionalSecretOutputs = + { + "accessToken", + }, }; var merged = CustomResourceOptions.Merge(defaultOptions, options); // Override the ID if one was specified for consistency with other language SDKs. @@ -39,15 +56,32 @@ private static CustomResourceOptions MakeResourceOptions(CustomResourceOptions? public sealed class ProviderArgs : global::Pulumi.ResourceArgs { + [Input("accessToken")] + private Input? _accessToken; + /// /// Access Token to authenticate with Pulumi Cloud. /// - [Input("accessToken")] - public Input? AccessToken { get; set; } + public Input? AccessToken + { + get => _accessToken; + set + { + var emptySecret = Output.CreateSecret(0); + _accessToken = Output.Tuple?, int>(value, emptySecret).Apply(t => t.Item1); + } + } + + /// + /// The service URL used to reach Pulumi Cloud. + /// + [Input("serviceURL")] + public Input? ServiceURL { get; set; } public ProviderArgs() { - AccessToken = Utilities.GetEnv("PULUMI_ACCESS_TOKEN") ?? ""; + AccessToken = Utilities.GetEnv("PULUMI_ACCESS_TOKEN"); + ServiceURL = Utilities.GetEnv("PULUMI_BACKEND_URL") ?? "https://api.pulumi.com"; } public static new ProviderArgs Empty => new ProviderArgs(); } diff --git a/sdk/go/pulumiservice/config/config.go b/sdk/go/pulumiservice/config/config.go index 40f9a460..2b8cf2a7 100644 --- a/sdk/go/pulumiservice/config/config.go +++ b/sdk/go/pulumiservice/config/config.go @@ -11,6 +11,28 @@ import ( var _ = internal.GetEnvOrDefault +// Access Token to authenticate with Pulumi Cloud. func GetAccessToken(ctx *pulumi.Context) string { - return config.Get(ctx, "pulumiservice:accessToken") + v, err := config.Try(ctx, "pulumiservice:accessToken") + if err == nil { + return v + } + var value string + if d := internal.GetEnvOrDefault(nil, nil, "PULUMI_ACCESS_TOKEN"); d != nil { + value = d.(string) + } + return value +} + +// The service URL used to reach Pulumi Cloud. +func GetServiceURL(ctx *pulumi.Context) string { + v, err := config.Try(ctx, "pulumiservice:serviceURL") + if err == nil { + return v + } + var value string + if d := internal.GetEnvOrDefault("https://api.pulumi.com", nil, "PULUMI_BACKEND_URL"); d != nil { + value = d.(string) + } + return value } diff --git a/sdk/go/pulumiservice/provider.go b/sdk/go/pulumiservice/provider.go index fe38f084..16fa5b25 100644 --- a/sdk/go/pulumiservice/provider.go +++ b/sdk/go/pulumiservice/provider.go @@ -13,6 +13,11 @@ import ( type Provider struct { pulumi.ProviderResourceState + + // Access Token to authenticate with Pulumi Cloud. + AccessToken pulumi.StringPtrOutput `pulumi:"accessToken"` + // The service URL used to reach Pulumi Cloud. + ServiceURL pulumi.StringPtrOutput `pulumi:"serviceURL"` } // NewProvider registers a new resource with the given unique name, arguments, and options. @@ -23,10 +28,22 @@ func NewProvider(ctx *pulumi.Context, } if args.AccessToken == nil { - if d := internal.GetEnvOrDefault("", nil, "PULUMI_ACCESS_TOKEN"); d != nil { + if d := internal.GetEnvOrDefault(nil, nil, "PULUMI_ACCESS_TOKEN"); d != nil { args.AccessToken = pulumi.StringPtr(d.(string)) } } + if args.ServiceURL == nil { + if d := internal.GetEnvOrDefault("https://api.pulumi.com", nil, "PULUMI_BACKEND_URL"); d != nil { + args.ServiceURL = pulumi.StringPtr(d.(string)) + } + } + if args.AccessToken != nil { + args.AccessToken = pulumi.ToSecret(args.AccessToken).(pulumi.StringPtrInput) + } + secrets := pulumi.AdditionalSecretOutputs([]string{ + "accessToken", + }) + opts = append(opts, secrets) opts = internal.PkgResourceDefaultOpts(opts) var resource Provider err := ctx.RegisterResource("pulumi:providers:pulumiservice", name, args, &resource, opts...) @@ -39,12 +56,16 @@ func NewProvider(ctx *pulumi.Context, type providerArgs struct { // Access Token to authenticate with Pulumi Cloud. AccessToken *string `pulumi:"accessToken"` + // The service URL used to reach Pulumi Cloud. + ServiceURL *string `pulumi:"serviceURL"` } // The set of arguments for constructing a Provider resource. type ProviderArgs struct { // Access Token to authenticate with Pulumi Cloud. AccessToken pulumi.StringPtrInput + // The service URL used to reach Pulumi Cloud. + ServiceURL pulumi.StringPtrInput } func (ProviderArgs) ElementType() reflect.Type { @@ -84,6 +105,16 @@ func (o ProviderOutput) ToProviderOutputWithContext(ctx context.Context) Provide return o } +// Access Token to authenticate with Pulumi Cloud. +func (o ProviderOutput) AccessToken() pulumi.StringPtrOutput { + return o.ApplyT(func(v *Provider) pulumi.StringPtrOutput { return v.AccessToken }).(pulumi.StringPtrOutput) +} + +// The service URL used to reach Pulumi Cloud. +func (o ProviderOutput) ServiceURL() pulumi.StringPtrOutput { + return o.ApplyT(func(v *Provider) pulumi.StringPtrOutput { return v.ServiceURL }).(pulumi.StringPtrOutput) +} + func init() { pulumi.RegisterInputType(reflect.TypeOf((*ProviderInput)(nil)).Elem(), &Provider{}) pulumi.RegisterOutputType(ProviderOutput{}) diff --git a/sdk/java/src/main/java/com/pulumi/pulumiservice/Config.java b/sdk/java/src/main/java/com/pulumi/pulumiservice/Config.java index 53260da1..2d9f73a2 100644 --- a/sdk/java/src/main/java/com/pulumi/pulumiservice/Config.java +++ b/sdk/java/src/main/java/com/pulumi/pulumiservice/Config.java @@ -10,7 +10,18 @@ public final class Config { private static final com.pulumi.Config config = com.pulumi.Config.of("pulumiservice"); +/** + * Access Token to authenticate with Pulumi Cloud. + * + */ public Optional accessToken() { - return Codegen.stringProp("accessToken").config(config).get(); + return Codegen.stringProp("accessToken").config(config).env("PULUMI_ACCESS_TOKEN").get(); + } +/** + * The service URL used to reach Pulumi Cloud. + * + */ + public Optional serviceURL() { + return Codegen.stringProp("serviceURL").config(config).env("PULUMI_BACKEND_URL").def("https://api.pulumi.com").get(); } } diff --git a/sdk/java/src/main/java/com/pulumi/pulumiservice/Provider.java b/sdk/java/src/main/java/com/pulumi/pulumiservice/Provider.java index 56af1bf1..08113f16 100644 --- a/sdk/java/src/main/java/com/pulumi/pulumiservice/Provider.java +++ b/sdk/java/src/main/java/com/pulumi/pulumiservice/Provider.java @@ -4,14 +4,47 @@ package com.pulumi.pulumiservice; import com.pulumi.core.Output; +import com.pulumi.core.annotations.Export; import com.pulumi.core.annotations.ResourceType; import com.pulumi.core.internal.Codegen; import com.pulumi.pulumiservice.ProviderArgs; import com.pulumi.pulumiservice.Utilities; +import java.lang.String; +import java.util.List; +import java.util.Optional; import javax.annotation.Nullable; @ResourceType(type="pulumi:providers:pulumiservice") public class Provider extends com.pulumi.resources.ProviderResource { + /** + * Access Token to authenticate with Pulumi Cloud. + * + */ + @Export(name="accessToken", refs={String.class}, tree="[0]") + private Output accessToken; + + /** + * @return Access Token to authenticate with Pulumi Cloud. + * + */ + public Output> accessToken() { + return Codegen.optional(this.accessToken); + } + /** + * The service URL used to reach Pulumi Cloud. + * + */ + @Export(name="serviceURL", refs={String.class}, tree="[0]") + private Output serviceURL; + + /** + * @return The service URL used to reach Pulumi Cloud. + * + */ + public Output> serviceURL() { + return Codegen.optional(this.serviceURL); + } + /** * * @param name The _unique_ name of the resulting resource. @@ -40,6 +73,9 @@ public Provider(String name, @Nullable ProviderArgs args, @Nullable com.pulumi.r private static com.pulumi.resources.CustomResourceOptions makeResourceOptions(@Nullable com.pulumi.resources.CustomResourceOptions options, @Nullable Output id) { var defaultOptions = com.pulumi.resources.CustomResourceOptions.builder() .version(Utilities.getVersion()) + .additionalSecretOutputs(List.of( + "accessToken" + )) .build(); return com.pulumi.resources.CustomResourceOptions.merge(defaultOptions, options, id); } diff --git a/sdk/java/src/main/java/com/pulumi/pulumiservice/ProviderArgs.java b/sdk/java/src/main/java/com/pulumi/pulumiservice/ProviderArgs.java index 90edb549..28b7b707 100644 --- a/sdk/java/src/main/java/com/pulumi/pulumiservice/ProviderArgs.java +++ b/sdk/java/src/main/java/com/pulumi/pulumiservice/ProviderArgs.java @@ -31,10 +31,26 @@ public Optional> accessToken() { return Optional.ofNullable(this.accessToken); } + /** + * The service URL used to reach Pulumi Cloud. + * + */ + @Import(name="serviceURL") + private @Nullable Output serviceURL; + + /** + * @return The service URL used to reach Pulumi Cloud. + * + */ + public Optional> serviceURL() { + return Optional.ofNullable(this.serviceURL); + } + private ProviderArgs() {} private ProviderArgs(ProviderArgs $) { this.accessToken = $.accessToken; + this.serviceURL = $.serviceURL; } public static Builder builder() { @@ -76,8 +92,30 @@ public Builder accessToken(String accessToken) { return accessToken(Output.of(accessToken)); } + /** + * @param serviceURL The service URL used to reach Pulumi Cloud. + * + * @return builder + * + */ + public Builder serviceURL(@Nullable Output serviceURL) { + $.serviceURL = serviceURL; + return this; + } + + /** + * @param serviceURL The service URL used to reach Pulumi Cloud. + * + * @return builder + * + */ + public Builder serviceURL(String serviceURL) { + return serviceURL(Output.of(serviceURL)); + } + public ProviderArgs build() { - $.accessToken = Codegen.stringProp("accessToken").output().arg($.accessToken).env("PULUMI_ACCESS_TOKEN").def("").getNullable(); + $.accessToken = Codegen.stringProp("accessToken").secret().arg($.accessToken).env("PULUMI_ACCESS_TOKEN").getNullable(); + $.serviceURL = Codegen.stringProp("serviceURL").output().arg($.serviceURL).env("PULUMI_BACKEND_URL").def("https://api.pulumi.com").getNullable(); return $; } } diff --git a/sdk/nodejs/config/vars.ts b/sdk/nodejs/config/vars.ts index 3e69beef..159e6ea0 100644 --- a/sdk/nodejs/config/vars.ts +++ b/sdk/nodejs/config/vars.ts @@ -7,10 +7,24 @@ import * as utilities from "../utilities"; declare var exports: any; const __config = new pulumi.Config("pulumiservice"); +/** + * Access Token to authenticate with Pulumi Cloud. + */ export declare const accessToken: string | undefined; Object.defineProperty(exports, "accessToken", { get() { - return __config.get("accessToken"); + return __config.get("accessToken") ?? utilities.getEnv("PULUMI_ACCESS_TOKEN"); + }, + enumerable: true, +}); + +/** + * The service URL used to reach Pulumi Cloud. + */ +export declare const serviceURL: string; +Object.defineProperty(exports, "serviceURL", { + get() { + return __config.get("serviceURL") ?? (utilities.getEnv("PULUMI_BACKEND_URL") || "https://api.pulumi.com"); }, enumerable: true, }); diff --git a/sdk/nodejs/provider.ts b/sdk/nodejs/provider.ts index 0e97ef2f..dab07e2a 100644 --- a/sdk/nodejs/provider.ts +++ b/sdk/nodejs/provider.ts @@ -19,6 +19,14 @@ export class Provider extends pulumi.ProviderResource { return obj['__pulumiType'] === "pulumi:providers:" + Provider.__pulumiType; } + /** + * Access Token to authenticate with Pulumi Cloud. + */ + public readonly accessToken!: pulumi.Output; + /** + * The service URL used to reach Pulumi Cloud. + */ + public readonly serviceURL!: pulumi.Output; /** * Create a Provider resource with the given unique name, arguments, and options. @@ -31,9 +39,12 @@ export class Provider extends pulumi.ProviderResource { let resourceInputs: pulumi.Inputs = {}; opts = opts || {}; { - resourceInputs["accessToken"] = (args ? args.accessToken : undefined) ?? (utilities.getEnv("PULUMI_ACCESS_TOKEN") || ""); + resourceInputs["accessToken"] = (args?.accessToken ? pulumi.secret(args.accessToken) : undefined) ?? utilities.getEnv("PULUMI_ACCESS_TOKEN"); + resourceInputs["serviceURL"] = (args ? args.serviceURL : undefined) ?? (utilities.getEnv("PULUMI_BACKEND_URL") || "https://api.pulumi.com"); } opts = pulumi.mergeOptions(utilities.resourceOptsDefaults(), opts); + const secretOpts = { additionalSecretOutputs: ["accessToken"] }; + opts = pulumi.mergeOptions(opts, secretOpts); super(Provider.__pulumiType, name, resourceInputs, opts); } } @@ -46,4 +57,8 @@ export interface ProviderArgs { * Access Token to authenticate with Pulumi Cloud. */ accessToken?: pulumi.Input; + /** + * The service URL used to reach Pulumi Cloud. + */ + serviceURL?: pulumi.Input; } diff --git a/sdk/python/pulumi_pulumiservice/config/__init__.pyi b/sdk/python/pulumi_pulumiservice/config/__init__.pyi index 35cce8d5..03333f84 100644 --- a/sdk/python/pulumi_pulumiservice/config/__init__.pyi +++ b/sdk/python/pulumi_pulumiservice/config/__init__.pyi @@ -10,4 +10,12 @@ from typing import Any, Mapping, Optional, Sequence, Union, overload from .. import _utilities accessToken: Optional[str] +""" +Access Token to authenticate with Pulumi Cloud. +""" + +serviceURL: str +""" +The service URL used to reach Pulumi Cloud. +""" diff --git a/sdk/python/pulumi_pulumiservice/config/vars.py b/sdk/python/pulumi_pulumiservice/config/vars.py index 31e88160..0ea0ff6b 100644 --- a/sdk/python/pulumi_pulumiservice/config/vars.py +++ b/sdk/python/pulumi_pulumiservice/config/vars.py @@ -17,5 +17,15 @@ class _ExportableConfig(types.ModuleType): @property def access_token(self) -> Optional[str]: - return __config__.get('accessToken') + """ + Access Token to authenticate with Pulumi Cloud. + """ + return __config__.get('accessToken') or _utilities.get_env('PULUMI_ACCESS_TOKEN') + + @property + def service_url(self) -> str: + """ + The service URL used to reach Pulumi Cloud. + """ + return __config__.get('serviceURL') or (_utilities.get_env('PULUMI_BACKEND_URL') or 'https://api.pulumi.com') diff --git a/sdk/python/pulumi_pulumiservice/provider.py b/sdk/python/pulumi_pulumiservice/provider.py index f451142e..2df6a0f5 100644 --- a/sdk/python/pulumi_pulumiservice/provider.py +++ b/sdk/python/pulumi_pulumiservice/provider.py @@ -14,15 +14,21 @@ @pulumi.input_type class ProviderArgs: def __init__(__self__, *, - access_token: Optional[pulumi.Input[str]] = None): + access_token: Optional[pulumi.Input[str]] = None, + service_url: Optional[pulumi.Input[str]] = None): """ The set of arguments for constructing a Provider resource. :param pulumi.Input[str] access_token: Access Token to authenticate with Pulumi Cloud. + :param pulumi.Input[str] service_url: The service URL used to reach Pulumi Cloud. """ if access_token is None: - access_token = (_utilities.get_env('PULUMI_ACCESS_TOKEN') or '') + access_token = _utilities.get_env('PULUMI_ACCESS_TOKEN') if access_token is not None: pulumi.set(__self__, "access_token", access_token) + if service_url is None: + service_url = (_utilities.get_env('PULUMI_BACKEND_URL') or 'https://api.pulumi.com') + if service_url is not None: + pulumi.set(__self__, "service_url", service_url) @property @pulumi.getter(name="accessToken") @@ -36,6 +42,18 @@ def access_token(self) -> Optional[pulumi.Input[str]]: def access_token(self, value: Optional[pulumi.Input[str]]): pulumi.set(self, "access_token", value) + @property + @pulumi.getter(name="serviceURL") + def service_url(self) -> Optional[pulumi.Input[str]]: + """ + The service URL used to reach Pulumi Cloud. + """ + return pulumi.get(self, "service_url") + + @service_url.setter + def service_url(self, value: Optional[pulumi.Input[str]]): + pulumi.set(self, "service_url", value) + class Provider(pulumi.ProviderResource): @overload @@ -43,12 +61,14 @@ def __init__(__self__, resource_name: str, opts: Optional[pulumi.ResourceOptions] = None, access_token: Optional[pulumi.Input[str]] = None, + service_url: Optional[pulumi.Input[str]] = None, __props__=None): """ Create a Pulumiservice resource with the given unique name, props, and options. :param str resource_name: The name of the resource. :param pulumi.ResourceOptions opts: Options for the resource. :param pulumi.Input[str] access_token: Access Token to authenticate with Pulumi Cloud. + :param pulumi.Input[str] service_url: The service URL used to reach Pulumi Cloud. """ ... @overload @@ -74,6 +94,7 @@ def _internal_init(__self__, resource_name: str, opts: Optional[pulumi.ResourceOptions] = None, access_token: Optional[pulumi.Input[str]] = None, + service_url: Optional[pulumi.Input[str]] = None, __props__=None): opts = pulumi.ResourceOptions.merge(_utilities.get_resource_opts_defaults(), opts) if not isinstance(opts, pulumi.ResourceOptions): @@ -84,11 +105,32 @@ def _internal_init(__self__, __props__ = ProviderArgs.__new__(ProviderArgs) if access_token is None: - access_token = (_utilities.get_env('PULUMI_ACCESS_TOKEN') or '') - __props__.__dict__["access_token"] = access_token + access_token = _utilities.get_env('PULUMI_ACCESS_TOKEN') + __props__.__dict__["access_token"] = None if access_token is None else pulumi.Output.secret(access_token) + if service_url is None: + service_url = (_utilities.get_env('PULUMI_BACKEND_URL') or 'https://api.pulumi.com') + __props__.__dict__["service_url"] = service_url + secret_opts = pulumi.ResourceOptions(additional_secret_outputs=["accessToken"]) + opts = pulumi.ResourceOptions.merge(opts, secret_opts) super(Provider, __self__).__init__( 'pulumiservice', resource_name, __props__, opts) + @property + @pulumi.getter(name="accessToken") + def access_token(self) -> pulumi.Output[Optional[str]]: + """ + Access Token to authenticate with Pulumi Cloud. + """ + return pulumi.get(self, "access_token") + + @property + @pulumi.getter(name="serviceURL") + def service_url(self) -> pulumi.Output[Optional[str]]: + """ + The service URL used to reach Pulumi Cloud. + """ + return pulumi.get(self, "service_url") +