Skip to content

Commit 59bd402

Browse files
authored
tgc-revival: google_project tfplan2cai converter (#13291)
1 parent aefd55b commit 59bd402

File tree

3 files changed

+284
-0
lines changed

3 files changed

+284
-0
lines changed

mmv1/templates/tgc_next/tfplan2cai/resource_converters.go.tmpl

+2
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ package converters
2929

3030
import (
3131
"github.com/GoogleCloudPlatform/terraform-google-conversion/v6/pkg/tfplan2cai/converters/cai"
32+
"github.com/GoogleCloudPlatform/terraform-google-conversion/v6/pkg/tfplan2cai/converters/services/resourcemanager"
3233
)
3334

3435
var ConverterMap = map[string]cai.ResourceConverter{
36+
"google_project": resourcemanager.ResourceConverterProject(),
3537
}
+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package caiasset
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
"time"
7+
)
8+
9+
// Asset is the CAI representation of a resource.
10+
type Asset struct {
11+
// The name, in a peculiar format: `\\<api>.googleapis.com/<self_link>`
12+
Name string `json:"name"`
13+
// The type name in `google.<api>.<resourcename>` format.
14+
Type string `json:"assetType"`
15+
Resource *AssetResource `json:"resource,omitempty"`
16+
IAMPolicy *IAMPolicy `json:"iamPolicy,omitempty"`
17+
OrgPolicy []*OrgPolicy `json:"orgPolicy,omitempty"`
18+
V2OrgPolicies []*V2OrgPolicies `json:"v2_org_policies,omitempty"`
19+
Ancestors []string `json:"ancestors"`
20+
TfplanAddress []string `json:"tfplanAddress,omitempty"`
21+
}
22+
23+
// IAMPolicy is the representation of a Cloud IAM policy set on a cloud resource.
24+
type IAMPolicy struct {
25+
Bindings []IAMBinding `json:"bindings"`
26+
}
27+
28+
// IAMBinding binds a role to a set of members.
29+
type IAMBinding struct {
30+
Role string `json:"role"`
31+
Members []string `json:"members"`
32+
}
33+
34+
// AssetResource is nested within the Asset type.
35+
type AssetResource struct {
36+
Version string `json:"version"`
37+
DiscoveryDocumentURI string `json:"discoveryDocumentUri"`
38+
DiscoveryName string `json:"discoveryName"`
39+
Parent string `json:"parent"`
40+
Data map[string]interface{} `json:"data"`
41+
Location string `json:"location,omitempty"`
42+
}
43+
44+
// OrgPolicy is for managing organization policies.
45+
type OrgPolicy struct {
46+
Constraint string `json:"constraint,omitempty"`
47+
ListPolicy *ListPolicy `json:"list_policy,omitempty"`
48+
BooleanPolicy *BooleanPolicy `json:"boolean_policy,omitempty"`
49+
RestoreDefault *RestoreDefault `json:"restore_default,omitempty"`
50+
UpdateTime *Timestamp `json:"update_time,omitempty"`
51+
}
52+
53+
// V2OrgPolicies is the represtation of V2OrgPolicies
54+
type V2OrgPolicies struct {
55+
Name string `json:"name"`
56+
PolicySpec *PolicySpec `json:"spec,omitempty"`
57+
}
58+
59+
// Spec is the representation of Spec for Custom Org Policy
60+
type PolicySpec struct {
61+
Etag string `json:"etag,omitempty"`
62+
UpdateTime *Timestamp `json:"update_time,omitempty"`
63+
PolicyRules []*PolicyRule `json:"rules,omitempty"`
64+
InheritFromParent bool `json:"inherit_from_parent,omitempty"`
65+
Reset bool `json:"reset,omitempty"`
66+
}
67+
68+
type PolicyRule struct {
69+
Values *StringValues `json:"values,omitempty"`
70+
AllowAll bool `json:"allow_all,omitempty"`
71+
DenyAll bool `json:"deny_all,omitempty"`
72+
Enforce bool `json:"enforce,omitempty"`
73+
Condition *Expr `json:"condition,omitempty"`
74+
}
75+
76+
type StringValues struct {
77+
AllowedValues []string `json:"allowed_values,omitempty"`
78+
DeniedValues []string `json:"denied_values,omitempty"`
79+
}
80+
81+
type Expr struct {
82+
Expression string `json:"expression,omitempty"`
83+
Title string `json:"title,omitempty"`
84+
Description string `json:"description,omitempty"`
85+
Location string `json:"location,omitempty"`
86+
}
87+
88+
type Timestamp struct {
89+
Seconds int64 `json:"seconds,omitempty"`
90+
Nanos int64 `json:"nanos,omitempty"`
91+
}
92+
93+
func (t Timestamp) MarshalJSON() ([]byte, error) {
94+
return []byte(`"` + time.Unix(0, t.Nanos).UTC().Format(time.RFC3339Nano) + `"`), nil
95+
}
96+
97+
func (t *Timestamp) UnmarshalJSON(b []byte) error {
98+
p, err := time.Parse(time.RFC3339Nano, strings.Trim(string(b), `"`))
99+
if err != nil {
100+
return fmt.Errorf("bad Timestamp: %v", err)
101+
}
102+
t.Seconds = p.Unix()
103+
t.Nanos = p.UnixNano()
104+
return nil
105+
}
106+
107+
// ListPolicyAllValues is used to set `Policies` that apply to all possible
108+
// configuration values rather than specific values in `allowed_values` or
109+
// `denied_values`.
110+
type ListPolicyAllValues int32
111+
112+
// ListPolicy can define specific values and subtrees of Cloud Resource
113+
// Manager resource hierarchy (`Organizations`, `Folders`, `Projects`) that
114+
// are allowed or denied by setting the `allowed_values` and `denied_values`
115+
// fields.
116+
type ListPolicy struct {
117+
AllowedValues []string `json:"allowed_values,omitempty"`
118+
DeniedValues []string `json:"denied_values,omitempty"`
119+
AllValues ListPolicyAllValues `json:"all_values,omitempty"`
120+
SuggestedValue string `json:"suggested_value,omitempty"`
121+
InheritFromParent bool `json:"inherit_from_parent,omitempty"`
122+
}
123+
124+
// BooleanPolicy If `true`, then the `Policy` is enforced. If `false`,
125+
// then any configuration is acceptable.
126+
type BooleanPolicy struct {
127+
Enforced bool `json:"enforced,omitempty"`
128+
}
129+
130+
// RestoreDefault determines if the default values of the `Constraints` are active for the
131+
// resources.
132+
type RestoreDefault struct {
133+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package resourcemanager
2+
3+
import (
4+
"fmt"
5+
"strconv"
6+
"strings"
7+
8+
"github.com/GoogleCloudPlatform/terraform-google-conversion/v6/pkg/caiasset"
9+
"github.com/GoogleCloudPlatform/terraform-google-conversion/v6/pkg/tfplan2cai/converters/cai"
10+
11+
"github.com/hashicorp/terraform-provider-google-beta/google-beta/tpgresource"
12+
transport_tpg "github.com/hashicorp/terraform-provider-google-beta/google-beta/transport"
13+
14+
"google.golang.org/api/cloudbilling/v1"
15+
"google.golang.org/api/cloudresourcemanager/v1"
16+
)
17+
18+
func ResourceConverterProject() cai.ResourceConverter {
19+
return cai.ResourceConverter{
20+
Convert: GetProjectAndBillingInfoCaiObjects,
21+
}
22+
}
23+
24+
func GetProjectAndBillingInfoCaiObjects(d tpgresource.TerraformResourceData, config *transport_tpg.Config) ([]caiasset.Asset, error) {
25+
if projectAsset, err := GetProjectCaiObject(d, config); err == nil {
26+
assets := []caiasset.Asset{projectAsset}
27+
if billingAsset, err := GetProjectBillingInfoCaiObject(d, config); err == nil {
28+
assets = append(assets, billingAsset)
29+
return assets, nil
30+
} else {
31+
return []caiasset.Asset{}, err
32+
}
33+
} else {
34+
return []caiasset.Asset{}, err
35+
}
36+
}
37+
38+
func GetProjectCaiObject(d tpgresource.TerraformResourceData, config *transport_tpg.Config) (caiasset.Asset, error) {
39+
linkTmpl := "//cloudresourcemanager.googleapis.com/projects/{{number}}"
40+
name, err := cai.AssetName(d, config, linkTmpl)
41+
if err != nil {
42+
return caiasset.Asset{}, err
43+
}
44+
if data, err := GetProjectData(d, config); err == nil {
45+
return caiasset.Asset{
46+
Name: name,
47+
Type: "cloudresourcemanager.googleapis.com/Project",
48+
Resource: &caiasset.AssetResource{
49+
Version: "v1",
50+
DiscoveryDocumentURI: "https://cloudresourcemanager.googleapis.com/$discovery/rest?version=v1",
51+
DiscoveryName: "Project",
52+
Data: data,
53+
},
54+
}, nil
55+
} else {
56+
return caiasset.Asset{}, err
57+
}
58+
}
59+
60+
func GetProjectData(d tpgresource.TerraformResourceData, config *transport_tpg.Config) (map[string]interface{}, error) {
61+
pid := d.Get("project_id").(string)
62+
63+
project := &cloudresourcemanager.Project{
64+
ProjectId: pid,
65+
Name: d.Get("name").(string),
66+
}
67+
68+
if res, ok := d.GetOk("number"); ok {
69+
num, err := strconv.ParseInt(res.(string), 10, 64)
70+
if err != nil {
71+
return nil, err
72+
}
73+
74+
project.ProjectNumber = num
75+
}
76+
77+
if err := getParentResourceId(d, project); err != nil {
78+
return nil, err
79+
}
80+
81+
if _, ok := d.GetOk("effective_labels"); ok {
82+
project.Labels = tpgresource.ExpandEffectiveLabels(d)
83+
}
84+
85+
return cai.JsonMap(project)
86+
}
87+
88+
func getParentResourceId(d tpgresource.TerraformResourceData, p *cloudresourcemanager.Project) error {
89+
orgId := d.Get("org_id").(string)
90+
folderId := d.Get("folder_id").(string)
91+
92+
if orgId != "" && folderId != "" {
93+
return fmt.Errorf("'org_id' and 'folder_id' cannot be both set.")
94+
}
95+
96+
if orgId != "" {
97+
p.Parent = &cloudresourcemanager.ResourceId{
98+
Id: orgId,
99+
Type: "organization",
100+
}
101+
}
102+
103+
if folderId != "" {
104+
p.Parent = &cloudresourcemanager.ResourceId{
105+
Id: strings.TrimPrefix(folderId, "folders/"),
106+
Type: "folder",
107+
}
108+
}
109+
110+
return nil
111+
}
112+
113+
func GetProjectBillingInfoCaiObject(d tpgresource.TerraformResourceData, config *transport_tpg.Config) (caiasset.Asset, error) {
114+
linkTmpl := "//cloudbilling.googleapis.com/projects/{{project_id_or_project}}/billingInfo"
115+
name, err := cai.AssetName(d, config, linkTmpl)
116+
if err != nil {
117+
return caiasset.Asset{}, err
118+
}
119+
project := strings.Split(name, "/")[4]
120+
if data, err := GetProjectBillingInfoData(d, project); err == nil {
121+
return caiasset.Asset{
122+
Name: name,
123+
Type: "cloudbilling.googleapis.com/ProjectBillingInfo",
124+
Resource: &caiasset.AssetResource{
125+
Version: "v1",
126+
DiscoveryDocumentURI: "https://cloudbilling.googleapis.com/$discovery/rest",
127+
DiscoveryName: "ProjectBillingInfo",
128+
Data: data,
129+
Location: "global",
130+
},
131+
}, nil
132+
} else {
133+
return caiasset.Asset{}, err
134+
}
135+
}
136+
137+
func GetProjectBillingInfoData(d tpgresource.TerraformResourceData, project string) (map[string]interface{}, error) {
138+
if _, ok := d.GetOk("billing_account"); !ok {
139+
return nil, cai.ErrNoConversion
140+
}
141+
142+
ba := &cloudbilling.ProjectBillingInfo{
143+
BillingAccountName: fmt.Sprintf("billingAccounts/%s", d.Get("billing_account")),
144+
Name: fmt.Sprintf("projects/%s/billingInfo", project),
145+
ProjectId: d.Get("project_id").(string),
146+
}
147+
148+
return cai.JsonMap(ba)
149+
}

0 commit comments

Comments
 (0)