/
cai.go
179 lines (152 loc) · 5.89 KB
/
cai.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
package google
import (
"fmt"
"math/rand"
"regexp"
)
type ConvertFunc func(d TerraformResourceData, config *Config) ([]Asset, error)
type GetApiObjectFunc func(d TerraformResourceData, config *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 TerraformResourceData, config *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 TerraformResourceData, config *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 := 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)
}