Skip to content

Commit b05257a

Browse files
omerdemirokactions-user
authored andcommitted
feat: add adapter for security policy (#1433)
PR for issue: https://github.com/overmindtech/workspace/issues/1402 Features: Implements GCP Security Policy adapter Ensures adapter is tested GitOrigin-RevId: 62a0766d4152ab64e985a3cddd80f112bf1a8083
1 parent 69eb60f commit b05257a

File tree

6 files changed

+390
-1
lines changed

6 files changed

+390
-1
lines changed

sources/gcp/adapters/adapters.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ func Adapters(ctx context.Context, projectID string, regions []string, zones []s
5050
return nil, err
5151
}
5252

53+
computeSecurityPolicyCli, err := compute.NewSecurityPoliciesRESTClient(ctx)
54+
if err != nil {
55+
return nil, err
56+
}
57+
5358
backendServiceCli, err := compute.NewBackendServicesRESTClient(ctx)
5459
if err != nil {
5560
return nil, err
@@ -89,6 +94,7 @@ func Adapters(ctx context.Context, projectID string, regions []string, zones []s
8994
sources.WrapperToAdapter(NewComputeBackendService(shared.NewComputeBackendServiceClient(backendServiceCli), projectID)),
9095
sources.WrapperToAdapter(NewComputeImage(shared.NewComputeImagesClient(computeImagesCli), projectID)),
9196
sources.WrapperToAdapter(NewComputeHealthCheck(shared.NewComputeHealthCheckClient(computeHealthCheckCli), projectID)),
97+
sources.WrapperToAdapter(NewComputeSecurityPolicy(shared.NewComputeSecurityPolicyClient(computeSecurityPolicyCli), projectID)),
9298
)
9399

94100
// Register the metadata for each adapter
Lines changed: 143 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,150 @@
11
package adapters
22

33
import (
4+
"context"
5+
"errors"
6+
"strconv"
7+
8+
"cloud.google.com/go/compute/apiv1/computepb"
9+
"google.golang.org/api/iterator"
10+
11+
"github.com/overmindtech/cli/sdp-go"
12+
"github.com/overmindtech/cli/sources"
413
gcpshared "github.com/overmindtech/cli/sources/gcp/shared"
514
"github.com/overmindtech/cli/sources/shared"
615
)
716

8-
var ComputeSecurityPolicy = shared.NewItemType(gcpshared.GCP, gcpshared.Compute, gcpshared.SecurityPolicy)
17+
var (
18+
ComputeSecurityPolicy = shared.NewItemType(gcpshared.GCP, gcpshared.Compute, gcpshared.SecurityPolicy)
19+
ComputeRule = shared.NewItemType(gcpshared.GCP, gcpshared.Compute, gcpshared.Rule)
20+
21+
ComputeSecurityPolicyLookupByName = shared.NewItemTypeLookup("name", ComputeSecurityPolicy)
22+
)
23+
24+
type computeSecurityPolicyWrapper struct {
25+
client gcpshared.ComputeSecurityPolicyClient
26+
27+
*gcpshared.ProjectBase
28+
}
29+
30+
// NewComputeSecurityPolicy creates a new computeSecurityPolicyWrapper instance
31+
func NewComputeSecurityPolicy(client gcpshared.ComputeSecurityPolicyClient, projectID string) sources.ListableWrapper {
32+
return &computeSecurityPolicyWrapper{
33+
client: client,
34+
ProjectBase: gcpshared.NewProjectBase(
35+
projectID,
36+
sdp.AdapterCategory_ADAPTER_CATEGORY_SECURITY,
37+
ComputeSecurityPolicy,
38+
),
39+
}
40+
}
41+
42+
// PotentialLinks returns the potential links for the compute forwarding rule wrapper
43+
func (c computeSecurityPolicyWrapper) PotentialLinks() map[shared.ItemType]bool {
44+
return shared.NewItemTypesSet(
45+
ComputeRule,
46+
)
47+
}
48+
49+
// TerraformMappings returns the Terraform mappings for the compute security policy wrapper
50+
func (c computeSecurityPolicyWrapper) TerraformMappings() []*sdp.TerraformMapping {
51+
return []*sdp.TerraformMapping{
52+
{
53+
TerraformMethod: sdp.QueryMethod_GET,
54+
TerraformQueryMap: "google_compute_security_policy.name",
55+
},
56+
}
57+
}
58+
59+
// GetLookups returns the lookups for the compute security policy wrapper
60+
func (c computeSecurityPolicyWrapper) GetLookups() sources.ItemTypeLookups {
61+
return sources.ItemTypeLookups{
62+
ComputeSecurityPolicyLookupByName,
63+
}
64+
}
65+
66+
// Get retrieves a compute security policy by its name
67+
func (c computeSecurityPolicyWrapper) Get(ctx context.Context, queryParts ...string) (*sdp.Item, *sdp.QueryError) {
68+
req := &computepb.GetSecurityPolicyRequest{
69+
Project: c.ProjectID(),
70+
SecurityPolicy: queryParts[0],
71+
}
72+
73+
securityPolicy, err := c.client.Get(ctx, req)
74+
if err != nil {
75+
return nil, gcpshared.QueryError(err)
76+
}
77+
78+
item, sdpErr := c.gcpComputeSecurityPolicyToSDPItem(securityPolicy)
79+
if sdpErr != nil {
80+
return nil, sdpErr
81+
}
82+
83+
return item, nil
84+
}
85+
86+
// List lists compute security policies and converts them to sdp.Items.
87+
func (c computeSecurityPolicyWrapper) List(ctx context.Context) ([]*sdp.Item, *sdp.QueryError) {
88+
it := c.client.List(ctx, &computepb.ListSecurityPoliciesRequest{
89+
Project: c.ProjectID(),
90+
})
91+
92+
var items []*sdp.Item
93+
for {
94+
securityPolicy, err := it.Next()
95+
if errors.Is(err, iterator.Done) {
96+
break
97+
}
98+
if err != nil {
99+
return nil, gcpshared.QueryError(err)
100+
}
101+
102+
item, sdpErr := c.gcpComputeSecurityPolicyToSDPItem(securityPolicy)
103+
if sdpErr != nil {
104+
return nil, sdpErr
105+
}
106+
107+
items = append(items, item)
108+
}
109+
110+
return items, nil
111+
}
112+
113+
// gcpComputeSecurityPolicyToSDPItem converts a GCP Security Policy to an SDP Item
114+
func (c computeSecurityPolicyWrapper) gcpComputeSecurityPolicyToSDPItem(securityPolicy *computepb.SecurityPolicy) (*sdp.Item, *sdp.QueryError) {
115+
attributes, err := shared.ToAttributesWithExclude(securityPolicy, "labels")
116+
if err != nil {
117+
return nil, &sdp.QueryError{
118+
ErrorType: sdp.QueryError_OTHER,
119+
ErrorString: err.Error(),
120+
}
121+
}
122+
123+
sdpItem := &sdp.Item{
124+
Type: ComputeSecurityPolicy.String(),
125+
UniqueAttribute: "name",
126+
Attributes: attributes,
127+
Scope: c.DefaultScope(),
128+
Tags: securityPolicy.GetLabels(),
129+
}
130+
131+
// Link to associated rules
132+
// API Reference: https://cloud.google.com/compute/docs/reference/rest/v1/securityPolicies/getRule
133+
// Rules can be inserted into a security policy using the following API call:
134+
// GET https://compute.googleapis.com/compute/v1/projects/{project}/global/securityPolicies/{securityPolicy}/getRule
135+
for _, rule := range securityPolicy.GetRules() {
136+
policyName := securityPolicy.GetName()
137+
rulePriority := strconv.Itoa(int(rule.GetPriority()))
138+
sdpItem.LinkedItemQueries = append(sdpItem.LinkedItemQueries, &sdp.LinkedItemQuery{
139+
Query: &sdp.Query{
140+
Type: ComputeRule.String(),
141+
Method: sdp.QueryMethod_GET,
142+
Query: shared.CompositeLookupKey(policyName, rulePriority),
143+
Scope: c.ProjectID(),
144+
},
145+
BlastPropagation: &sdp.BlastPropagation{In: false, Out: true},
146+
})
147+
148+
}
149+
return sdpItem, nil
150+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package adapters_test
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"cloud.google.com/go/compute/apiv1/computepb"
8+
"go.uber.org/mock/gomock"
9+
"google.golang.org/api/iterator"
10+
"k8s.io/utils/ptr"
11+
12+
"github.com/overmindtech/cli/sdp-go"
13+
"github.com/overmindtech/cli/sources"
14+
"github.com/overmindtech/cli/sources/gcp/adapters"
15+
"github.com/overmindtech/cli/sources/gcp/shared/mocks"
16+
"github.com/overmindtech/cli/sources/shared"
17+
)
18+
19+
func TestComputeSecurityPolicy(t *testing.T) {
20+
ctx := context.Background()
21+
ctrl := gomock.NewController(t)
22+
defer ctrl.Finish()
23+
24+
mockClient := mocks.NewMockComputeSecurityPolicyClient(ctrl)
25+
projectID := "test-project-id"
26+
27+
t.Run("Get", func(t *testing.T) {
28+
wrapper := adapters.NewComputeSecurityPolicy(mockClient, projectID)
29+
30+
mockClient.EXPECT().Get(ctx, gomock.Any()).Return(createComputeSecurityPolicy("test-security-policy"), nil)
31+
32+
adapter := sources.WrapperToAdapter(wrapper)
33+
34+
sdpItem, qErr := adapter.Get(ctx, wrapper.Scopes()[0], "test-security-policy", true)
35+
if qErr != nil {
36+
t.Fatalf("Expected no error, got: %v", qErr)
37+
}
38+
39+
if sdpItem.GetTags()["env"] != "test" {
40+
t.Fatalf("Expected tag 'env=test', got: %v", sdpItem.GetTags()["env"])
41+
}
42+
43+
t.Run("StaticTests", func(t *testing.T) {
44+
queryTests := shared.QueryTests{
45+
{
46+
ExpectedType: adapters.ComputeRule.String(),
47+
ExpectedMethod: sdp.QueryMethod_GET,
48+
ExpectedQuery: "test-security-policy|1000",
49+
ExpectedScope: projectID,
50+
ExpectedBlastPropagation: &sdp.BlastPropagation{
51+
In: false,
52+
Out: true,
53+
},
54+
},
55+
}
56+
57+
shared.RunStaticTests(t, adapter, sdpItem, queryTests)
58+
})
59+
})
60+
61+
t.Run("List", func(t *testing.T) {
62+
wrapper := adapters.NewComputeSecurityPolicy(mockClient, projectID)
63+
64+
adapter := sources.WrapperToAdapter(wrapper)
65+
66+
mockComputeIterator := mocks.NewMockComputeSecurityPolicyIterator(ctrl)
67+
68+
mockComputeIterator.EXPECT().Next().Return(createComputeSecurityPolicy("test-security-policy-1"), nil)
69+
mockComputeIterator.EXPECT().Next().Return(createComputeSecurityPolicy("test-security-policy-2"), nil)
70+
mockComputeIterator.EXPECT().Next().Return(nil, iterator.Done)
71+
72+
mockClient.EXPECT().List(ctx, gomock.Any()).Return(mockComputeIterator)
73+
74+
sdpItems, err := adapter.List(ctx, wrapper.Scopes()[0], true)
75+
if err != nil {
76+
t.Fatalf("Expected no error, got: %v", err)
77+
}
78+
79+
if len(sdpItems) != 2 {
80+
t.Fatalf("Expected 2 items, got: %d", len(sdpItems))
81+
}
82+
83+
for _, item := range sdpItems {
84+
if item.Validate() != nil {
85+
t.Fatalf("Expected no validation error, got: %v", item.Validate())
86+
}
87+
88+
if item.GetTags()["env"] != "test" {
89+
t.Fatalf("Expected tag 'env=test', got: %s", item.GetTags()["env"])
90+
}
91+
}
92+
})
93+
}
94+
95+
func createComputeSecurityPolicy(policyName string) *computepb.SecurityPolicy {
96+
return &computepb.SecurityPolicy{
97+
Name: ptr.To(policyName),
98+
Labels: map[string]string{"env": "test"},
99+
Rules: []*computepb.SecurityPolicyRule{
100+
{
101+
Priority: ptr.To(int32(1000)),
102+
},
103+
},
104+
Region: ptr.To("us-central1"),
105+
}
106+
}

sources/gcp/shared/compute-clients.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,3 +383,35 @@ func (c computeReservationClient) Get(ctx context.Context, req *computepb.GetRes
383383
func (c computeReservationClient) List(ctx context.Context, req *computepb.ListReservationsRequest, opts ...gax.CallOption) ComputeReservationIterator {
384384
return c.client.List(ctx, req, opts...)
385385
}
386+
387+
// ComputeSecurityPolicyIterator is an interface for iterating over compute security policies
388+
type ComputeSecurityPolicyIterator interface {
389+
Next() (*computepb.SecurityPolicy, error)
390+
}
391+
392+
// ComputeSecurityPolicyClient is an interface for the Compute Security Policies client
393+
type ComputeSecurityPolicyClient interface {
394+
Get(ctx context.Context, req *computepb.GetSecurityPolicyRequest, opts ...gax.CallOption) (*computepb.SecurityPolicy, error)
395+
List(ctx context.Context, req *computepb.ListSecurityPoliciesRequest, opts ...gax.CallOption) ComputeSecurityPolicyIterator
396+
}
397+
398+
type computeSecurityPolicyClient struct {
399+
client *compute.SecurityPoliciesClient
400+
}
401+
402+
// NewComputeSecurityPolicyClient creates a new ComputeSecurityPolicyClient
403+
func NewComputeSecurityPolicyClient(securityPolicyClient *compute.SecurityPoliciesClient) ComputeSecurityPolicyClient {
404+
return &computeSecurityPolicyClient{
405+
client: securityPolicyClient,
406+
}
407+
}
408+
409+
// Get retrieves a compute security policy
410+
func (c computeSecurityPolicyClient) Get(ctx context.Context, req *computepb.GetSecurityPolicyRequest, opts ...gax.CallOption) (*computepb.SecurityPolicy, error) {
411+
return c.client.Get(ctx, req, opts...)
412+
}
413+
414+
// List lists compute security policies and returns an iterator
415+
func (c computeSecurityPolicyClient) List(ctx context.Context, req *computepb.ListSecurityPoliciesRequest, opts ...gax.CallOption) ComputeSecurityPolicyIterator {
416+
return c.client.List(ctx, req, opts...)
417+
}

0 commit comments

Comments
 (0)