Files
pulumi-incus/provider/resources.go
Brandon Kalinowski 908889d9af improvements
cleans package.json, update binary download URL, etc
2025-04-25 19:22:29 -04:00

359 lines
15 KiB
Go

// Copyright 2016-2024, Pulumi Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package incus
import (
"context"
"fmt"
"path"
"strings"
// Allow embedding bridge-metadata.json in the provider.
_ "embed"
incus "github.com/lxc/terraform-provider-incus/shim" // Import the upstream provider
pfbridge "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/pf/tfbridge"
"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge"
"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge/info"
"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge/tokens"
"github.com/pulumi/pulumi/pkg/v3/codegen/schema"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
"git.kalinow.ski/nimbus/pulumi-incus/provider/pkg/version"
)
// all of the token components used below.
const (
// This variable controls the default name of the package in the package
// registries for nodejs and python:
mainPkg = "incus"
// modules:
mainMod = "index" // the incus module
)
//go:embed cmd/pulumi-resource-incus/bridge-metadata.json
var metadata []byte
// Provider returns additional overlaid schema and metadata associated with the provider.
func Provider() tfbridge.ProviderInfo {
// Create a Pulumi provider mapping
prov := tfbridge.ProviderInfo{
// Instantiate the Terraform provider
//
// The [pulumi-terraform-bridge](https://github.com/pulumi/pulumi-terraform-bridge) supports 3
// types of Terraform providers:
//
// 1. Providers written with the terraform-plugin-sdk/v1:
//
// If the provider you are bridging is written with the terraform-plugin-sdk/v1, then you
// will need to adapt the boilerplate:
//
// - Change the import "shimv2" to "shimv1" and change the associated import to
// "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim/sdk-v1".
//
// You can then proceed as normal.
//
// 2. Providers written with terraform-plugin-sdk/v2:
//
// This boilerplate is already geared towards providers written with the
// terraform-plugin-sdk/v2, since it is the most common provider framework used. No
// adaptions are needed.
//
// 3. Providers written with terraform-plugin-framework:
//
// If the provider you are bridging is written with the terraform-plugin-framework, then
// you will need to adapt the boilerplate:
//
// - Remove the `shimv2` import and add:
//
// pfbridge "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/pf/tfbridge"
//
// - Replace `shimv2.NewProvider` with `pfbridge.ShimProvider`.
//
// - In provider/cmd/pulumi-tfgen-incus/main.go, replace the
// "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfgen" import with
// "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/pf/tfgen". Remove the `version.Version`
// argument to `tfgen.Main`.
//
// - In provider/cmd/pulumi-resource-incus/main.go, replace the
// "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge" import with
// "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/pf/tfbridge". Replace the arguments to the
// `tfbridge.Main` so it looks like this:
//
// tfbridge.Main(context.Background(), "incus", incus.Provider(),
// tfbridge.ProviderMetadata{PulumiSchema: pulumiSchema})
//
// Detailed instructions can be found at
// https://pulumi-developer-docs.readthedocs.io/projects/pulumi-terraform-bridge/en/latest/docs/guides/new-pf-provider.html
// After that, you can proceed as normal.
//
// This is where you give the bridge a handle to the upstream terraform provider. SDKv2
// convention is to have a function at "github.com/lxc/terraform-provider-incus/provider".New
// which takes a version and produces a factory function. The provider you are bridging may
// not do that. You will need to find the function (generally called in upstream's main.go)
// that produces a:
//
// - *"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema".Provider (for SDKv2)
// - *"github.com/hashicorp/terraform-plugin-sdk/v1/helper/schema".Provider (for SDKv1)
// - "github.com/hashicorp/terraform-plugin-framework/provider".Provider (for plugin-framework)
//
//nolint:lll
P: pfbridge.ShimProvider(incus.Provider(version.Version)()),
Name: "incus",
Version: version.Version,
// DisplayName is a way to be able to change the casing of the provider name when being
// displayed on the Pulumi registry
DisplayName: "Incus",
// Change this to your personal name (or a company name) that you would like to be shown in
// the Pulumi Registry if this package is published there.
Publisher: "Kite.run",
// LogoURL is optional but useful to help identify your package in the Pulumi Registry
// if this package is published there.
//
// You may host a logo on a domain you control or add an PNG logo (100x100) for your package
// in your repository and use the raw content URL for that file as your logo URL.
LogoURL: "https://linuxcontainers.org/static/img/containers.png",
// PluginDownloadURL is an optional URL used to download the Provider
// for use in Pulumi programs
// e.g. https://github.com/org/pulumi-provider-name/releases/download/v${VERSION}/
PluginDownloadURL: "https://git.kalinow.ski/api/packages/kiterun/generic/pulumi-incus/${VERSION}/",
Description: "A Pulumi package for creating and managing incus cloud resources.",
// category/cloud tag helps with categorizing the package in the Pulumi Registry.
// For all available categories, see `Keywords` in
// https://www.pulumi.com/docs/guides/pulumi-packages/schema/#package.
Keywords: []string{"incus", "category/cloud", "containers", "nimbus"},
Homepage: "https://linuxcontainers.org",
Repository: "https://git.kalinow.ski/nimbus/pulumi-incus",
// The GitHub Org for the provider - defaults to `terraform-providers`. Note that this should
// match the TF provider module's require directive, not any replace directives.
GitHubOrg: "lxc",
// Temporarily comment out the MetadataInfo
MetadataInfo: tfbridge.NewProviderMetadata(metadata),
Config: map[string]*tfbridge.SchemaInfo{
// Add any required configuration here, or remove the example below if
// no additional points are required.
// "region": {
// Type: "incus:region/region:Region",
// },
},
// If extra types are needed for configuration, they can be added here.
ExtraTypes: map[string]schema.ComplexTypeSpec{
// "incus:region/region:Region": {
// ObjectTypeSpec: schema.ObjectTypeSpec{
// Type: "string",
// },
// Enum: []schema.EnumValueSpec{
// {Name: "here", Value: "HERE"},
// {Name: "overThere", Value: "OVER_THERE"},
// },
// },
},
JavaScript: &tfbridge.JavaScriptInfo{
// RespectSchemaVersion ensures the SDK is generated linking to the correct version of the provider.
RespectSchemaVersion: true,
PackageName: "@kiterun/incus",
TypeScriptVersion: "^5.8.3",
UseTypeOnlyReferences: true,
},
Python: &tfbridge.PythonInfo{
// RespectSchemaVersion ensures the SDK is generated linking to the correct version of the provider.
RespectSchemaVersion: true,
// Enable modern PyProject support in the generated Python SDK.
PyProject: struct{ Enabled bool }{true},
},
Golang: &tfbridge.GolangInfo{
// Set where the SDK is going to be published to.
ImportBasePath: path.Join(
"git.kalinow.ski/nimbus/pulumi-incus/sdk/",
tfbridge.GetModuleMajorVersion(version.Version),
"go",
mainPkg,
),
// Opt in to all available code generation features.
GenerateResourceContainerTypes: true,
GenerateExtraInputTypes: true,
// RespectSchemaVersion ensures the SDK is generated linking to the correct version of the provider.
RespectSchemaVersion: true,
},
CSharp: &tfbridge.CSharpInfo{
// RespectSchemaVersion ensures the SDK is generated linking to the correct version of the provider.
RespectSchemaVersion: true,
// Use a wildcard import so NuGet will prefer the latest possible version.
PackageReferences: map[string]string{
"Pulumi": "3.*",
},
},
}
// MustComputeTokens maps all resources and datasources from the upstream provider into Pulumi.
//
// tokens.SingleModule puts every upstream item into your provider's main module.
//
// You shouldn't need to override anything, but if you do, use the [tfbridge.ProviderInfo.Resources]
// and [tfbridge.ProviderInfo.DataSources].
prov.MustComputeTokens(tokens.SingleModule("incus_", mainMod, tokens.MakeStandard(mainPkg)))
// Add special ID handling for the incus_instance resource
// Docs here https://registry.terraform.io/providers/lxc/incus/latest/docs/resources/certificate
defaultProjectField := map[string]*info.Schema{
"project": {
Default: &info.Default{
Value: "default",
},
},
}
prov.Resources["incus_certificate"] = &tfbridge.ResourceInfo{
Tok: tfbridge.MakeResource(mainPkg, mainMod, "Certificate"),
ComputeID: tfbridge.DelegateIDField("name", prov.Name, prov.Repository),
}
prov.Resources["incus_cluster_group"] = &tfbridge.ResourceInfo{
Tok: tfbridge.MakeResource(mainPkg, mainMod, "ClusterGroup"),
ComputeID: tfbridge.DelegateIDField("name", prov.Name, prov.Repository),
}
prov.Resources["incus_image"] = &tfbridge.ResourceInfo{
Tok: tfbridge.MakeResource(mainPkg, mainMod, "Image"),
// Does this work if source image is used?
ComputeID: delegateIDFields([]resource.PropertyKey{"project", "fingerprint"}, prov.Name, prov.Repository),
// DREAM: default project
// Fields: defaultProjectField,
}
prov.Resources["incus_instance"] = &tfbridge.ResourceInfo{
Tok: tfbridge.MakeResource(mainPkg, mainMod, "Instance"),
ComputeID: delegateIDFields([]resource.PropertyKey{"project", "name"}, prov.Name, prov.Repository),
Fields: defaultProjectField,
DeleteBeforeReplace: true,
}
prov.Resources["incus_instance_snapshot"] = &tfbridge.ResourceInfo{
Tok: tfbridge.MakeResource(mainPkg, mainMod, "InstanceSnapshot"),
ComputeID: delegateIDFields([]resource.PropertyKey{"project", "name"}, prov.Name, prov.Repository),
Fields: defaultProjectField,
}
prov.Resources["incus_network"] = &tfbridge.ResourceInfo{
Tok: tfbridge.MakeResource(mainPkg, mainMod, "Network"),
ComputeID: delegateIDFields([]resource.PropertyKey{"project", "name"}, prov.Name, prov.Repository),
Fields: defaultProjectField,
}
prov.Resources["incus_network_acl"] = &tfbridge.ResourceInfo{
Tok: tfbridge.MakeResource(mainPkg, mainMod, "NetworkAcl"),
ComputeID: delegateIDFields([]resource.PropertyKey{"project", "name"}, prov.Name, prov.Repository),
Fields: defaultProjectField,
}
prov.Resources["incus_network_forward"] = &tfbridge.ResourceInfo{
Tok: tfbridge.MakeResource(mainPkg, mainMod, "NetworkForward"),
ComputeID: delegateIDFields([]resource.PropertyKey{"project", "network", "listen_address"}, prov.Name, prov.Repository),
// DREAM: project
// Fields: defaultProjectField,
}
prov.Resources["incus_network_integration"] = &tfbridge.ResourceInfo{
Tok: tfbridge.MakeResource(mainPkg, mainMod, "NetworkIntegration"),
ComputeID: delegateIDFields([]resource.PropertyKey{"project", "name", "type"}, prov.Name, prov.Repository),
Fields: defaultProjectField,
}
prov.Resources["incus_network_lb"] = &tfbridge.ResourceInfo{
Tok: tfbridge.MakeResource(mainPkg, mainMod, "NetworkLoadBalancer"),
ComputeID: delegateIDFields([]resource.PropertyKey{"project", "network", "listen_address"}, prov.Name, prov.Repository),
// Fields: defaultProjectField,
}
prov.Resources["incus_network_peer"] = &tfbridge.ResourceInfo{
Tok: tfbridge.MakeResource(mainPkg, mainMod, "NetworkPeer"),
ComputeID: delegateIDFields([]resource.PropertyKey{"project", "name", "network"}, prov.Name, prov.Repository),
Fields: defaultProjectField,
}
prov.Resources["incus_network_zone"] = &tfbridge.ResourceInfo{
Tok: tfbridge.MakeResource(mainPkg, mainMod, "NetworkZone"),
ComputeID: delegateIDFields([]resource.PropertyKey{"project", "name"}, prov.Name, prov.Repository),
Fields: defaultProjectField,
}
prov.Resources["incus_network_zone_record"] = &tfbridge.ResourceInfo{
Tok: tfbridge.MakeResource(mainPkg, mainMod, "NetworkZoneRecord"),
ComputeID: delegateIDFields([]resource.PropertyKey{"project", "zone", "name"}, prov.Name, prov.Repository),
Fields: defaultProjectField,
}
prov.Resources["incus_profile"] = &tfbridge.ResourceInfo{
Tok: tfbridge.MakeResource(mainPkg, mainMod, "Profile"),
ComputeID: delegateIDFields([]resource.PropertyKey{"project", "name"}, prov.Name, prov.Repository),
Fields: defaultProjectField,
}
prov.Resources["incus_project"] = &tfbridge.ResourceInfo{
Tok: tfbridge.MakeResource(mainPkg, mainMod, "Project"),
ComputeID: tfbridge.DelegateIDField("name", prov.Name, prov.Repository),
}
prov.Resources["incus_storage_bucket"] = &tfbridge.ResourceInfo{
Tok: tfbridge.MakeResource(mainPkg, mainMod, "StorageBucket"),
ComputeID: delegateIDFields([]resource.PropertyKey{"project", "pool", "name"}, prov.Name, prov.Repository),
Fields: defaultProjectField,
}
prov.Resources["incus_storage_bucket_key"] = &tfbridge.ResourceInfo{
Tok: tfbridge.MakeResource(mainPkg, mainMod, "StorageBucketKey"),
ComputeID: delegateIDFields([]resource.PropertyKey{"project", "pool", "name"}, prov.Name, prov.Repository),
Fields: defaultProjectField,
}
prov.Resources["incus_storage_pool"] = &tfbridge.ResourceInfo{
Tok: tfbridge.MakeResource(mainPkg, mainMod, "StoragePool"),
ComputeID: delegateIDFields([]resource.PropertyKey{"project", "name"}, prov.Name, prov.Repository),
Fields: defaultProjectField,
}
prov.Resources["incus_storage_volume"] = &tfbridge.ResourceInfo{
Tok: tfbridge.MakeResource(mainPkg, mainMod, "StorageVolume"),
ComputeID: delegateIDFields([]resource.PropertyKey{"project", "pool", "name"}, prov.Name, prov.Repository),
Fields: defaultProjectField,
}
prov.MustApplyAutoAliases()
prov.SetAutonaming(255, "-")
return prov
}
func delegateIDFields(fields []resource.PropertyKey, providerName, repoURL string) tfbridge.ComputeID {
return func(ctx context.Context, state resource.PropertyMap) (resource.ID, error) {
if len(fields) == 0 {
return "", fmt.Errorf("no fields provided for ID computation")
}
var idParts []string
for _, field := range fields {
id, err := tfbridge.DelegateIDProperty(resource.PropertyPath{string(field)}, providerName, repoURL)(ctx, state)
if err != nil {
return "", err
}
idParts = append(idParts, string(id))
}
return resource.ID(strings.Join(idParts, "/")), nil
}
}