tfplan2cai/converters/google/resources/cai/cai.go (130 lines of code) (raw):
package cai
import (
"fmt"
"math/rand"
"regexp"
"github.com/hashicorp/terraform-provider-google-beta/google-beta/tpgresource"
transport_tpg "github.com/hashicorp/terraform-provider-google-beta/google-beta/transport"
)
type ConvertFunc func(d tpgresource.TerraformResourceData, config *transport_tpg.Config) ([]Asset, error)
type GetApiObjectFunc func(d tpgresource.TerraformResourceData, config *transport_tpg.Config) (map[string]interface{}, error)
// FetchFullResourceFunc allows initial data for a resource to be fetched from the API and merged
// with the planned changes. This is useful for resources that are only partially managed
// by Terraform, like IAM policies managed with member/binding resources.
type FetchFullResourceFunc func(d tpgresource.TerraformResourceData, config *transport_tpg.Config) (Asset, error)
// MergeFunc combines multiple terraform resources into a single CAI asset.
// The incoming asset will either be an asset that was created/updated or deleted.
type MergeFunc func(existing, incoming Asset) Asset
type ResourceConverter struct {
AssetType string
Convert ConvertFunc
FetchFullResource FetchFullResourceFunc
MergeCreateUpdate MergeFunc
MergeDelete MergeFunc
}
// Asset is the CAI representation of a resource.
type Asset struct {
// The name, in a peculiar format: `\\<api>.googleapis.com/<self_link>`
Name string `json:"name"`
// The type name in `google.<api>.<resourcename>` format.
Type string `json:"asset_type"`
Resource *AssetResource `json:"resource,omitempty"`
IAMPolicy *IAMPolicy `json:"iam_policy,omitempty"`
OrgPolicy []*OrgPolicy `json:"org_policy,omitempty"`
V2OrgPolicies []*V2OrgPolicies `json:"v2_org_policies,omitempty"`
}
// AssetResource is the Asset's Resource field.
type AssetResource struct {
// Api version
Version string `json:"version"`
// URI including scheme for the discovery doc - assembled from
// product name and version.
DiscoveryDocumentURI string `json:"discovery_document_uri"`
// Resource name.
DiscoveryName string `json:"discovery_name"`
// Actual resource state as per Terraform. Note that this does
// not necessarily correspond perfectly with the CAI representation
// as there are occasional deviations between CAI and API responses.
// This returns the API response values instead.
Data map[string]interface{} `json:"data,omitempty"`
}
type Folder struct {
Name string `json:"name,omitempty"`
Parent string `json:"parent,omitempty"`
DisplayName string `json:"display_name,omitempty"`
State string `json:"state,omitempty"`
CreateTime *Timestamp `json:"create_time,omitempty"`
}
type IAMPolicy struct {
Bindings []IAMBinding `json:"bindings"`
}
type IAMBinding struct {
Role string `json:"role"`
Members []string `json:"members"`
}
type OrgPolicy struct {
Constraint string `json:"constraint,omitempty"`
ListPolicy *ListPolicy `json:"listPolicy"`
BooleanPolicy *BooleanPolicy `json:"booleanPolicy"`
RestoreDefault *RestoreDefault `json:"restoreDefault"`
UpdateTime *Timestamp `json:"update_time,omitempty"`
}
// V2OrgPolicies is the represtation of V2OrgPolicies
type V2OrgPolicies struct {
Name string `json:"name"`
PolicySpec *PolicySpec `json:"spec,omitempty"`
}
// Spec is the representation of Spec for V2OrgPolicy
type PolicySpec struct {
Etag string `json:"etag,omitempty"`
UpdateTime *Timestamp `json:"update_time,omitempty"`
PolicyRules []*PolicyRule `json:"rules,omitempty"`
InheritFromParent bool `json:"inherit_from_parent,omitempty"`
Reset bool `json:"reset,omitempty"`
}
type PolicyRule struct {
Values *StringValues `json:"values,omitempty"`
AllowAll bool `json:"allow_all,omitempty"`
DenyAll bool `json:"deny_all,omitempty"`
Enforce bool `json:"enforce,omitempty"`
Condition *Expr `json:"condition,omitempty"`
}
type StringValues struct {
AllowedValues []string `json:"allowed_values,omitempty"`
DeniedValues []string `json:"denied_values,omitempty"`
}
type Expr struct {
Expression string `json:"expression,omitempty"`
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
Location string `json:"location,omitempty"`
}
type Timestamp struct {
Seconds int64 `json:"seconds,omitempty"`
Nanos int64 `json:"nanos,omitempty"`
}
type ListPolicyAllValues int32
type ListPolicy struct {
AllowedValues []string `json:"allowed_values,omitempty"`
DeniedValues []string `json:"denied_values,omitempty"`
AllValues ListPolicyAllValues `json:"all_values,omitempty"`
SuggestedValue string `json:"suggested_value,omitempty"`
InheritFromParent bool `json:"inherit_from_parent,omitempty"`
}
type BooleanPolicy struct {
Enforced bool `json:"enforced,omitempty"`
}
type RestoreDefault struct {
}
// AssetName templates an asset.name by looking up and replacing all instances
// of {{field}}. In the case where a field would resolve to an empty string, a
// generated unique string will be used: "placeholder-" + randomString().
// This is done to preserve uniqueness of asset.name for a given asset.asset_type.
func AssetName(d tpgresource.TerraformResourceData, config *transport_tpg.Config, linkTmpl string) (string, error) {
re := regexp.MustCompile("{{([%[:word:]]+)}}")
// workaround for empty project
placeholderSet := false
if config.Project == "" {
config.Project = fmt.Sprintf("placeholder-%s", RandString(8))
placeholderSet = true
}
f, err := tpgresource.BuildReplacementFunc(re, d, config, linkTmpl, false)
if err != nil {
return "", err
}
if placeholderSet {
config.Project = ""
}
fWithPlaceholder := func(key string) string {
val := f(key)
if val == "" {
val = fmt.Sprintf("placeholder-%s", RandString(8))
}
return val
}
return re.ReplaceAllStringFunc(linkTmpl, fWithPlaceholder), nil
}
func RandString(n int) string {
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
}
return string(b)
}