Skip to content

Commit 5148a4a

Browse files
add image registry policy schema
Signed-off-by: Vasundhara Shukla <vasundharas@vmware.com>
1 parent 756b635 commit 5148a4a

10 files changed

+907
-0
lines changed

internal/provider/provider.go

+3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import (
1818
"github.com/vmware/terraform-provider-tanzu-mission-control/internal/resources/namespace"
1919
custompolicy "github.com/vmware/terraform-provider-tanzu-mission-control/internal/resources/policy/kind/custom"
2020
custompolicyresource "github.com/vmware/terraform-provider-tanzu-mission-control/internal/resources/policy/kind/custom/resource"
21+
imagepolicy "github.com/vmware/terraform-provider-tanzu-mission-control/internal/resources/policy/kind/image"
22+
imagepolicyresource "github.com/vmware/terraform-provider-tanzu-mission-control/internal/resources/policy/kind/image/resource"
2123
securitypolicy "github.com/vmware/terraform-provider-tanzu-mission-control/internal/resources/policy/kind/security"
2224
securitypolicyresource "github.com/vmware/terraform-provider-tanzu-mission-control/internal/resources/policy/kind/security/resource"
2325
"github.com/vmware/terraform-provider-tanzu-mission-control/internal/resources/workspace"
@@ -36,6 +38,7 @@ func Provider() *schema.Provider {
3638
iampolicy.ResourceName: iampolicy.ResourceIAMPolicy(),
3739
custompolicy.ResourceName: custompolicyresource.ResourceCustomPolicy(),
3840
securitypolicy.ResourceName: securitypolicyresource.ResourceSecurityPolicy(),
41+
imagepolicy.ResourceName: imagepolicyresource.ResourceImageRegistryPolicy(),
3942
credential.ResourceName: credential.ResourceCredential(),
4043
integration.ResourceName: integration.ResourceIntegration(),
4144
},
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
Copyright © 2022 VMware, Inc. All Rights Reserved.
3+
SPDX-License-Identifier: MPL-2.0
4+
*/
5+
6+
package policykindimage
7+
8+
import (
9+
"github.com/vmware/terraform-provider-tanzu-mission-control/internal/resources/policy"
10+
reciperesource "github.com/vmware/terraform-provider-tanzu-mission-control/internal/resources/policy/kind/image/recipe"
11+
)
12+
13+
const (
14+
ResourceName = "tanzu-mission-control_image_registry_policy"
15+
typeDefaultValue = "image-registry-policy"
16+
)
17+
18+
// Allowed input recipes.
19+
const (
20+
UnknownRecipe Recipe = policy.UnknownRecipe
21+
AllowedNameTagRecipe Recipe = reciperesource.AllowedNameTagKey
22+
CustomRecipe Recipe = reciperesource.CustomKey
23+
BlockLatestTagRecipe Recipe = reciperesource.BlockLatestTagKey
24+
RequireDigestRecipe Recipe = reciperesource.RequireDigestKey
25+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
/*
2+
Copyright © 2022 VMware, Inc. All Rights Reserved.
3+
SPDX-License-Identifier: MPL-2.0
4+
*/
5+
6+
package policykindimage
7+
8+
import (
9+
"context"
10+
"fmt"
11+
"strings"
12+
13+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
14+
15+
policyrecipeimagemodel "github.com/vmware/terraform-provider-tanzu-mission-control/internal/models/policy/recipe/image"
16+
"github.com/vmware/terraform-provider-tanzu-mission-control/internal/resources/policy"
17+
reciperesource "github.com/vmware/terraform-provider-tanzu-mission-control/internal/resources/policy/kind/image/recipe"
18+
)
19+
20+
var (
21+
inputSchema = &schema.Schema{
22+
Type: schema.TypeList,
23+
Description: "Input for the image registry policy, having one of the valid recipes: allowed-name-tag, custom, block-latest-tag or require-digest.",
24+
Required: true,
25+
MaxItems: 1,
26+
MinItems: 1,
27+
ForceNew: true,
28+
Elem: &schema.Resource{
29+
Schema: map[string]*schema.Schema{
30+
reciperesource.AllowedNameTagKey: reciperesource.AllowedNameTag,
31+
reciperesource.CustomKey: reciperesource.Custom,
32+
reciperesource.BlockLatestTagKey: reciperesource.BlockLatestTag,
33+
reciperesource.RequireDigestKey: reciperesource.RequireDigest,
34+
},
35+
},
36+
}
37+
RecipesAllowed = [...]string{reciperesource.AllowedNameTagKey, reciperesource.CustomKey, reciperesource.BlockLatestTagKey, reciperesource.RequireDigestKey}
38+
)
39+
40+
type (
41+
Recipe string
42+
// InputRecipe is a struct for all types of image registry policy inputs.
43+
inputRecipe struct {
44+
recipe Recipe
45+
inputAllowedNameTag *policyrecipeimagemodel.VmwareTanzuManageV1alpha1CommonPolicySpecImageV1AllowedNameTag
46+
inputCustom *policyrecipeimagemodel.VmwareTanzuManageV1alpha1CommonPolicySpecImageV1Custom
47+
inputBlockLatestTag *policyrecipeimagemodel.VmwareTanzuManageV1alpha1CommonPolicySpecImageV1CommonRecipe
48+
inputRequireDigest *policyrecipeimagemodel.VmwareTanzuManageV1alpha1CommonPolicySpecImageV1CommonRecipe
49+
}
50+
)
51+
52+
func constructInput(data []interface{}) (inputRecipeData *inputRecipe) {
53+
if len(data) == 0 || data[0] == nil {
54+
return inputRecipeData
55+
}
56+
57+
inputData, _ := data[0].(map[string]interface{})
58+
59+
if v, ok := inputData[reciperesource.AllowedNameTagKey]; ok {
60+
if v1, ok := v.([]interface{}); ok && len(v1) != 0 {
61+
inputRecipeData = &inputRecipe{
62+
recipe: AllowedNameTagRecipe,
63+
inputAllowedNameTag: reciperesource.ConstructAllowedNameTag(v1),
64+
}
65+
}
66+
}
67+
68+
if v, ok := inputData[reciperesource.CustomKey]; ok {
69+
if v1, ok := v.([]interface{}); ok && len(v1) != 0 {
70+
inputRecipeData = &inputRecipe{
71+
recipe: CustomRecipe,
72+
inputCustom: reciperesource.ConstructCustom(v1),
73+
}
74+
}
75+
}
76+
77+
if v, ok := inputData[reciperesource.BlockLatestTagKey]; ok {
78+
if v1, ok := v.([]interface{}); ok && len(v1) != 0 {
79+
inputRecipeData = &inputRecipe{
80+
recipe: BlockLatestTagRecipe,
81+
inputBlockLatestTag: reciperesource.ConstructCommonRecipe(v1),
82+
}
83+
}
84+
}
85+
86+
if v, ok := inputData[reciperesource.RequireDigestKey]; ok {
87+
if v1, ok := v.([]interface{}); ok && len(v1) != 0 {
88+
inputRecipeData = &inputRecipe{
89+
recipe: RequireDigestRecipe,
90+
inputRequireDigest: reciperesource.ConstructCommonRecipe(v1),
91+
}
92+
}
93+
}
94+
95+
return inputRecipeData
96+
}
97+
98+
func flattenInput(inputRecipeData *inputRecipe) (data []interface{}) {
99+
if inputRecipeData == nil {
100+
return data
101+
}
102+
103+
flattenInputData := make(map[string]interface{})
104+
105+
switch inputRecipeData.recipe {
106+
case AllowedNameTagRecipe:
107+
flattenInputData[reciperesource.AllowedNameTagKey] = reciperesource.FlattenAllowedNameTag(inputRecipeData.inputAllowedNameTag)
108+
case CustomRecipe:
109+
flattenInputData[reciperesource.CustomKey] = reciperesource.FlattenCustom(inputRecipeData.inputCustom)
110+
case BlockLatestTagRecipe:
111+
flattenInputData[reciperesource.BlockLatestTagKey] = reciperesource.FlattenCommonRecipe(inputRecipeData.inputBlockLatestTag)
112+
case RequireDigestRecipe:
113+
flattenInputData[reciperesource.RequireDigestKey] = reciperesource.FlattenCommonRecipe(inputRecipeData.inputRequireDigest)
114+
case UnknownRecipe:
115+
fmt.Printf("[ERROR]: No valid input recipe block found: minimum one valid input recipe block is required among: %v. Please check the schema.", strings.Join(RecipesAllowed[:], `, `))
116+
}
117+
118+
return []interface{}{flattenInputData}
119+
}
120+
121+
func ValidateInput(ctx context.Context, diff *schema.ResourceDiff, i interface{}) error {
122+
value, ok := diff.GetOk(policy.SpecKey)
123+
if !ok {
124+
return fmt.Errorf("spec: %v is not valid: minimum one valid spec block is required", value)
125+
}
126+
127+
data, _ := value.([]interface{})
128+
129+
if len(data) == 0 || data[0] == nil {
130+
return fmt.Errorf("spec data: %v is not valid: minimum one valid spec block is required among: %v", data, strings.Join(RecipesAllowed[:], `, `))
131+
}
132+
133+
specData := data[0].(map[string]interface{})
134+
135+
v, ok := specData[policy.InputKey]
136+
if !ok {
137+
return fmt.Errorf("input: %v is not valid: minimum one valid input block is required", v)
138+
}
139+
140+
v1, ok := v.([]interface{})
141+
if !ok {
142+
return fmt.Errorf("type of input block data: %v is not valid", v1)
143+
}
144+
145+
if len(v1) == 0 || v1[0] == nil {
146+
return fmt.Errorf("input data: %v is not valid: minimum one valid input block is required", v1)
147+
}
148+
149+
inputData, _ := v1[0].(map[string]interface{})
150+
recipesFound := make([]string, 0)
151+
152+
if v, ok := inputData[reciperesource.AllowedNameTagKey]; ok {
153+
if v1, ok := v.([]interface{}); ok && len(v1) != 0 {
154+
recipesFound = append(recipesFound, reciperesource.AllowedNameTagKey)
155+
}
156+
}
157+
158+
if v, ok := inputData[reciperesource.CustomKey]; ok {
159+
if v1, ok := v.([]interface{}); ok && len(v1) != 0 {
160+
recipesFound = append(recipesFound, reciperesource.CustomKey)
161+
}
162+
}
163+
164+
if v, ok := inputData[reciperesource.BlockLatestTagKey]; ok {
165+
if v1, ok := v.([]interface{}); ok && len(v1) != 0 {
166+
recipesFound = append(recipesFound, reciperesource.BlockLatestTagKey)
167+
}
168+
}
169+
170+
if v, ok := inputData[reciperesource.RequireDigestKey]; ok {
171+
if v1, ok := v.([]interface{}); ok && len(v1) != 0 {
172+
recipesFound = append(recipesFound, reciperesource.RequireDigestKey)
173+
}
174+
}
175+
176+
if len(recipesFound) == 0 {
177+
return fmt.Errorf("no valid input recipe block found: minimum one valid input recipe block is required among: %v", strings.Join(RecipesAllowed[:], `, `))
178+
} else if len(recipesFound) > 1 {
179+
return fmt.Errorf("found input recipes: %v are not valid: maximum one valid input recipe block is allowed", strings.Join(recipesFound, `, `))
180+
}
181+
182+
return nil
183+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
Copyright © 2022 VMware, Inc. All Rights Reserved.
3+
SPDX-License-Identifier: MPL-2.0
4+
*/
5+
6+
package recipe
7+
8+
import (
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
10+
11+
"github.com/vmware/terraform-provider-tanzu-mission-control/internal/helper"
12+
policyrecipeimagemodel "github.com/vmware/terraform-provider-tanzu-mission-control/internal/models/policy/recipe/image"
13+
)
14+
15+
var AllowedNameTag = &schema.Schema{
16+
Type: schema.TypeList,
17+
Description: "The input schema for image policy allowed-name-tag recipe version v1",
18+
Optional: true,
19+
ForceNew: true,
20+
MaxItems: 1,
21+
Elem: &schema.Resource{
22+
Schema: map[string]*schema.Schema{
23+
AuditKey: {
24+
Type: schema.TypeBool,
25+
Description: "Audit (dry-run). Violations will be logged but not denied.",
26+
Optional: true,
27+
Default: false,
28+
},
29+
RulesKey: {
30+
Type: schema.TypeList,
31+
Description: "It specifies a list of rules that defines allowed image patterns.",
32+
Required: true,
33+
MinItems: 1,
34+
Elem: &schema.Resource{
35+
Schema: map[string]*schema.Schema{
36+
ImageNameKey: {
37+
Type: schema.TypeString,
38+
Description: "Allowed image names, wildcards are supported(for example: fooservice/*). Empty field is equivalent to *.",
39+
Optional: true,
40+
},
41+
TagKey: tag,
42+
},
43+
},
44+
},
45+
},
46+
},
47+
}
48+
49+
func ConstructAllowedNameTag(data []interface{}) (nameTag *policyrecipeimagemodel.VmwareTanzuManageV1alpha1CommonPolicySpecImageV1AllowedNameTag) {
50+
if len(data) == 0 || data[0] == nil {
51+
return nameTag
52+
}
53+
54+
allowedNameTagData, _ := data[0].(map[string]interface{})
55+
56+
nameTag = &policyrecipeimagemodel.VmwareTanzuManageV1alpha1CommonPolicySpecImageV1AllowedNameTag{}
57+
58+
if v, ok := allowedNameTagData[AuditKey]; ok {
59+
helper.SetPrimitiveValue(v, &nameTag.Audit, AuditKey)
60+
}
61+
62+
if v, ok := allowedNameTagData[RulesKey]; ok {
63+
if vs, ok := v.([]interface{}); ok {
64+
if len(vs) != 0 && vs[0] != nil {
65+
nameTag.Rules = make([]*policyrecipeimagemodel.VmwareTanzuManageV1alpha1CommonPolicySpecImageV1AllowedNameTagRules, 0)
66+
67+
for _, raw := range vs {
68+
nameTag.Rules = append(nameTag.Rules, expandNameTagRules(raw))
69+
}
70+
}
71+
}
72+
}
73+
74+
return nameTag
75+
}
76+
77+
func expandNameTagRules(data interface{}) (rules *policyrecipeimagemodel.VmwareTanzuManageV1alpha1CommonPolicySpecImageV1AllowedNameTagRules) {
78+
if data == nil {
79+
return rules
80+
}
81+
82+
rulesData, ok := data.(map[string]interface{})
83+
if !ok {
84+
return rules
85+
}
86+
87+
rules = &policyrecipeimagemodel.VmwareTanzuManageV1alpha1CommonPolicySpecImageV1AllowedNameTagRules{}
88+
89+
if v, ok := rulesData[ImageNameKey]; ok {
90+
helper.SetPrimitiveValue(v, &rules.ImageName, ImageNameKey)
91+
}
92+
93+
if v, ok := rulesData[TagKey]; ok {
94+
if v1, ok := v.([]interface{}); ok {
95+
rules.Tag = expandTag(v1)
96+
}
97+
}
98+
99+
return rules
100+
}
101+
102+
func FlattenAllowedNameTag(nameTag *policyrecipeimagemodel.VmwareTanzuManageV1alpha1CommonPolicySpecImageV1AllowedNameTag) (data []interface{}) {
103+
if nameTag == nil {
104+
return data
105+
}
106+
107+
flattenAllowedNameTag := make(map[string]interface{})
108+
109+
flattenAllowedNameTag[AuditKey] = nameTag.Audit
110+
111+
if nameTag.Rules != nil {
112+
var rules []interface{}
113+
114+
for _, rule := range nameTag.Rules {
115+
rules = append(rules, flattenNameTagRules(rule))
116+
}
117+
118+
flattenAllowedNameTag[RulesKey] = rules
119+
}
120+
121+
return []interface{}{flattenAllowedNameTag}
122+
}
123+
124+
func flattenNameTagRules(rules *policyrecipeimagemodel.VmwareTanzuManageV1alpha1CommonPolicySpecImageV1AllowedNameTagRules) (data interface{}) {
125+
if rules == nil {
126+
return data
127+
}
128+
129+
flattenRules := make(map[string]interface{})
130+
131+
flattenRules[ImageNameKey] = rules.ImageName
132+
133+
if rules.Tag != nil {
134+
flattenRules[TagKey] = flattenTag(rules.Tag)
135+
}
136+
137+
return flattenRules
138+
}

0 commit comments

Comments
 (0)