Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 75 additions & 6 deletions castai/data_source_gke.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,38 @@ import (

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)

const (
// policiesResourceName is the name of the resource
policiesResourceName = "policy"
// featuresResourceName is the name of the policies per feature
featuresResourceName = "features"
loadBalancersNetworkEndpointGroupFeature = "load_balancers_network_endpoint_group"
loadBalancersTargetBackendPoolsFeature = "load_balancers_target_backend_pools"
loadBalancersUnmanagedInstanceGroupsFeature = "load_balancers_unmanaged_instance_groups"
)

func dataSourceGKEPolicies() *schema.Resource {
return &schema.Resource{
ReadContext: dataSourceGKEPoliciesRead,
Description: "Data source for retrieving GKE policies",
Schema: map[string]*schema.Schema{
"policy": {
featuresResourceName: {
Description: "Includes list of policies needed for the GCP features",
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validation.StringInSlice([]string{
loadBalancersNetworkEndpointGroupFeature,
loadBalancersTargetBackendPoolsFeature,
loadBalancersUnmanagedInstanceGroupsFeature,
}, false),
},
},
policiesResourceName: {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
Expand All @@ -23,12 +48,56 @@ func dataSourceGKEPolicies() *schema.Resource {
}
}

func dataSourceGKEPoliciesRead(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
policies, _ := gke.GetUserPolicy()
data.SetId("gke")
if err := data.Set("policy", policies); err != nil {
return diag.FromErr(fmt.Errorf("setting gke policy: %w", err))
func dataSourceGKEPoliciesRead(_ context.Context, data *schema.ResourceData, _ interface{}) diag.Diagnostics {
// add policies per specified features
features := data.Get(featuresResourceName).([]interface{})
policySet := make(map[string]struct{})

for _, feature := range features {
var err error
var policies []string

switch feature {
case loadBalancersNetworkEndpointGroupFeature:
policies, err = gke.GetLoadBalancersNetworkEndpointGroupPolicy()
case loadBalancersTargetBackendPoolsFeature:
policies, err = gke.GetLoadBalancersTargetBackendPoolsPolicy()
case loadBalancersUnmanagedInstanceGroupsFeature:
policies, err = gke.GetLoadBalancersUnmanagedInstanceGroupsPolicy()
}

if err != nil {
return diag.FromErr(fmt.Errorf("getting %s policy: %w", feature, err))
}

policySet = appendArrayToMap(policies, policySet)
}

// add base user policies
userPolicy, err := gke.GetUserPolicy()
if err != nil {
return diag.FromErr(fmt.Errorf("getting user policy: %w", err))
}
policySet = appendArrayToMap(userPolicy, policySet)

var allPolicies []string
for policy := range policySet {
allPolicies = append(allPolicies, policy)
}

if err := data.Set(policiesResourceName, allPolicies); err != nil {
return diag.FromErr(fmt.Errorf("setting %s policy: %w", policiesResourceName, err))
}

return nil
}

func appendArrayToMap(arr []string, m map[string]struct{}) map[string]struct{} {
if m == nil {
m = make(map[string]struct{})
}
for _, v := range arr {
m[v] = struct{}{}
}
return m
}
70 changes: 70 additions & 0 deletions castai/data_source_gke_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package castai

import (
"context"
"github.com/castai/terraform-provider-castai/castai/policies/gke"
"github.com/castai/terraform-provider-castai/castai/sdk"
"github.com/hashicorp/go-cty/cty"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
"github.com/stretchr/testify/require"
"testing"
)

func Test_dataSourceGKEPoliciesRead(t *testing.T) {
up, _ := gke.GetUserPolicy()
lbNeg, _ := gke.GetLoadBalancersNetworkEndpointGroupPolicy()
lbTbp, _ := gke.GetLoadBalancersTargetBackendPoolsPolicy()
lbUig, _ := gke.GetLoadBalancersUnmanagedInstanceGroupsPolicy()
tests := []struct {
name string
features []interface{}
expected int
hasError bool
}{
{
name: "all features",
features: []interface{}{
loadBalancersNetworkEndpointGroupFeature,
loadBalancersTargetBackendPoolsFeature,
loadBalancersUnmanagedInstanceGroupsFeature,
},
expected: len(up) + len(lbNeg) + len(lbTbp) + len(lbUig) - 1, // -1 for the duplicate policy
hasError: false,
},
{
name: "empty features",
expected: len(up),
hasError: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := require.New(t)
//mockClient := mock_sdk.NewMockClientInterface(gomock.NewController(t))

ctx := context.Background()
provider := &ProviderConfig{
api: &sdk.ClientWithResponses{
ClientInterface: nil,
},
}

state := terraform.NewInstanceStateShimmedFromValue(cty.ObjectVal(map[string]cty.Value{}), 0)

resource := dataSourceGKEPolicies()
data := resource.Data(state)
r.NoError(data.Set(featuresResourceName, tt.features))

result := resource.ReadContext(ctx, data, provider)
if tt.hasError {
r.True(result.HasError())
} else {
r.Nil(result)
r.False(result.HasError())
actualPolicies := data.Get(policiesResourceName).([]interface{})
r.Len(actualPolicies, tt.expected)
}
})
}
}
8 changes: 8 additions & 0 deletions castai/policies/gke/loadBalancers-networkEndpointGroup.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Policies": [
"compute.networkEndpointGroups.get",
"compute.networkEndpointGroups.list",
"compute.networkEndpointGroups.attachNetworkEndpoints",
"compute.networkEndpointGroups.detachNetworkEndpoints"
]
}
8 changes: 8 additions & 0 deletions castai/policies/gke/loadBalancers-targetBackendPools.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Policies": [
"compute.targetPools.get",
"compute.targetPools.addInstance",
"compute.targetPools.removeInstance",
"compute.instances.use"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"Policies": [
"compute.instanceGroups.update",
"compute.instances.use"
]
}
39 changes: 39 additions & 0 deletions castai/policies/gke/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ import (
var (
//go:embed iam-policy.json
Policy []byte

//go:embed loadBalancers-networkEndpointGroup.json
LoadBalancersNetworkEndpointGroup []byte

//go:embed loadBalancers-targetBackendPools.json
LoadBalancersTargetBackendPools []byte

//go:embed loadBalancers-unmanagedInstanceGroups.json
LoadBalancersUnmanagedInstanceGroups []byte
)

type pols struct {
Expand All @@ -23,3 +32,33 @@ func GetUserPolicy() ([]string, error) {

return p.Policies, nil
}

func GetLoadBalancersNetworkEndpointGroupPolicy() ([]string, error) {
var p pols
err := json.Unmarshal(LoadBalancersNetworkEndpointGroup, &p)
if err != nil {
return nil, err
}

return p.Policies, nil
}

func GetLoadBalancersTargetBackendPoolsPolicy() ([]string, error) {
var p pols
err := json.Unmarshal(LoadBalancersTargetBackendPools, &p)
if err != nil {
return nil, err
}

return p.Policies, nil
}

func GetLoadBalancersUnmanagedInstanceGroupsPolicy() ([]string, error) {
var p pols
err := json.Unmarshal(LoadBalancersUnmanagedInstanceGroups, &p)
if err != nil {
return nil, err
}

return p.Policies, nil
}
56 changes: 53 additions & 3 deletions castai/policies/gke/policy_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package gke

import (
"github.com/stretchr/testify/require"
"testing"
)

Expand All @@ -14,12 +15,61 @@ func TestPolicies(t *testing.T) {
t.Fatalf("couldn't generate user policy")
}

clustersGet := "container.clusters.get"
zonesGet := "serviceusage.services.list"
wantClustersGet := "container.clusters.get"
wantZonesGet := "serviceusage.services.list"

if !contains(userpolicy, clustersGet) || !contains(userpolicy, zonesGet) {
if !contains(userpolicy, wantClustersGet) || !contains(userpolicy, wantZonesGet) {
t.Fatalf("generated User policy document does not contain required policies")
}
require.Equal(t, 37, len(userpolicy))
})
t.Run("LoadBalancersNetworkEndpointGroup policy", func(t *testing.T) {
lbNegPolicy, err := GetLoadBalancersNetworkEndpointGroupPolicy()
if err != nil {
t.Error(err)
}
if lbNegPolicy == nil {
t.Fatalf("couldn't generate LoadBalancersNetworkEndpointGroup policy")
}

want := "compute.networkEndpointGroups.get"

if !contains(lbNegPolicy, want) {
t.Fatalf("generated LoadBalancersNetworkEndpointGroup policy document does not contain required policies")
}
require.Equal(t, 4, len(lbNegPolicy))
})
t.Run("LoadBalancersTargetBackendPools policy", func(t *testing.T) {
lbTbpPolicy, err := GetLoadBalancersTargetBackendPoolsPolicy()
if err != nil {
t.Error(err)
}
if lbTbpPolicy == nil {
t.Fatalf("couldn't generate LoadBalancersTargetBackendPools policy")
}

want := "compute.targetPools.get"

if !contains(lbTbpPolicy, want) {
t.Fatalf("generated LoadBalancersTargetBackendPools policy document does not contain required policies")
}
require.Equal(t, 4, len(lbTbpPolicy))
})
t.Run("LoadBalancersUnmanagedInstanceGroups policy", func(t *testing.T) {
lbUigPolicy, err := GetLoadBalancersUnmanagedInstanceGroupsPolicy()
if err != nil {
t.Error(err)
}
if lbUigPolicy == nil {
t.Fatalf("couldn't generate LoadBalancersUnmanagedInstanceGroups policy")
}

want := "compute.instanceGroups.update"

if !contains(lbUigPolicy, want) {
t.Fatalf("generated LoadBalancersUnmanagedInstanceGroups policy document does not contain required policies")
}
require.Equal(t, 2, len(lbUigPolicy))
})
}

Expand Down
19 changes: 11 additions & 8 deletions castai/sdk/api.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading