diff --git a/apis/vpcresources/v1beta1/securitygrouppolicy_types.go b/apis/vpcresources/v1beta1/securitygrouppolicy_types.go index b929dcf5..5ebf786b 100644 --- a/apis/vpcresources/v1beta1/securitygrouppolicy_types.go +++ b/apis/vpcresources/v1beta1/securitygrouppolicy_types.go @@ -31,7 +31,13 @@ type GroupIds struct { // Groups is the list of EC2 Security Groups Ids that need to be applied to the ENI of a Pod. // +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MaxItems=5 + // +kubebuilder:validation:UniqueItems=true Groups []string `json:"groupIds,omitempty"` + // GroupNames is the list of EC2 Security Group Names that need to be applied to the ENI of a Pod. + // +kubebuilder:validation:MinItems=0 + // +kubebuilder:validation:MaxItems=5 + // +kubebuilder:validation:UniqueItems=true + GroupNames []string `json:"groupNames,omitempty"` } // ServiceAccountSelector contains the selection criteria for matching pod with service account that matches the label selector @@ -45,6 +51,7 @@ type ServiceAccountSelector struct { // +kubebuilder:object:root=true // +kubebuilder:printcolumn:name="Security-Group-Ids",type=string,JSONPath=`.spec.securityGroups.groupIds`,description="The security group IDs to apply to the elastic network interface of pods that match this policy" +// +kubebuilder:printcolumn:name="Security-Group-Names",type=string,JSONPath=`.spec.securityGroups.groupNames`,description="The security group names to apply to the elastic network interface of pods that match this policy" // +kubebuilder:resource:shortName=sgp // Custom Resource Definition for applying security groups to pods diff --git a/apis/vpcresources/v1beta1/zz_generated.deepcopy.go b/apis/vpcresources/v1beta1/zz_generated.deepcopy.go index d3910ca5..d17853df 100644 --- a/apis/vpcresources/v1beta1/zz_generated.deepcopy.go +++ b/apis/vpcresources/v1beta1/zz_generated.deepcopy.go @@ -31,6 +31,11 @@ func (in *GroupIds) DeepCopyInto(out *GroupIds) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.GroupNames != nil { + in, out := &in.GroupNames, &out.GroupNames + *out = make([]string, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GroupIds. diff --git a/config/crd/bases/vpcresources.k8s.aws_securitygrouppolicies.yaml b/config/crd/bases/vpcresources.k8s.aws_securitygrouppolicies.yaml index 64d4aac0..aadf77d6 100644 --- a/config/crd/bases/vpcresources.k8s.aws_securitygrouppolicies.yaml +++ b/config/crd/bases/vpcresources.k8s.aws_securitygrouppolicies.yaml @@ -23,6 +23,11 @@ spec: jsonPath: .spec.securityGroups.groupIds name: Security-Group-Ids type: string + - description: The security group names to apply to the elastic network interface + of pods that match this policy + jsonPath: .spec.securityGroups.groupNames + name: Security-Group-Names + type: string name: v1beta1 schema: openAPIV3Schema: @@ -102,6 +107,16 @@ spec: maxItems: 5 minItems: 1 type: array + uniqueItems: true + groupNames: + description: GroupNames is the list of EC2 Security Group Names + that need to be applied to the ENI of a Pod. + items: + type: string + maxItems: 5 + minItems: 0 + type: array + uniqueItems: true type: object serviceAccountSelector: description: A label selector is a label query over a set of resources. diff --git a/main.go b/main.go index 5e15688c..762316a2 100644 --- a/main.go +++ b/main.go @@ -273,7 +273,7 @@ func main() { if err != nil { setupLog.Error(err, "unable to create ec2 wrapper") } - ec2APIHelper := ec2API.NewEC2APIHelper(ec2Wrapper, clusterName) + ec2APIHelper := ec2API.NewEC2APIHelper(ec2Wrapper, clusterName, vpcID) sgpAPI := utils.NewSecurityGroupForPodsAPI( mgr.GetClient(), diff --git a/mocks/amazon-vcp-resource-controller-k8s/pkg/aws/ec2/api/mock_ec2_apihelper.go b/mocks/amazon-vcp-resource-controller-k8s/pkg/aws/ec2/api/mock_ec2_apihelper.go index 19f7e104..8d287821 100644 --- a/mocks/amazon-vcp-resource-controller-k8s/pkg/aws/ec2/api/mock_ec2_apihelper.go +++ b/mocks/amazon-vcp-resource-controller-k8s/pkg/aws/ec2/api/mock_ec2_apihelper.go @@ -1,16 +1,3 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"). You may -// not use this file except in compliance with the License. A copy of the -// License is located at -// -// http://aws.amazon.com/apache2.0/ -// -// or in the "license" file accompanying this file. This file is distributed -// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -// express or implied. See the License for the specific language governing -// permissions and limitations under the License. - // Code generated by MockGen. DO NOT EDIT. // Source: github.com/aws/amazon-vpc-resource-controller-k8s/pkg/aws/ec2/api (interfaces: EC2APIHelper) @@ -240,6 +227,21 @@ func (mr *MockEC2APIHelperMockRecorder) GetInstanceNetworkInterface(arg0 interfa return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInstanceNetworkInterface", reflect.TypeOf((*MockEC2APIHelper)(nil).GetInstanceNetworkInterface), arg0) } +// GetSecurityGroupIdsForSecurityGroupNames mocks base method. +func (m *MockEC2APIHelper) GetSecurityGroupIdsForSecurityGroupNames(arg0 []string) ([]string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSecurityGroupIdsForSecurityGroupNames", arg0) + ret0, _ := ret[0].([]string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetSecurityGroupIdsForSecurityGroupNames indicates an expected call of GetSecurityGroupIdsForSecurityGroupNames. +func (mr *MockEC2APIHelperMockRecorder) GetSecurityGroupIdsForSecurityGroupNames(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSecurityGroupIdsForSecurityGroupNames", reflect.TypeOf((*MockEC2APIHelper)(nil).GetSecurityGroupIdsForSecurityGroupNames), arg0) +} + // GetSubnet mocks base method. func (m *MockEC2APIHelper) GetSubnet(arg0 *string) (*ec2.Subnet, error) { m.ctrl.T.Helper() diff --git a/mocks/amazon-vcp-resource-controller-k8s/pkg/aws/ec2/api/mock_ec2_wrapper.go b/mocks/amazon-vcp-resource-controller-k8s/pkg/aws/ec2/api/mock_ec2_wrapper.go index f40d94c6..d653e736 100644 --- a/mocks/amazon-vcp-resource-controller-k8s/pkg/aws/ec2/api/mock_ec2_wrapper.go +++ b/mocks/amazon-vcp-resource-controller-k8s/pkg/aws/ec2/api/mock_ec2_wrapper.go @@ -1,16 +1,3 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"). You may -// not use this file except in compliance with the License. A copy of the -// License is located at -// -// http://aws.amazon.com/apache2.0/ -// -// or in the "license" file accompanying this file. This file is distributed -// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -// express or implied. See the License for the specific language governing -// permissions and limitations under the License. - // Code generated by MockGen. DO NOT EDIT. // Source: github.com/aws/amazon-vpc-resource-controller-k8s/pkg/aws/ec2/api (interfaces: EC2Wrapper) @@ -182,6 +169,21 @@ func (mr *MockEC2WrapperMockRecorder) DescribeNetworkInterfaces(arg0 interface{} return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeNetworkInterfaces", reflect.TypeOf((*MockEC2Wrapper)(nil).DescribeNetworkInterfaces), arg0) } +// DescribeNetworkInterfacesPages mocks base method. +func (m *MockEC2Wrapper) DescribeNetworkInterfacesPages(arg0 *ec2.DescribeNetworkInterfacesInput) ([]*ec2.NetworkInterface, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DescribeNetworkInterfacesPages", arg0) + ret0, _ := ret[0].([]*ec2.NetworkInterface) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeNetworkInterfacesPages indicates an expected call of DescribeNetworkInterfacesPages. +func (mr *MockEC2WrapperMockRecorder) DescribeNetworkInterfacesPages(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeNetworkInterfacesPages", reflect.TypeOf((*MockEC2Wrapper)(nil).DescribeNetworkInterfacesPages), arg0) +} + // DescribeSubnets mocks base method. func (m *MockEC2Wrapper) DescribeSubnets(arg0 *ec2.DescribeSubnetsInput) (*ec2.DescribeSubnetsOutput, error) { m.ctrl.T.Helper() @@ -227,6 +229,21 @@ func (mr *MockEC2WrapperMockRecorder) DetachNetworkInterface(arg0 interface{}) * return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DetachNetworkInterface", reflect.TypeOf((*MockEC2Wrapper)(nil).DetachNetworkInterface), arg0) } +// GetSecurityGroupsForVpc mocks base method. +func (m *MockEC2Wrapper) GetSecurityGroupsForVpc(arg0 *ec2.GetSecurityGroupsForVpcInput) (*ec2.GetSecurityGroupsForVpcOutput, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSecurityGroupsForVpc", arg0) + ret0, _ := ret[0].(*ec2.GetSecurityGroupsForVpcOutput) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetSecurityGroupsForVpc indicates an expected call of GetSecurityGroupsForVpc. +func (mr *MockEC2WrapperMockRecorder) GetSecurityGroupsForVpc(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSecurityGroupsForVpc", reflect.TypeOf((*MockEC2Wrapper)(nil).GetSecurityGroupsForVpc), arg0) +} + // ModifyNetworkInterfaceAttribute mocks base method. func (m *MockEC2Wrapper) ModifyNetworkInterfaceAttribute(arg0 *ec2.ModifyNetworkInterfaceAttributeInput) (*ec2.ModifyNetworkInterfaceAttributeOutput, error) { m.ctrl.T.Helper() diff --git a/mocks/amazon-vcp-resource-controller-k8s/pkg/aws/ec2/mock_instance.go b/mocks/amazon-vcp-resource-controller-k8s/pkg/aws/ec2/mock_instance.go index d287cff4..53f71af7 100644 --- a/mocks/amazon-vcp-resource-controller-k8s/pkg/aws/ec2/mock_instance.go +++ b/mocks/amazon-vcp-resource-controller-k8s/pkg/aws/ec2/mock_instance.go @@ -1,16 +1,3 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"). You may -// not use this file except in compliance with the License. A copy of the -// License is located at -// -// http://aws.amazon.com/apache2.0/ -// -// or in the "license" file accompanying this file. This file is distributed -// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -// express or implied. See the License for the specific language governing -// permissions and limitations under the License. - // Code generated by MockGen. DO NOT EDIT. // Source: github.com/aws/amazon-vpc-resource-controller-k8s/pkg/aws/ec2 (interfaces: EC2Instance) diff --git a/mocks/amazon-vcp-resource-controller-k8s/pkg/condition/mock_condition.go b/mocks/amazon-vcp-resource-controller-k8s/pkg/condition/mock_condition.go index f2771980..4d6f08b4 100644 --- a/mocks/amazon-vcp-resource-controller-k8s/pkg/condition/mock_condition.go +++ b/mocks/amazon-vcp-resource-controller-k8s/pkg/condition/mock_condition.go @@ -1,16 +1,3 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"). You may -// not use this file except in compliance with the License. A copy of the -// License is located at -// -// http://aws.amazon.com/apache2.0/ -// -// or in the "license" file accompanying this file. This file is distributed -// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -// express or implied. See the License for the specific language governing -// permissions and limitations under the License. - // Code generated by MockGen. DO NOT EDIT. // Source: github.com/aws/amazon-vpc-resource-controller-k8s/pkg/condition (interfaces: Conditions) diff --git a/mocks/amazon-vcp-resource-controller-k8s/pkg/handler/mock_handler.go b/mocks/amazon-vcp-resource-controller-k8s/pkg/handler/mock_handler.go index 8ad52745..9570956b 100644 --- a/mocks/amazon-vcp-resource-controller-k8s/pkg/handler/mock_handler.go +++ b/mocks/amazon-vcp-resource-controller-k8s/pkg/handler/mock_handler.go @@ -1,16 +1,3 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"). You may -// not use this file except in compliance with the License. A copy of the -// License is located at -// -// http://aws.amazon.com/apache2.0/ -// -// or in the "license" file accompanying this file. This file is distributed -// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -// express or implied. See the License for the specific language governing -// permissions and limitations under the License. - // Code generated by MockGen. DO NOT EDIT. // Source: github.com/aws/amazon-vpc-resource-controller-k8s/pkg/handler (interfaces: Handler) diff --git a/mocks/amazon-vcp-resource-controller-k8s/pkg/k8s/mock_k8swrapper.go b/mocks/amazon-vcp-resource-controller-k8s/pkg/k8s/mock_k8swrapper.go index 9b376ede..07b6a4fe 100644 --- a/mocks/amazon-vcp-resource-controller-k8s/pkg/k8s/mock_k8swrapper.go +++ b/mocks/amazon-vcp-resource-controller-k8s/pkg/k8s/mock_k8swrapper.go @@ -1,16 +1,3 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"). You may -// not use this file except in compliance with the License. A copy of the -// License is located at -// -// http://aws.amazon.com/apache2.0/ -// -// or in the "license" file accompanying this file. This file is distributed -// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -// express or implied. See the License for the specific language governing -// permissions and limitations under the License. - // Code generated by MockGen. DO NOT EDIT. // Source: github.com/aws/amazon-vpc-resource-controller-k8s/pkg/k8s (interfaces: K8sWrapper) diff --git a/mocks/amazon-vcp-resource-controller-k8s/pkg/k8s/pod/mock_podapiwrapper.go b/mocks/amazon-vcp-resource-controller-k8s/pkg/k8s/pod/mock_podapiwrapper.go index a1ef9652..da1e79ba 100644 --- a/mocks/amazon-vcp-resource-controller-k8s/pkg/k8s/pod/mock_podapiwrapper.go +++ b/mocks/amazon-vcp-resource-controller-k8s/pkg/k8s/pod/mock_podapiwrapper.go @@ -1,16 +1,3 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"). You may -// not use this file except in compliance with the License. A copy of the -// License is located at -// -// http://aws.amazon.com/apache2.0/ -// -// or in the "license" file accompanying this file. This file is distributed -// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -// express or implied. See the License for the specific language governing -// permissions and limitations under the License. - // Code generated by MockGen. DO NOT EDIT. // Source: github.com/aws/amazon-vpc-resource-controller-k8s/pkg/k8s/pod (interfaces: PodClientAPIWrapper) diff --git a/mocks/amazon-vcp-resource-controller-k8s/pkg/node/manager/mock_manager.go b/mocks/amazon-vcp-resource-controller-k8s/pkg/node/manager/mock_manager.go index 092caf34..643e803f 100644 --- a/mocks/amazon-vcp-resource-controller-k8s/pkg/node/manager/mock_manager.go +++ b/mocks/amazon-vcp-resource-controller-k8s/pkg/node/manager/mock_manager.go @@ -1,16 +1,3 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"). You may -// not use this file except in compliance with the License. A copy of the -// License is located at -// -// http://aws.amazon.com/apache2.0/ -// -// or in the "license" file accompanying this file. This file is distributed -// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -// express or implied. See the License for the specific language governing -// permissions and limitations under the License. - // Code generated by MockGen. DO NOT EDIT. // Source: github.com/aws/amazon-vpc-resource-controller-k8s/pkg/node/manager (interfaces: Manager) diff --git a/mocks/amazon-vcp-resource-controller-k8s/pkg/node/mock_node.go b/mocks/amazon-vcp-resource-controller-k8s/pkg/node/mock_node.go index 59879491..0c96420e 100644 --- a/mocks/amazon-vcp-resource-controller-k8s/pkg/node/mock_node.go +++ b/mocks/amazon-vcp-resource-controller-k8s/pkg/node/mock_node.go @@ -1,16 +1,3 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"). You may -// not use this file except in compliance with the License. A copy of the -// License is located at -// -// http://aws.amazon.com/apache2.0/ -// -// or in the "license" file accompanying this file. This file is distributed -// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -// express or implied. See the License for the specific language governing -// permissions and limitations under the License. - // Code generated by MockGen. DO NOT EDIT. // Source: github.com/aws/amazon-vpc-resource-controller-k8s/pkg/node (interfaces: Node) diff --git a/mocks/amazon-vcp-resource-controller-k8s/pkg/pool/mock_pool.go b/mocks/amazon-vcp-resource-controller-k8s/pkg/pool/mock_pool.go index b0654a51..c5c29888 100644 --- a/mocks/amazon-vcp-resource-controller-k8s/pkg/pool/mock_pool.go +++ b/mocks/amazon-vcp-resource-controller-k8s/pkg/pool/mock_pool.go @@ -1,16 +1,3 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"). You may -// not use this file except in compliance with the License. A copy of the -// License is located at -// -// http://aws.amazon.com/apache2.0/ -// -// or in the "license" file accompanying this file. This file is distributed -// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -// express or implied. See the License for the specific language governing -// permissions and limitations under the License. - // Code generated by MockGen. DO NOT EDIT. // Source: github.com/aws/amazon-vpc-resource-controller-k8s/pkg/pool (interfaces: Pool) diff --git a/mocks/amazon-vcp-resource-controller-k8s/pkg/provider/branch/cooldown/mock_cooldown.go b/mocks/amazon-vcp-resource-controller-k8s/pkg/provider/branch/cooldown/mock_cooldown.go index ba1f1427..d4883986 100644 --- a/mocks/amazon-vcp-resource-controller-k8s/pkg/provider/branch/cooldown/mock_cooldown.go +++ b/mocks/amazon-vcp-resource-controller-k8s/pkg/provider/branch/cooldown/mock_cooldown.go @@ -1,16 +1,3 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"). You may -// not use this file except in compliance with the License. A copy of the -// License is located at -// -// http://aws.amazon.com/apache2.0/ -// -// or in the "license" file accompanying this file. This file is distributed -// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -// express or implied. See the License for the specific language governing -// permissions and limitations under the License. - // Code generated by MockGen. DO NOT EDIT. // Source: github.com/aws/amazon-vpc-resource-controller-k8s/pkg/provider/branch/cooldown (interfaces: CoolDown) diff --git a/mocks/amazon-vcp-resource-controller-k8s/pkg/provider/branch/trunk/mock_trunk.go b/mocks/amazon-vcp-resource-controller-k8s/pkg/provider/branch/trunk/mock_trunk.go index ac4b1c73..ef08dbf5 100644 --- a/mocks/amazon-vcp-resource-controller-k8s/pkg/provider/branch/trunk/mock_trunk.go +++ b/mocks/amazon-vcp-resource-controller-k8s/pkg/provider/branch/trunk/mock_trunk.go @@ -1,16 +1,3 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"). You may -// not use this file except in compliance with the License. A copy of the -// License is located at -// -// http://aws.amazon.com/apache2.0/ -// -// or in the "license" file accompanying this file. This file is distributed -// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -// express or implied. See the License for the specific language governing -// permissions and limitations under the License. - // Code generated by MockGen. DO NOT EDIT. // Source: github.com/aws/amazon-vpc-resource-controller-k8s/pkg/provider/branch/trunk (interfaces: TrunkENI) diff --git a/mocks/amazon-vcp-resource-controller-k8s/pkg/provider/ip/eni/mock_eni.go b/mocks/amazon-vcp-resource-controller-k8s/pkg/provider/ip/eni/mock_eni.go index 9cf897f8..b63526fa 100644 --- a/mocks/amazon-vcp-resource-controller-k8s/pkg/provider/ip/eni/mock_eni.go +++ b/mocks/amazon-vcp-resource-controller-k8s/pkg/provider/ip/eni/mock_eni.go @@ -1,16 +1,3 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"). You may -// not use this file except in compliance with the License. A copy of the -// License is located at -// -// http://aws.amazon.com/apache2.0/ -// -// or in the "license" file accompanying this file. This file is distributed -// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -// express or implied. See the License for the specific language governing -// permissions and limitations under the License. - // Code generated by MockGen. DO NOT EDIT. // Source: github.com/aws/amazon-vpc-resource-controller-k8s/pkg/provider/ip/eni (interfaces: ENIManager) diff --git a/mocks/amazon-vcp-resource-controller-k8s/pkg/provider/mock_provider.go b/mocks/amazon-vcp-resource-controller-k8s/pkg/provider/mock_provider.go index 0046284b..487180b1 100644 --- a/mocks/amazon-vcp-resource-controller-k8s/pkg/provider/mock_provider.go +++ b/mocks/amazon-vcp-resource-controller-k8s/pkg/provider/mock_provider.go @@ -1,16 +1,3 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"). You may -// not use this file except in compliance with the License. A copy of the -// License is located at -// -// http://aws.amazon.com/apache2.0/ -// -// or in the "license" file accompanying this file. This file is distributed -// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -// express or implied. See the License for the specific language governing -// permissions and limitations under the License. - // Code generated by MockGen. DO NOT EDIT. // Source: github.com/aws/amazon-vpc-resource-controller-k8s/pkg/provider (interfaces: ResourceProvider) diff --git a/mocks/amazon-vcp-resource-controller-k8s/pkg/resource/mock_resources.go b/mocks/amazon-vcp-resource-controller-k8s/pkg/resource/mock_resources.go index 328c2218..2867d39e 100644 --- a/mocks/amazon-vcp-resource-controller-k8s/pkg/resource/mock_resources.go +++ b/mocks/amazon-vcp-resource-controller-k8s/pkg/resource/mock_resources.go @@ -1,16 +1,3 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"). You may -// not use this file except in compliance with the License. A copy of the -// License is located at -// -// http://aws.amazon.com/apache2.0/ -// -// or in the "license" file accompanying this file. This file is distributed -// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -// express or implied. See the License for the specific language governing -// permissions and limitations under the License. - // Code generated by MockGen. DO NOT EDIT. // Source: github.com/aws/amazon-vpc-resource-controller-k8s/pkg/resource (interfaces: ResourceManager) diff --git a/mocks/amazon-vcp-resource-controller-k8s/pkg/utils/mock_k8shelper.go b/mocks/amazon-vcp-resource-controller-k8s/pkg/utils/mock_k8shelper.go index 1e6f3c27..b7217716 100644 --- a/mocks/amazon-vcp-resource-controller-k8s/pkg/utils/mock_k8shelper.go +++ b/mocks/amazon-vcp-resource-controller-k8s/pkg/utils/mock_k8shelper.go @@ -1,16 +1,3 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"). You may -// not use this file except in compliance with the License. A copy of the -// License is located at -// -// http://aws.amazon.com/apache2.0/ -// -// or in the "license" file accompanying this file. This file is distributed -// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -// express or implied. See the License for the specific language governing -// permissions and limitations under the License. - // Code generated by MockGen. DO NOT EDIT. // Source: github.com/aws/amazon-vpc-resource-controller-k8s/pkg/utils (interfaces: SecurityGroupForPodsAPI) diff --git a/mocks/amazon-vcp-resource-controller-k8s/pkg/worker/mock_worker.go b/mocks/amazon-vcp-resource-controller-k8s/pkg/worker/mock_worker.go index 831e6364..d14e3d95 100644 --- a/mocks/amazon-vcp-resource-controller-k8s/pkg/worker/mock_worker.go +++ b/mocks/amazon-vcp-resource-controller-k8s/pkg/worker/mock_worker.go @@ -1,16 +1,3 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"). You may -// not use this file except in compliance with the License. A copy of the -// License is located at -// -// http://aws.amazon.com/apache2.0/ -// -// or in the "license" file accompanying this file. This file is distributed -// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -// express or implied. See the License for the specific language governing -// permissions and limitations under the License. - // Code generated by MockGen. DO NOT EDIT. // Source: github.com/aws/amazon-vpc-resource-controller-k8s/pkg/worker (interfaces: Worker) diff --git a/pkg/aws/ec2/api/helper.go b/pkg/aws/ec2/api/helper.go index 14a7864f..4ffe6db8 100644 --- a/pkg/aws/ec2/api/helper.go +++ b/pkg/aws/ec2/api/helper.go @@ -15,9 +15,11 @@ package api import ( "fmt" + "sort" "time" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/ec2" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/util/retry" @@ -60,17 +62,39 @@ var ( ) type ec2APIHelper struct { - ec2Wrapper EC2Wrapper + ec2Wrapper EC2Wrapper + workerNodeVpcId string + securityGroupNameToIdMap map[string]string } -func NewEC2APIHelper(ec2Wrapper EC2Wrapper, clusterName string) EC2APIHelper { +func NewEC2APIHelper(ec2Wrapper EC2Wrapper, clusterName, workerNodeVpcId string) EC2APIHelper { // Set the key and value of the cluster name tag which will be used to tag all the network interfaces created by // the controller clusterNameTag = &ec2.Tag{ Key: aws.String(fmt.Sprintf(config.ClusterNameTagKeyFormat, clusterName)), Value: aws.String(config.ClusterNameTagValue), } - return &ec2APIHelper{ec2Wrapper: ec2Wrapper} + return &ec2APIHelper{ + ec2Wrapper: ec2Wrapper, + workerNodeVpcId: workerNodeVpcId, + securityGroupNameToIdMap: make(map[string]string), + } +} + +// For unit testing +func newEC2APIHelper(ec2Wrapper EC2Wrapper, clusterName, workerNodeVpcId string, + securityGroupNameToIdMap map[string]string) EC2APIHelper { + // Set the key and value of the cluster name tag which will be used to tag all the network interfaces created by + // the controller + clusterNameTag = &ec2.Tag{ + Key: aws.String(fmt.Sprintf(config.ClusterNameTagKeyFormat, clusterName)), + Value: aws.String(config.ClusterNameTagValue), + } + return &ec2APIHelper{ + ec2Wrapper: ec2Wrapper, + workerNodeVpcId: workerNodeVpcId, + securityGroupNameToIdMap: securityGroupNameToIdMap, + } } type EC2APIHelper interface { @@ -93,6 +117,7 @@ type EC2APIHelper interface { GetInstanceDetails(instanceId *string) (*ec2.Instance, error) AssignIPv4ResourcesAndWaitTillReady(eniID string, resourceType config.ResourceType, count int) ([]string, error) UnassignIPv4Resources(eniID string, resourceType config.ResourceType, resources []string) error + GetSecurityGroupIdsForSecurityGroupNames(securityGroupNames []string) ([]string, error) } // CreateNetworkInterface creates a new network interface @@ -101,7 +126,7 @@ func (h *ec2APIHelper) CreateNetworkInterface(description *string, subnetId *str eniDescription := CreateENIDescriptionPrefix + *description var ec2SecurityGroups []*string - if securityGroups != nil && len(securityGroups) != 0 { + if len(securityGroups) != 0 { // Only add security groups if there are one or more security group provided, otherwise API call will fail instead // of creating the interface with default security groups ec2SecurityGroups = aws.StringSlice(securityGroups) @@ -623,3 +648,77 @@ func (h *ec2APIHelper) DetachAndDeleteNetworkInterface(attachmentID *string, nwI } return nil } + +func (h *ec2APIHelper) GetSecurityGroupIdsForSecurityGroupNames(securityGroupNames []string) ([]string, error) { + sgIds := make([]string, 0) + if len(securityGroupNames) < 1 { + return sgIds, nil + } + sgNamesNotInCache := make([]string, 0) + for _, sgName := range securityGroupNames { + if sgId, ok := h.securityGroupNameToIdMap[sgName]; !ok { + sgNamesNotInCache = append(sgNamesNotInCache, sgName) + } else { + sgIds = append(sgIds, sgId) + } + } + if len(sgIds) == len(securityGroupNames) { + //All SGNames found in cache + return sgIds, nil + } + filters := []*ec2.Filter{ + { + Name: aws.String("group-name"), + Values: aws.StringSlice(sgNamesNotInCache), + }, + } + input := &ec2.GetSecurityGroupsForVpcInput{VpcId: &h.workerNodeVpcId, Filters: filters} + foundSgNames := map[string]bool{} + for { + output, err := h.ec2Wrapper.GetSecurityGroupsForVpc(input) + if err != nil { + return nil, err + } + for _, sg := range output.SecurityGroupForVpcs { + h.securityGroupNameToIdMap[*sg.GroupName] = *sg.GroupId + sgIds = append(sgIds, *sg.GroupId) + foundSgNames[*sg.GroupName] = true + } + if output.NextToken == nil { + break + } + input.NextToken = output.NextToken + } + var missingSgNames []string + for _, sgName := range sgNamesNotInCache { + if _, ok := foundSgNames[sgName]; !ok { + missingSgNames = append(missingSgNames, sgName) + } + } + if len(missingSgNames) > 0 { + sort.Strings(missingSgNames) + return nil, fmt.Errorf("security groups %s not found", missingSgNames) + } + return sgIds, nil +} + +// GetSourceAcctAndArn constructs source acct and arn and return them for use +func GetSourceAcctAndArn(roleARN, region, clusterName string) (string, string, error) { + // ARN format (https://docs.aws.amazon.com/IAM/latest/UserGuide/reference-arns.html) + // arn:partition:service:region:account-id:resource-type/resource-id + // IAM format, region is always blank + // arn:aws:iam::account:role/role-name-with-path + if !arn.IsARN(roleARN) { + return "", "", fmt.Errorf("incorrect ARN format for role %s", roleARN) + } else if region == "" { + return "", "", nil + } + + parsedArn, err := arn.Parse(roleARN) + if err != nil { + return "", "", err + } + + sourceArn := fmt.Sprintf("arn:%s:eks:%s:%s:cluster/%s", parsedArn.Partition, region, parsedArn.AccountID, clusterName) + return parsedArn.AccountID, sourceArn, nil +} diff --git a/pkg/aws/ec2/api/helper_test.go b/pkg/aws/ec2/api/helper_test.go index 6981c99a..e7fe501c 100644 --- a/pkg/aws/ec2/api/helper_test.go +++ b/pkg/aws/ec2/api/helper_test.go @@ -29,7 +29,8 @@ import ( ) var ( - clusterName = "cluster-name" + clusterName = "cluster-name" + workerNodeVpcId = "vpc-abcdef" // instance id of EC2 worker node instanceId = "i-00000000000000000" instanceType = "m5.xlarge" @@ -58,6 +59,11 @@ var ( securityGroup2 = "sg-00000000000000002" securityGroups = []string{securityGroup1, securityGroup2} + securityGroupName1 = "db-sg" + securityGroupName2 = "redis-sg" + securityGroupName3 = "web-sg" + securityGroupNames = []string{securityGroupName1, securityGroupName2} + tags = []*ec2.Tag{ { Key: aws.String("mock-key"), @@ -335,6 +341,28 @@ var ( } maxRetryOnError = 3 + + getSecurityGroupForVpcInput = &ec2.GetSecurityGroupsForVpcInput{ + Filters: []*ec2.Filter{ + { + Name: aws.String("group-name"), + Values: aws.StringSlice(securityGroupNames), + }, + }, + VpcId: &workerNodeVpcId, + } + getSecurityGroupForVpcOutput = &ec2.GetSecurityGroupsForVpcOutput{ + SecurityGroupForVpcs: []*ec2.SecurityGroupForVpc{ + { + GroupId: aws.String(securityGroup1), + GroupName: aws.String(securityGroupName1), + }, + { + GroupId: aws.String(securityGroup2), + GroupName: aws.String(securityGroupName2), + }, + }, + } ) // getMockWrapper returns the Mock EC2Wrapper along with the EC2APIHelper with mock EC2Wrapper set up @@ -363,7 +391,7 @@ func getMockWrapper(ctrl *gomock.Controller) (EC2APIHelper, *mock_api.MockEC2Wra } mockWrapper := mock_api.NewMockEC2Wrapper(ctrl) - ec2ApiHelper := NewEC2APIHelper(mockWrapper, clusterName) + ec2ApiHelper := NewEC2APIHelper(mockWrapper, clusterName, workerNodeVpcId) return ec2ApiHelper, mockWrapper } @@ -1203,3 +1231,113 @@ func TestEc2APIHelper_GetBranchNetworkInterface_PaginatedResults(t *testing.T) { assert.NoError(t, err) assert.ElementsMatch(t, []*ec2.NetworkInterface{&networkInterface1, &networkInterface2}, branchInterfaces) } + +func TestEc2APIHelper_GetSecurityGroupIdsForSecurityGroupNames_EmptyCache(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + ec2ApiHelper, mockWrapper := getMockWrapper(ctrl) + + mockWrapper.EXPECT().GetSecurityGroupsForVpc(getSecurityGroupForVpcInput).Return(getSecurityGroupForVpcOutput, nil) + + securityGroupIds, err := ec2ApiHelper.GetSecurityGroupIdsForSecurityGroupNames(securityGroupNames) + + assert.Nil(t, err) + assert.Equal(t, securityGroupIds[0], securityGroup1) + assert.Equal(t, securityGroupIds[1], securityGroup2) + assert.Equal(t, len(securityGroupIds), 2) +} + +func TestEc2APIHelper_GetSecurityGroupIdsForSecurityGroupNames_FullCache(t *testing.T) { + securityGroupNameToIdMap := map[string]string{ + securityGroupName1: securityGroup1, + securityGroupName2: securityGroup2, + } + ec2ApiHelper := newEC2APIHelper(nil, clusterName, workerNodeVpcId, securityGroupNameToIdMap) + + securityGroupIds, err := ec2ApiHelper.GetSecurityGroupIdsForSecurityGroupNames(securityGroupNames) + + assert.Nil(t, err) + assert.Equal(t, securityGroupIds[0], securityGroup1) + assert.Equal(t, securityGroupIds[1], securityGroup2) + assert.Equal(t, len(securityGroupIds), 2) +} + +// TestGetSourceAcctAndArn tests that generating account ID and cluster ARN +func TestGetSourceAcctAndArn(t *testing.T) { + accountID := "123456789876" + clusterName := "test-cluster" + region := "us-west-2" + clusterARN := "arn:aws:eks:us-west-2:123456789876:cluster/test-cluster" + + roleARN := "arn:aws:iam::123456789876:role/test-cluster" + + // test correct inputs + acct, arn, err := GetSourceAcctAndArn(roleARN, region, clusterName) + assert.NoError(t, err, "no error should be returned with accurate role arn") + assert.Equal(t, accountID, acct, "correct account ID should be retrieved") + assert.Equal(t, clusterARN, arn, "correct cluster arn should be retrieved") + + region = "us-gov-west-1" + roleARN = "arn:aws-us-gov:iam::123456789876:role/test-cluster" + clusterARN = "arn:aws-us-gov:eks:us-gov-west-1:123456789876:cluster/test-cluster" + acct, arn, err = GetSourceAcctAndArn(roleARN, region, clusterName) + assert.NoError(t, err, "no error should be returned with accurate aws-us-gov partition role arn") + assert.Equal(t, accountID, acct, "correct account ID should be retrieved") + assert.Equal(t, clusterARN, arn, "correct gov partition cluster arn should be retrieved") + + // test error handling + roleARN = "arn:aws:iam::123456789876" + _, _, err = GetSourceAcctAndArn(roleARN, region, clusterName) + assert.Error(t, err, "error should be returned with inaccurate role arn is given") +} + +// TestGetSourceAcctAndArn_NoRegion test empty region +func TestGetSourceAcctAndArn_NoRegion(t *testing.T) { + clusterName := "test-cluster" + region := "" + + roleARN := "arn:aws:iam::123456789876:role/test-cluster" + + // test correct inputs + acct, arn, err := GetSourceAcctAndArn(roleARN, region, clusterName) + assert.NoError(t, err, "no error should be returned with accurate role arn") + assert.Equal(t, "", acct, "correct account ID should be retrieved") + assert.Equal(t, "", arn, "correct cluster arn should be retrieved") +} + +func TestEc2APIHelper_GetSecurityGroupIdsForSecurityGroupNames_MissingGroup(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + securityGroupNames := []string{securityGroupName1, securityGroupName2, securityGroupName3} + + getSecurityGroupsForVpcInput := &ec2.GetSecurityGroupsForVpcInput{ + Filters: []*ec2.Filter{ + { + Name: aws.String("group-name"), + Values: aws.StringSlice(securityGroupNames), + }, + }, + VpcId: &workerNodeVpcId, + } + + getSecurityGroupsForVpcOutput := &ec2.GetSecurityGroupsForVpcOutput{ + SecurityGroupForVpcs: []*ec2.SecurityGroupForVpc{ + { + GroupId: aws.String(securityGroup1), + GroupName: aws.String(securityGroupName1), + }, + }, + } + + ec2ApiHelper, mockWrapper := getMockWrapper(ctrl) + + mockWrapper.EXPECT().GetSecurityGroupsForVpc(getSecurityGroupsForVpcInput).Return(getSecurityGroupsForVpcOutput, nil) + + securityGroupIds, err := ec2ApiHelper.GetSecurityGroupIdsForSecurityGroupNames(securityGroupNames) + + assert.Nil(t, securityGroupIds) + assert.NotNil(t, err) + assert.ErrorContains(t, err, fmt.Sprintf("security groups [%s %s] not found", securityGroupName2, securityGroupName3)) +} diff --git a/pkg/aws/ec2/api/wrapper.go b/pkg/aws/ec2/api/wrapper.go index 81ca97a3..7361db1a 100644 --- a/pkg/aws/ec2/api/wrapper.go +++ b/pkg/aws/ec2/api/wrapper.go @@ -20,7 +20,7 @@ import ( "time" "github.com/aws/amazon-vpc-resource-controller-k8s/pkg/config" - "github.com/aws/amazon-vpc-resource-controller-k8s/pkg/utils" + "github.com/aws/amazon-vpc-resource-controller-k8s/pkg/httpclient" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/credentials" @@ -58,6 +58,7 @@ type EC2Wrapper interface { DescribeTrunkInterfaceAssociations(input *ec2.DescribeTrunkInterfaceAssociationsInput) (*ec2.DescribeTrunkInterfaceAssociationsOutput, error) ModifyNetworkInterfaceAttribute(input *ec2.ModifyNetworkInterfaceAttributeInput) (*ec2.ModifyNetworkInterfaceAttributeOutput, error) CreateNetworkInterfacePermission(input *ec2.CreateNetworkInterfacePermissionInput) (*ec2.CreateNetworkInterfacePermissionOutput, error) + GetSecurityGroupsForVpc(input *ec2.GetSecurityGroupsForVpcInput) (*ec2.GetSecurityGroupsForVpcOutput, error) } var ( @@ -307,6 +308,33 @@ var ( }, ) + ec2GetSecurityGroupsForVpcCallCnt = prometheus.NewCounter( + prometheus.CounterOpts{ + Name: "ec2_get_security_groups_for_vpc_api_call_count", + Help: "The number of calls made to get security groups for vpc", + }, + ) + + ec2GetSecurityGroupsForVpcErrCnt = prometheus.NewCounter( + prometheus.CounterOpts{ + Name: "ec2_get_security_groups_for_vpc_api_err_count", + Help: "The number of errors encountered while making calls to get security groups for vpc", + }, + ) + + ec2DescribeNetworkInterfacesPagesAPICallCnt = prometheus.NewCounter( + prometheus.CounterOpts{ + Name: "ec2_describe_network_interfaces_pages_api_call_count", + Help: "The number of calls made to describe network interfaces (paginated)", + }, + ) + ec2DescribeNetworkInterfacesPagesAPIErrCnt = prometheus.NewCounter( + prometheus.CounterOpts{ + Name: "ec2_describe_network_interfaces_pages_api_err_count", + Help: "The number of errors encountered while making call to describe network interfaces (paginated)", + }, + ) + prometheusRegistered = false ) @@ -344,6 +372,8 @@ func prometheusRegister() { ec2describeTrunkInterfaceAssociationAPIErrCnt, ec2modifyNetworkInterfaceAttributeAPICallCnt, ec2modifyNetworkInterfaceAttributeAPIErrCnt, + ec2GetSecurityGroupsForVpcCallCnt, + ec2GetSecurityGroupsForVpcErrCnt, ec2APICallLatencies, vpccniAvailableENICnt, vpcrcAvailableENICnt, @@ -432,7 +462,7 @@ func (e *ec2Wrapper) getInstanceSession() (instanceSession *session.Session, err } func (e *ec2Wrapper) getInstanceServiceClient(qps int, burst int, instanceSession *session.Session) (*ec2.EC2, error) { - instanceClient, err := utils.NewRateLimitedClient(qps, burst) + instanceClient, err := httpclient.NewRateLimitedClient(qps, burst) if err != nil { return nil, fmt.Errorf("failed to create reate limited client with %d qps and %d burst: %v", qps, burst, err) @@ -449,7 +479,7 @@ func (e *ec2Wrapper) getClientUsingAssumedRole(instanceRegion, roleARN, clusterN injectUserAgent(&userStsSession.Handlers) // Create a rate limited http client for the - client, err := utils.NewRateLimitedClient(qps, burst) + client, err := httpclient.NewRateLimitedClient(qps, burst) if err != nil { return nil, fmt.Errorf("failed to create reate limited client with %d qps and %d burst: %v", qps, burst, err) } @@ -465,7 +495,7 @@ func (e *ec2Wrapper) getClientUsingAssumedRole(instanceRegion, roleARN, clusterN roleARN = strings.Trim(roleARN, "\"") - sourceAcct, sourceArn, err := utils.GetSourceAcctAndArn(roleARN, region, clusterName) + sourceAcct, sourceArn, err := GetSourceAcctAndArn(roleARN, region, clusterName) if err != nil { return nil, err } @@ -789,3 +819,27 @@ func (e *ec2Wrapper) CreateNetworkInterfacePermission(input *ec2.CreateNetworkIn return output, err } + +func (e *ec2Wrapper) GetSecurityGroupsForVpc(input *ec2.GetSecurityGroupsForVpcInput) (*ec2.GetSecurityGroupsForVpcOutput, error) { + accountIdFilter := ec2.Filter{ + Name: aws.String("owner-id"), + Values: aws.StringSlice([]string{e.accountID}), + } + + start := time.Now() + input.Filters = append(input.Filters, &accountIdFilter) + // e.userServiceClient.Get + output, err := e.userServiceClient.GetSecurityGroupsForVpc(input) + ec2APICallLatencies.WithLabelValues("get_security_groups_for_vpc").Observe(timeSinceMs(start)) + + // Metric Update + ec2APICallCnt.Inc() + ec2GetSecurityGroupsForVpcCallCnt.Inc() + + if err != nil { + ec2APIErrCnt.Inc() + ec2GetSecurityGroupsForVpcErrCnt.Inc() + } + + return output, err +} diff --git a/pkg/utils/httpClient.go b/pkg/httpclient/httpClient.go similarity index 98% rename from pkg/utils/httpClient.go rename to pkg/httpclient/httpClient.go index fcd68d6f..80b3d389 100644 --- a/pkg/utils/httpClient.go +++ b/pkg/httpclient/httpClient.go @@ -11,7 +11,7 @@ // express or implied. See the License for the specific language governing // permissions and limitations under the License. -package utils +package httpclient import ( "fmt" diff --git a/pkg/utils/httpClient_test.go b/pkg/httpclient/httpClient_test.go similarity index 99% rename from pkg/utils/httpClient_test.go rename to pkg/httpclient/httpClient_test.go index 53695732..30dd993e 100644 --- a/pkg/utils/httpClient_test.go +++ b/pkg/httpclient/httpClient_test.go @@ -11,7 +11,7 @@ // express or implied. See the License for the specific language governing // permissions and limitations under the License. -package utils +package httpclient import ( "context" diff --git a/pkg/utils/helper.go b/pkg/utils/helper.go index d6b6eeac..36ea5517 100644 --- a/pkg/utils/helper.go +++ b/pkg/utils/helper.go @@ -20,10 +20,9 @@ import ( "strings" vpcresourcesv1beta1 "github.com/aws/amazon-vpc-resource-controller-k8s/apis/vpcresources/v1beta1" + "github.com/aws/amazon-vpc-resource-controller-k8s/pkg/aws/ec2/api" "github.com/aws/amazon-vpc-resource-controller-k8s/pkg/aws/vpc" - "github.com/aws/aws-sdk-go/aws/arn" - "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" @@ -57,8 +56,9 @@ type SecurityGroupForPodsAPI interface { } type SecurityGroupForPods struct { - Client client.Client - Log logr.Logger + Client client.Client + Log logr.Logger + Ec2Helper api.EC2APIHelper } // NewSecurityGroupForPodsAPI returns the SecurityGroupForPod APIs for common operations on objects @@ -102,7 +102,12 @@ func (s *SecurityGroupForPods) GetMatchingSecurityGroupForPods(pod *corev1.Pod) return nil, err } - sgList := s.filterPodSecurityGroups(sgpList, pod, sa) + sgList, err := s.filterPodSecurityGroups(sgpList, pod, sa) + if err != nil { + helperLog.Error(err, "Failed in associating pod to security groups") + return nil, err + } + if len(sgList) > 0 { helperLog.V(1).Info("Pod matched a SecurityGroupPolicy and will get the following Security Groups:", "Security Groups", sgList) @@ -113,13 +118,15 @@ func (s *SecurityGroupForPods) GetMatchingSecurityGroupForPods(pod *corev1.Pod) func (s *SecurityGroupForPods) filterPodSecurityGroups( sgpList *vpcresourcesv1beta1.SecurityGroupPolicyList, pod *corev1.Pod, - sa *corev1.ServiceAccount) []string { + sa *corev1.ServiceAccount) ([]string, error) { var sgList []string + var sgNameList []string sgpLogger := s.Log.WithValues("Pod name", pod.Name, "Pod namespace", pod.Namespace) for _, sgp := range sgpList.Items { hasPodSelector := sgp.Spec.PodSelector != nil hasSASelector := sgp.Spec.ServiceAccountSelector != nil - hasSecurityGroup := sgp.Spec.SecurityGroups.Groups != nil && len(sgp.Spec.SecurityGroups.Groups) > 0 + hasSecurityGroup := (sgp.Spec.SecurityGroups.Groups != nil && len(sgp.Spec.SecurityGroups.Groups) > 0) || + (sgp.Spec.SecurityGroups.GroupNames != nil && len(sgp.Spec.SecurityGroups.GroupNames) > 0) if (!hasPodSelector && !hasSASelector) || !hasSecurityGroup { sgpLogger.Info( @@ -155,11 +162,17 @@ func (s *SecurityGroupForPods) filterPodSecurityGroups( continue } + sgNameList = append(sgNameList, sgp.Spec.SecurityGroups.GroupNames...) sgList = append(sgList, sgp.Spec.SecurityGroups.Groups...) } + sgIdsForNames, err := s.Ec2Helper.GetSecurityGroupIdsForSecurityGroupNames(sgNameList) + if err != nil { + return nil, err + } + sgList = append(sgList, sgIdsForNames...) sgList = RemoveDuplicatedSg(sgList) - return sgList + return sgList, nil } // DeconstructIPsFromPrefix deconstructs a IPv4 prefix into a list of /32 IPv4 addresses @@ -211,24 +224,3 @@ func IsNitroInstance(instanceType string) (bool, error) { } return false, nil } - -// GetSourceAcctAndArn constructs source acct and arn and return them for use -func GetSourceAcctAndArn(roleARN, region, clusterName string) (string, string, error) { - // ARN format (https://docs.aws.amazon.com/IAM/latest/UserGuide/reference-arns.html) - // arn:partition:service:region:account-id:resource-type/resource-id - // IAM format, region is always blank - // arn:aws:iam::account:role/role-name-with-path - if !arn.IsARN(roleARN) { - return "", "", fmt.Errorf("incorrect ARN format for role %s", roleARN) - } else if region == "" { - return "", "", nil - } - - parsedArn, err := arn.Parse(roleARN) - if err != nil { - return "", "", err - } - - sourceArn := fmt.Sprintf("arn:%s:eks:%s:%s:cluster/%s", parsedArn.Partition, region, parsedArn.AccountID, clusterName) - return parsedArn.AccountID, sourceArn, nil -} diff --git a/pkg/utils/helper_test.go b/pkg/utils/helper_test.go index 34a4e4d2..ba93ed80 100644 --- a/pkg/utils/helper_test.go +++ b/pkg/utils/helper_test.go @@ -16,6 +16,7 @@ package utils import ( "testing" + "github.com/golang/mock/gomock" "github.com/samber/lo" "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -60,22 +61,76 @@ func TestCanInjectENI_CombinedSelectors(t *testing.T) { } // Combined SA selector and PodSelector - sgs := helper.filterPodSecurityGroups(sgpList, testPod, testSA) + sgs, err := helper.filterPodSecurityGroups(sgpList, testPod, testSA) + assert.Nil(t, err) assert.True(t, isEverySecurityGroupIncluded(sgs)) } // TestCanInjectENI_CombinedSelectors tests SGP with Pod selector. func TestCanInjectENI_PodSelectors(t *testing.T) { - // PodSelector alone - securityGroupPolicyPod := NewSecurityGroupPolicyPodSelector( - "test", "test_namespace", testSecurityGroupsOne) - sgpList := &vpcresourcesv1beta1.SecurityGroupPolicyList{ - TypeMeta: metav1.TypeMeta{}, - ListMeta: metav1.ListMeta{}, - Items: []vpcresourcesv1beta1.SecurityGroupPolicy{securityGroupPolicyPod}, + ctrl := gomock.NewController(t) + defer ctrl.Finish() + sgp, ec2WrapperMock := createHelper(ctrl) + + var sgNames, sgIds []string + for key, value := range testSecurityGroupNamesToIdMap { + sgNames = append(sgNames, key) + sgIds = append(sgIds, value) + } + + var tests = []struct { + testName string + sgpListInput *vpcresourcesv1beta1.SecurityGroupPolicyList + mockSecurityGroupIdsToReturn []string + expectedSgIds []string + }{ + { + testName: "OnlySecurityGroupIds", + sgpListInput: &vpcresourcesv1beta1.SecurityGroupPolicyList{ + TypeMeta: metav1.TypeMeta{}, + ListMeta: metav1.ListMeta{}, + Items: []vpcresourcesv1beta1.SecurityGroupPolicy{NewSecurityGroupPolicyPodSelector( + "test", "test_namespace", testSecurityGroupsOne, nil)}, + }, + mockSecurityGroupIdsToReturn: nil, + expectedSgIds: testSecurityGroupsOne, + }, + { + testName: "OnlySecurityGroupNames", + sgpListInput: &vpcresourcesv1beta1.SecurityGroupPolicyList{ + TypeMeta: metav1.TypeMeta{}, + ListMeta: metav1.ListMeta{}, + Items: []vpcresourcesv1beta1.SecurityGroupPolicy{NewSecurityGroupPolicyPodSelector( + "test", "test_namespace", nil, sgNames)}, + }, + mockSecurityGroupIdsToReturn: sgIds, + expectedSgIds: sgIds, + }, + { + testName: "BothSecurityGroupIdsAndSecurityGroupNames", + sgpListInput: &vpcresourcesv1beta1.SecurityGroupPolicyList{ + TypeMeta: metav1.TypeMeta{}, + ListMeta: metav1.ListMeta{}, + Items: []vpcresourcesv1beta1.SecurityGroupPolicy{NewSecurityGroupPolicyPodSelector( + "test", "test_namespace", testSecurityGroupsOne, sgNames)}, + }, + mockSecurityGroupIdsToReturn: sgIds, + expectedSgIds: append(sgIds, testSecurityGroupsOne...), + }, + } + + for _, test := range tests { + ec2WrapperMock.EXPECT().GetSecurityGroupIdsForSecurityGroupNames(test.sgpListInput.Items[0]. + Spec.SecurityGroups.GroupNames).Return(test.mockSecurityGroupIdsToReturn, nil) + sgIds, err := sgp.filterPodSecurityGroups(test.sgpListInput, testPod, testSA) + if err != nil { + t.Fatalf("Test %s failed", test.testName) + } + if !isEverySecurityGroupIncluded2(test.expectedSgIds, sgIds) { + t.Fatalf("Test Name: %s Failed. Expected SGs %v got %v. Test %s failed", test.testName, test.expectedSgIds, sgIds, + test.testName) + } } - sgs := helper.filterPodSecurityGroups(sgpList, testPod, testSA) - assert.True(t, isEverySecurityGroupIncluded(sgs)) } // TestCanInjectENI_SASelectors tests SGP with SA selector. @@ -88,7 +143,8 @@ func TestCanInjectENI_SASelectors(t *testing.T) { ListMeta: metav1.ListMeta{}, Items: []vpcresourcesv1beta1.SecurityGroupPolicy{securityGroupPolicySa}, } - sgs := helper.filterPodSecurityGroups(sgpList, testPod, testSA) + sgs, err := helper.filterPodSecurityGroups(sgpList, testPod, testSA) + assert.Nil(t, err) assert.True(t, isEverySecurityGroupIncluded(sgs)) } @@ -97,7 +153,7 @@ func TestCanInjectENI_Multi_SGPs(t *testing.T) { securityGroupPolicySa := NewSecurityGroupPolicySaSelector( name, namespace, []string{"sg-00001"}) securityGroupPolicyPod := NewSecurityGroupPolicyPodSelector( - name, namespace, []string{"sg-00002"}) + name, namespace, []string{"sg-00002"}, nil) sgsList := []vpcresourcesv1beta1.SecurityGroupPolicy{ securityGroupPolicySa, securityGroupPolicyPod} @@ -106,7 +162,8 @@ func TestCanInjectENI_Multi_SGPs(t *testing.T) { ListMeta: metav1.ListMeta{}, Items: sgsList, } - sgs := helper.filterPodSecurityGroups(sgpList, testPod, testSA) + sgs, err := helper.filterPodSecurityGroups(sgpList, testPod, testSA) + assert.Nil(t, err) assert.True(t, isEverySecurityGroupIncluded(sgs)) } @@ -120,7 +177,8 @@ func TestCanInjectENI_EmptyPodSelector(t *testing.T) { ListMeta: metav1.ListMeta{}, Items: []vpcresourcesv1beta1.SecurityGroupPolicy{securityGroupPolicyEmptyPodSelector}, } - sgs := helper.filterPodSecurityGroups(sgpList, testPod, testSA) + sgs, err := helper.filterPodSecurityGroups(sgpList, testPod, testSA) + assert.Nil(t, err) assert.True(t, isEverySecurityGroupIncluded(sgs)) } @@ -134,7 +192,8 @@ func TestCanInjectENI_EmptySASelector(t *testing.T) { ListMeta: metav1.ListMeta{}, Items: []vpcresourcesv1beta1.SecurityGroupPolicy{securityGroupPolicyEmptySaSelector}, } - sgs := helper.filterPodSecurityGroups(sgpList, testPod, testSA) + sgs, err := helper.filterPodSecurityGroups(sgpList, testPod, testSA) + assert.Nil(t, err) assert.True(t, isEverySecurityGroupIncluded(sgs)) } @@ -148,7 +207,8 @@ func TestCanInjectENI_MatchLabelSASelector(t *testing.T) { ListMeta: metav1.ListMeta{}, Items: []vpcresourcesv1beta1.SecurityGroupPolicy{securityGroupPolicyEmptySaSelector}, } - sgs := helper.filterPodSecurityGroups(sgpList, testPod, testSA) + sgs, err := helper.filterPodSecurityGroups(sgpList, testPod, testSA) + assert.Nil(t, err) assert.True(t, isEverySecurityGroupIncluded(sgs)) } @@ -162,7 +222,8 @@ func TestCanInjectENI_MatchExpressionsSASelector(t *testing.T) { ListMeta: metav1.ListMeta{}, Items: []vpcresourcesv1beta1.SecurityGroupPolicy{securityGroupPolicyEmptySaSelector}, } - sgs := helper.filterPodSecurityGroups(sgpList, testPod, testSA) + sgs, err := helper.filterPodSecurityGroups(sgpList, testPod, testSA) + assert.Nil(t, err) assert.True(t, isEverySecurityGroupIncluded(sgs)) } @@ -176,7 +237,8 @@ func TestCanInjectENI_MatchLabelPodSelector(t *testing.T) { ListMeta: metav1.ListMeta{}, Items: []vpcresourcesv1beta1.SecurityGroupPolicy{securityGroupPolicyEmptySaSelector}, } - sgs := helper.filterPodSecurityGroups(sgpList, testPod, testSA) + sgs, err := helper.filterPodSecurityGroups(sgpList, testPod, testSA) + assert.Nil(t, err) assert.True(t, isEverySecurityGroupIncluded(sgs)) } @@ -190,7 +252,8 @@ func TestCanInjectENI_MatchExpressionsPodSelector(t *testing.T) { ListMeta: metav1.ListMeta{}, Items: []vpcresourcesv1beta1.SecurityGroupPolicy{securityGroupPolicyEmptySaSelector}, } - sgs := helper.filterPodSecurityGroups(sgpList, testPod, testSA) + sgs, err := helper.filterPodSecurityGroups(sgpList, testPod, testSA) + assert.Nil(t, err) assert.True(t, isEverySecurityGroupIncluded(sgs)) } @@ -206,21 +269,23 @@ func TestCanInjectENI_MismatchedSASelector(t *testing.T) { } mismatchedSa := testSA.DeepCopy() mismatchedSa.Labels["environment"] = "dev" - sgs := helper.filterPodSecurityGroups(sgpList, testPod, mismatchedSa) + sgs, err := helper.filterPodSecurityGroups(sgpList, testPod, mismatchedSa) + assert.Nil(t, err) assert.True(t, len(sgs) == 0) } // TestEmptySecurityGroupInSGP tests empty security group groupids in SGP. func TestEmptySecurityGroupInSGP(t *testing.T) { securityGroupPolicyPod := NewSecurityGroupPolicyPodSelector( - "test", "test_namespace", testSecurityGroupsOne) + "test", "test_namespace", testSecurityGroupsOne, nil) securityGroupPolicyPod.Spec.SecurityGroups.Groups = []string{} sgpList := &vpcresourcesv1beta1.SecurityGroupPolicyList{ TypeMeta: metav1.TypeMeta{}, ListMeta: metav1.ListMeta{}, Items: []vpcresourcesv1beta1.SecurityGroupPolicy{securityGroupPolicyPod}, } - sgs := helper.filterPodSecurityGroups(sgpList, testPod, testSA) + sgs, err := helper.filterPodSecurityGroups(sgpList, testPod, testSA) + assert.Nil(t, err) assert.True(t, len(sgs) == 0) } @@ -249,6 +314,19 @@ func isEverySecurityGroupIncluded(retrievedSgs []string) bool { return true } +func isEverySecurityGroupIncluded2(expectedSgs, retrievedSgs []string) bool { + if len(expectedSgs) != len(retrievedSgs) { + return false + } + + for _, s := range retrievedSgs { + if !lo.Contains(expectedSgs, s) { + return false + } + } + return true +} + // NewSecurityGroupPolicyCombined creates a test SGP with both pod and SA selectors. func NewSecurityGroupPolicyCombined( name string, namespace string, securityGroups []string) vpcresourcesv1beta1.SecurityGroupPolicy { @@ -285,7 +363,7 @@ func NewSecurityGroupPolicyCombined( // NewSecurityGroupPolicyPodSelector creates a test SGP with only pod selector. func NewSecurityGroupPolicyPodSelector( - name string, namespace string, securityGroups []string) vpcresourcesv1beta1.SecurityGroupPolicy { + name string, namespace string, securityGroupIds []string, securityGroupNames []string) vpcresourcesv1beta1.SecurityGroupPolicy { sgp := vpcresourcesv1beta1.SecurityGroupPolicy{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{ @@ -302,7 +380,8 @@ func NewSecurityGroupPolicyPodSelector( }}, }, SecurityGroups: vpcresourcesv1beta1.GroupIds{ - Groups: securityGroups, + Groups: securityGroupIds, + GroupNames: securityGroupNames, }, }, } @@ -531,46 +610,3 @@ func TestIsNitroInstance_NonNitro(t *testing.T) { assert.NoError(t, err) assert.False(t, isNitro) } - -// TestGetSourceAcctAndArn tests that generating account ID and cluster ARN -func TestGetSourceAcctAndArn(t *testing.T) { - accountID := "123456789876" - clusterName := "test-cluster" - region := "us-west-2" - clusterARN := "arn:aws:eks:us-west-2:123456789876:cluster/test-cluster" - - roleARN := "arn:aws:iam::123456789876:role/test-cluster" - - // test correct inputs - acct, arn, err := GetSourceAcctAndArn(roleARN, region, clusterName) - assert.NoError(t, err, "no error should be returned with accurate role arn") - assert.Equal(t, accountID, acct, "correct account ID should be retrieved") - assert.Equal(t, clusterARN, arn, "correct cluster arn should be retrieved") - - region = "us-gov-west-1" - roleARN = "arn:aws-us-gov:iam::123456789876:role/test-cluster" - clusterARN = "arn:aws-us-gov:eks:us-gov-west-1:123456789876:cluster/test-cluster" - acct, arn, err = GetSourceAcctAndArn(roleARN, region, clusterName) - assert.NoError(t, err, "no error should be returned with accurate aws-us-gov partition role arn") - assert.Equal(t, accountID, acct, "correct account ID should be retrieved") - assert.Equal(t, clusterARN, arn, "correct gov partition cluster arn should be retrieved") - - // test error handling - roleARN = "arn:aws:iam::123456789876" - _, _, err = GetSourceAcctAndArn(roleARN, region, clusterName) - assert.Error(t, err, "error should be returned with inaccurate role arn is given") -} - -// TestGetSourceAcctAndArn_NoRegion test empty region -func TestGetSourceAcctAndArn_NoRegion(t *testing.T) { - clusterName := "test-cluster" - region := "" - - roleARN := "arn:aws:iam::123456789876:role/test-cluster" - - // test correct inputs - acct, arn, err := GetSourceAcctAndArn(roleARN, region, clusterName) - assert.NoError(t, err, "no error should be returned with accurate role arn") - assert.Equal(t, "", acct, "correct account ID should be retrieved") - assert.Equal(t, "", arn, "correct cluster arn should be retrieved") -} diff --git a/pkg/utils/setup_test.go b/pkg/utils/setup_test.go index aaf65034..0f65aeae 100644 --- a/pkg/utils/setup_test.go +++ b/pkg/utils/setup_test.go @@ -14,6 +14,8 @@ package utils import ( + mock_api "github.com/aws/amazon-vpc-resource-controller-k8s/mocks/amazon-vcp-resource-controller-k8s/pkg/aws/ec2/api" + "github.com/golang/mock/gomock" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -23,21 +25,32 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client/fake" vpcresourcesv1beta1 "github.com/aws/amazon-vpc-resource-controller-k8s/apis/vpcresources/v1beta1" + "github.com/aws/amazon-vpc-resource-controller-k8s/pkg/aws/ec2/api" ) var ( - testSA *corev1.ServiceAccount - testPod *corev1.Pod - testScheme *runtime.Scheme - testClient client.Client - testSecurityGroupsOne []string - testSecurityGroupsTwo []string - helper SecurityGroupForPods - name string - namespace string - saName string + testSA *corev1.ServiceAccount + testPod *corev1.Pod + testScheme *runtime.Scheme + testClient client.Client + testSecurityGroupsOne []string + testSecurityGroupsTwo []string + testSecurityGroupNamesToIdMap map[string]string + helper SecurityGroupForPods + name string + namespace string + saName string ) +// TODO: Use the mocks and actually correctly mock this +type FakeEc2Helper struct { + api.EC2APIHelper +} + +func (f *FakeEc2Helper) GetSecurityGroupIdsForSecurityGroupNames(securityGroupNames []string) ([]string, error) { + return nil, nil +} + func init() { name = "test" namespace = "test_namespace" @@ -50,6 +63,10 @@ func init() { testSecurityGroupsOne = []string{"sg-00001", "sg-00002"} testSecurityGroupsTwo = []string{"sg-00003", "sg-00004"} + testSecurityGroupNamesToIdMap = map[string]string{ + "db-sg": "sg-aaaa", + "cache-sg": "sg-bbbb", + } testClient = fake.NewClientBuilder().WithScheme(testScheme).WithObjects( NewPod(name, saName, namespace), NewPodNotForENI(name+"_NoENI", saName, namespace), @@ -60,11 +77,21 @@ func init() { ).Build() helper = SecurityGroupForPods{ - Client: testClient, - Log: ctrl.Log.WithName("testLog"), + Client: testClient, + Log: ctrl.Log.WithName("testLog"), + Ec2Helper: &FakeEc2Helper{}, } } +func createHelper(mockCtrl *gomock.Controller) (SecurityGroupForPods, *mock_api.MockEC2APIHelper) { + mockWrapper := mock_api.NewMockEC2APIHelper(mockCtrl) + return SecurityGroupForPods{ + Client: testClient, + Log: ctrl.Log.WithName("testLog"), + Ec2Helper: mockWrapper, + }, mockWrapper +} + // NewSecurityGroupPolicyOne creates a test security group policy's pointer. func NewSecurityGroupPolicyOne(name string, namespace string, securityGroups []string) *vpcresourcesv1beta1.SecurityGroupPolicy { sgp := &vpcresourcesv1beta1.SecurityGroupPolicy{