diff --git a/.gitignore b/.gitignore index e14a1ec..adf5b04 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *.swp *~ .idea +.vscode /docs/site bin build \ No newline at end of file diff --git a/apis/v1alpha1/ack-generate-metadata.yaml b/apis/v1alpha1/ack-generate-metadata.yaml index a873eb1..438cb02 100755 --- a/apis/v1alpha1/ack-generate-metadata.yaml +++ b/apis/v1alpha1/ack-generate-metadata.yaml @@ -1,13 +1,13 @@ ack_generate_info: - build_date: "2025-09-19T16:50:59Z" - build_hash: 6b4211163dcc34776b01da9a18217bac0f4103fd - go_version: go1.24.6 - version: v0.52.0 + build_date: "2025-09-30T23:38:44Z" + build_hash: 37562000612658e62686882f1b4b924049d1e38c + go_version: go1.25.0 + version: v0.52.0-5-g3756200 api_directory_checksum: fcb205ac280ed1b0f107a291e5ea43d93c0991e9 api_version: v1alpha1 aws_sdk_go_version: v1.32.6 generator_config_info: - file_checksum: 9e30795ffa094ac7b68fe2bcb6913b0a2d7bccba + file_checksum: ceef3af34f41f300f4d827886f35d272f50cb38c original_file_name: generator.yaml last_modification: reason: API generation diff --git a/apis/v1alpha1/generator.yaml b/apis/v1alpha1/generator.yaml index 27f288c..09ffcc9 100644 --- a/apis/v1alpha1/generator.yaml +++ b/apis/v1alpha1/generator.yaml @@ -277,6 +277,8 @@ resources: is_immutable: true compare: is_ignored: true + Thumbprints: + late_initialize: {} Tags: compare: is_ignored: true diff --git a/config/crd/bases/iam.services.k8s.aws_groups.yaml b/config/crd/bases/iam.services.k8s.aws_groups.yaml index 1f186ca..36dc7dd 100644 --- a/config/crd/bases/iam.services.k8s.aws_groups.yaml +++ b/config/crd/bases/iam.services.k8s.aws_groups.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.2 + controller-gen.kubebuilder.io/version: v0.19.0 name: groups.iam.services.k8s.aws spec: group: iam.services.k8s.aws diff --git a/config/crd/bases/iam.services.k8s.aws_instanceprofiles.yaml b/config/crd/bases/iam.services.k8s.aws_instanceprofiles.yaml index 75a59ef..32cd24a 100644 --- a/config/crd/bases/iam.services.k8s.aws_instanceprofiles.yaml +++ b/config/crd/bases/iam.services.k8s.aws_instanceprofiles.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.2 + controller-gen.kubebuilder.io/version: v0.19.0 name: instanceprofiles.iam.services.k8s.aws spec: group: iam.services.k8s.aws diff --git a/config/crd/bases/iam.services.k8s.aws_openidconnectproviders.yaml b/config/crd/bases/iam.services.k8s.aws_openidconnectproviders.yaml index 3eb872e..4a7638f 100644 --- a/config/crd/bases/iam.services.k8s.aws_openidconnectproviders.yaml +++ b/config/crd/bases/iam.services.k8s.aws_openidconnectproviders.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.2 + controller-gen.kubebuilder.io/version: v0.19.0 name: openidconnectproviders.iam.services.k8s.aws spec: group: iam.services.k8s.aws diff --git a/config/crd/bases/iam.services.k8s.aws_policies.yaml b/config/crd/bases/iam.services.k8s.aws_policies.yaml index b3571d4..974786c 100644 --- a/config/crd/bases/iam.services.k8s.aws_policies.yaml +++ b/config/crd/bases/iam.services.k8s.aws_policies.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.2 + controller-gen.kubebuilder.io/version: v0.19.0 name: policies.iam.services.k8s.aws spec: group: iam.services.k8s.aws diff --git a/config/crd/bases/iam.services.k8s.aws_roles.yaml b/config/crd/bases/iam.services.k8s.aws_roles.yaml index 092cee8..3372018 100644 --- a/config/crd/bases/iam.services.k8s.aws_roles.yaml +++ b/config/crd/bases/iam.services.k8s.aws_roles.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.2 + controller-gen.kubebuilder.io/version: v0.19.0 name: roles.iam.services.k8s.aws spec: group: iam.services.k8s.aws diff --git a/config/crd/bases/iam.services.k8s.aws_servicelinkedroles.yaml b/config/crd/bases/iam.services.k8s.aws_servicelinkedroles.yaml index f5c00af..8b0f7d8 100644 --- a/config/crd/bases/iam.services.k8s.aws_servicelinkedroles.yaml +++ b/config/crd/bases/iam.services.k8s.aws_servicelinkedroles.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.2 + controller-gen.kubebuilder.io/version: v0.19.0 name: servicelinkedroles.iam.services.k8s.aws spec: group: iam.services.k8s.aws diff --git a/config/crd/bases/iam.services.k8s.aws_users.yaml b/config/crd/bases/iam.services.k8s.aws_users.yaml index 85732b6..72ff54a 100644 --- a/config/crd/bases/iam.services.k8s.aws_users.yaml +++ b/config/crd/bases/iam.services.k8s.aws_users.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.2 + controller-gen.kubebuilder.io/version: v0.19.0 name: users.iam.services.k8s.aws spec: group: iam.services.k8s.aws diff --git a/generator.yaml b/generator.yaml index 27f288c..09ffcc9 100644 --- a/generator.yaml +++ b/generator.yaml @@ -277,6 +277,8 @@ resources: is_immutable: true compare: is_ignored: true + Thumbprints: + late_initialize: {} Tags: compare: is_ignored: true diff --git a/helm/crds/iam.services.k8s.aws_groups.yaml b/helm/crds/iam.services.k8s.aws_groups.yaml index cffb194..de2ba68 100644 --- a/helm/crds/iam.services.k8s.aws_groups.yaml +++ b/helm/crds/iam.services.k8s.aws_groups.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.2 + controller-gen.kubebuilder.io/version: v0.19.0 name: groups.iam.services.k8s.aws spec: group: iam.services.k8s.aws diff --git a/helm/crds/iam.services.k8s.aws_instanceprofiles.yaml b/helm/crds/iam.services.k8s.aws_instanceprofiles.yaml index 4655431..91f456c 100644 --- a/helm/crds/iam.services.k8s.aws_instanceprofiles.yaml +++ b/helm/crds/iam.services.k8s.aws_instanceprofiles.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.2 + controller-gen.kubebuilder.io/version: v0.19.0 name: instanceprofiles.iam.services.k8s.aws spec: group: iam.services.k8s.aws diff --git a/helm/crds/iam.services.k8s.aws_openidconnectproviders.yaml b/helm/crds/iam.services.k8s.aws_openidconnectproviders.yaml index 3eb872e..4a7638f 100644 --- a/helm/crds/iam.services.k8s.aws_openidconnectproviders.yaml +++ b/helm/crds/iam.services.k8s.aws_openidconnectproviders.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.2 + controller-gen.kubebuilder.io/version: v0.19.0 name: openidconnectproviders.iam.services.k8s.aws spec: group: iam.services.k8s.aws diff --git a/helm/crds/iam.services.k8s.aws_policies.yaml b/helm/crds/iam.services.k8s.aws_policies.yaml index 44a1226..88e738a 100644 --- a/helm/crds/iam.services.k8s.aws_policies.yaml +++ b/helm/crds/iam.services.k8s.aws_policies.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.2 + controller-gen.kubebuilder.io/version: v0.19.0 name: policies.iam.services.k8s.aws spec: group: iam.services.k8s.aws diff --git a/helm/crds/iam.services.k8s.aws_roles.yaml b/helm/crds/iam.services.k8s.aws_roles.yaml index f51446b..e1bb442 100644 --- a/helm/crds/iam.services.k8s.aws_roles.yaml +++ b/helm/crds/iam.services.k8s.aws_roles.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.2 + controller-gen.kubebuilder.io/version: v0.19.0 name: roles.iam.services.k8s.aws spec: group: iam.services.k8s.aws diff --git a/helm/crds/iam.services.k8s.aws_servicelinkedroles.yaml b/helm/crds/iam.services.k8s.aws_servicelinkedroles.yaml index f5c00af..8b0f7d8 100644 --- a/helm/crds/iam.services.k8s.aws_servicelinkedroles.yaml +++ b/helm/crds/iam.services.k8s.aws_servicelinkedroles.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.2 + controller-gen.kubebuilder.io/version: v0.19.0 name: servicelinkedroles.iam.services.k8s.aws spec: group: iam.services.k8s.aws diff --git a/helm/crds/iam.services.k8s.aws_users.yaml b/helm/crds/iam.services.k8s.aws_users.yaml index 156c55e..fc35c54 100644 --- a/helm/crds/iam.services.k8s.aws_users.yaml +++ b/helm/crds/iam.services.k8s.aws_users.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.2 + controller-gen.kubebuilder.io/version: v0.19.0 name: users.iam.services.k8s.aws spec: group: iam.services.k8s.aws diff --git a/helm/crds/services.k8s.aws_adoptedresources.yaml b/helm/crds/services.k8s.aws_adoptedresources.yaml index b7be322..d6cdd10 100644 --- a/helm/crds/services.k8s.aws_adoptedresources.yaml +++ b/helm/crds/services.k8s.aws_adoptedresources.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.2 + controller-gen.kubebuilder.io/version: v0.19.0 name: adoptedresources.services.k8s.aws spec: group: services.k8s.aws diff --git a/helm/crds/services.k8s.aws_fieldexports.yaml b/helm/crds/services.k8s.aws_fieldexports.yaml index 49b4f38..6e2c61e 100644 --- a/helm/crds/services.k8s.aws_fieldexports.yaml +++ b/helm/crds/services.k8s.aws_fieldexports.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.2 + controller-gen.kubebuilder.io/version: v0.19.0 name: fieldexports.services.k8s.aws spec: group: services.k8s.aws diff --git a/pkg/resource/open_id_connect_provider/manager.go b/pkg/resource/open_id_connect_provider/manager.go index b9d5f2d..e7da113 100644 --- a/pkg/resource/open_id_connect_provider/manager.go +++ b/pkg/resource/open_id_connect_provider/manager.go @@ -50,7 +50,7 @@ var ( // +kubebuilder:rbac:groups=iam.services.k8s.aws,resources=openidconnectproviders,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=iam.services.k8s.aws,resources=openidconnectproviders/status,verbs=get;update;patch -var lateInitializeFieldNames = []string{} +var lateInitializeFieldNames = []string{"Thumbprints"} // resourceManager is responsible for providing a consistent way to perform // CRUD operations in a backend AWS service API for Book custom resources. @@ -248,6 +248,10 @@ func (rm *resourceManager) LateInitialize( func (rm *resourceManager) incompleteLateInitialization( res acktypes.AWSResource, ) bool { + ko := rm.concreteResource(res).ko.DeepCopy() + if ko.Spec.Thumbprints == nil { + return true + } return false } @@ -257,7 +261,12 @@ func (rm *resourceManager) lateInitializeFromReadOneOutput( observed acktypes.AWSResource, latest acktypes.AWSResource, ) acktypes.AWSResource { - return latest + observedKo := rm.concreteResource(observed).ko.DeepCopy() + latestKo := rm.concreteResource(latest).ko.DeepCopy() + if observedKo.Spec.Thumbprints != nil && latestKo.Spec.Thumbprints == nil { + latestKo.Spec.Thumbprints = observedKo.Spec.Thumbprints + } + return &resource{latestKo} } // IsSynced returns true if the resource is synced. diff --git a/test/e2e/bootstrap_resources.py b/test/e2e/bootstrap_resources.py index f7263ab..6f9c614 100644 --- a/test/e2e/bootstrap_resources.py +++ b/test/e2e/bootstrap_resources.py @@ -18,12 +18,14 @@ from dataclasses import dataclass from acktest.bootstrapping import Resources from acktest.bootstrapping.iam import UserPolicies, Role +from acktest.bootstrapping.cognito_identity import UserPool from e2e import bootstrap_directory @dataclass class BootstrapResources(Resources): AdoptedPolicy: UserPolicies AdoptedRole: Role + OIDCProviderUserPool: UserPool _bootstrap_resources = None def get_bootstrap_resources(bootstrap_file_name: str = "bootstrap.pkl") -> BootstrapResources: diff --git a/test/e2e/resources/open_id_connect_provider_no_thumbprint.yaml b/test/e2e/resources/open_id_connect_provider_no_thumbprint.yaml new file mode 100644 index 0000000..9e2f0e5 --- /dev/null +++ b/test/e2e/resources/open_id_connect_provider_no_thumbprint.yaml @@ -0,0 +1,12 @@ +apiVersion: iam.services.k8s.aws/v1alpha1 +kind: OpenIDConnectProvider +metadata: + name: $OPEN_ID_CONNECT_PROVIDER_NAME +spec: + url: $URL + clientIDs: + - $CLIENT_ID + tags: + - key: $TAG_KEY + value: $TAG_VALUE + diff --git a/test/e2e/service_bootstrap.py b/test/e2e/service_bootstrap.py index 6886726..ac2d6b3 100644 --- a/test/e2e/service_bootstrap.py +++ b/test/e2e/service_bootstrap.py @@ -19,6 +19,7 @@ from acktest.bootstrapping import Resources, BootstrapFailureException from acktest.bootstrapping.iam import UserPolicies, Role +from acktest.bootstrapping.cognito_identity import UserPool from e2e import bootstrap_directory from e2e.bootstrap_resources import BootstrapResources @@ -38,7 +39,8 @@ def service_bootstrap() -> Resources: }) resources = BootstrapResources( AdoptedPolicy=UserPolicies("adopted-policies", policy_documents=[sample_policy]), - AdoptedRole=Role("adopted-role", "eks.amazonaws.com", managed_policies=["arn:aws:iam::aws:policy/AmazonSQSFullAccess", "arn:aws:iam::aws:policy/AmazonEC2FullAccess"]) + AdoptedRole=Role("adopted-role", "eks.amazonaws.com", managed_policies=["arn:aws:iam::aws:policy/AmazonSQSFullAccess", "arn:aws:iam::aws:policy/AmazonEC2FullAccess"]), + OIDCProviderUserPool=UserPool(name_prefix="oidc-test-pool") ) try: diff --git a/test/e2e/tests/test_open_id_connect_provider.py b/test/e2e/tests/test_open_id_connect_provider.py index 87e4154..b150d2f 100644 --- a/test/e2e/tests/test_open_id_connect_provider.py +++ b/test/e2e/tests/test_open_id_connect_provider.py @@ -15,6 +15,7 @@ import logging import time +import boto3 import pytest @@ -25,6 +26,7 @@ from e2e.replacement_values import REPLACEMENT_VALUES from e2e import open_id_connect_provider from e2e import tag +from e2e.bootstrap_resources import get_bootstrap_resources RESOURCE_PLURAL = "openidconnectproviders" @@ -33,6 +35,12 @@ MODIFY_WAIT_AFTER_SECONDS = 10 +def get_cognito_user_pool_well_known_url(region: str, user_pool_id: str): + """Returns the JWKS URL for token verification.""" + return f"https://cognito-idp.{region}.amazonaws.com/{user_pool_id}" + + + @pytest.fixture def oidc_provider(): # required: @@ -79,6 +87,45 @@ def oidc_provider(): except: pass +@pytest.fixture +def oidc_provider_no_thumbprint(iam_client): + oidc_provider_name = random_suffix_name("oidc-provider-ack-test", 24) + + replacements = REPLACEMENT_VALUES.copy() + replacements["OPEN_ID_CONNECT_PROVIDER_NAME"] = oidc_provider_name + + region = boto3.Session().region_name + cognito_user_pool_id = get_bootstrap_resources().OIDCProviderUserPool.user_pool_id + replacements["URL"] = get_cognito_user_pool_well_known_url(region, cognito_user_pool_id) + + replacements["CLIENT_ID"] = "phippy" + replacements["TAG_KEY"] = "tag1" + replacements["TAG_VALUE"] = "val1" + + resource_data = load_resource( + "open_id_connect_provider_no_thumbprint", + additional_replacements=replacements, + ) + + ref = k8s.CustomResourceReference( + CRD_GROUP, + CRD_VERSION, + RESOURCE_PLURAL, + oidc_provider_name, + namespace="default", + ) + k8s.create_custom_resource(ref, resource_data) + cr = k8s.wait_resource_consumed_by_controller(ref) + + yield (ref, cr) + + # Delete the OIDC provider when tests complete + try: + _, deleted = k8s.delete_custom_resource(ref, 3, 10) + assert deleted + except: + pass + def assert_url_equals_ignore_prefix(url, match): if url.startswith("https://"): @@ -90,6 +137,46 @@ def assert_url_equals_ignore_prefix(url, match): @service_marker @pytest.mark.canary class TestOpenIdConnectProvider: + + def test_without_thumbprint(self, oidc_provider_no_thumbprint): + (ref, cr) = oidc_provider_no_thumbprint + + k8s.wait_on_condition(ref, condition.CONDITION_TYPE_RESOURCE_SYNCED, "True") + cr = k8s.get_resource(ref) + condition.assert_synced(ref) + + cr = k8s.get_resource(ref) + assert cr is not None + assert "thumbprints" in cr["spec"] + assert len(cr["spec"]["thumbprints"]) > 0 + + + assert "status" in cr + assert "ackResourceMetadata" in cr["status"] + assert "arn" in cr["status"]["ackResourceMetadata"] + oidc_provider_arn = cr["status"]["ackResourceMetadata"]["arn"] + + latest_oidcp_boto3 = open_id_connect_provider.get(oidc_provider_arn) + assert latest_oidcp_boto3 is not None + assert len(latest_oidcp_boto3["ThumbprintList"]) == len(cr["spec"]["thumbprints"]) + assert set(latest_oidcp_boto3["ThumbprintList"]) == set(cr["spec"]["thumbprints"]) + + # Trigger reconcile with an update to tags. + updates = { + "spec": { + "tags": [{"key": "key2", "value": "val2"}], + }, + } + + k8s.patch_custom_resource(ref, updates) + time.sleep(MODIFY_WAIT_AFTER_SECONDS) + k8s.wait_on_condition(ref, condition.CONDITION_TYPE_RESOURCE_SYNCED, "True") + condition.assert_synced(ref) + + after_update_expected_tags = [{"Key": "key2", "Value": "val2"}] + latest_tags = open_id_connect_provider.get_tags(oidc_provider_arn) + assert tag.cleaned(latest_tags) == after_update_expected_tags + def test_crud(self, oidc_provider): (ref, cr) = oidc_provider