async def _create_service_token( self, service_secret: ServiceSecret ) -> Token: request = AdminTokenRequest( username=service_secret.service, token_type=TokenType.service, scopes=service_secret.scopes, ) return await self._token_service.create_token_from_admin_request( request, TokenData.internal_token(), ip_address=None )
async def test_modify(setup: SetupTest, mock_kubernetes: MockCoreV1Api, caplog: LogCaptureFixture) -> None: assert setup.config.kubernetes assert len(setup.config.kubernetes.service_secrets) >= 2 service_secret_one = setup.config.kubernetes.service_secrets[0] service_secret_two = setup.config.kubernetes.service_secrets[1] kubernetes_service = setup.factory.create_kubernetes_service() token_service = setup.factory.create_token_service() # Secret that shouldn't exist. secret = V1Secret( api_version="v1", data={"token": "bogus"}, metadata=V1ObjectMeta( labels={KUBERNETES_TOKEN_TYPE_LABEL: "service"}, name="foo", namespace="bar", ), type="Opaque", ) mock_kubernetes.create_namespaced_secret("bar", secret) # Valid secret but with a bogus token. secret = V1Secret( api_version="v1", data={"token": "bogus"}, metadata=V1ObjectMeta( labels={KUBERNETES_TOKEN_TYPE_LABEL: "service"}, name=service_secret_one.secret_name, namespace=service_secret_one.secret_namespace, ), type="Opaque", ) mock_kubernetes.create_namespaced_secret( service_secret_one.secret_namespace, secret) # Valid secret but with a nonexistent token. secret = V1Secret( api_version="v1", data={"token": token_as_base64(Token())}, metadata=V1ObjectMeta( labels={KUBERNETES_TOKEN_TYPE_LABEL: "service"}, name=service_secret_two.secret_name, namespace=service_secret_two.secret_namespace, ), type="Opaque", ) mock_kubernetes.create_namespaced_secret( service_secret_two.secret_namespace, secret) # Update the secrets. This should delete the secret that shouldn't exist # and update the two that should with fresh secrets. await kubernetes_service.update_service_secrets() await assert_kubernetes_secrets_match_config(setup, mock_kubernetes) # Check the logging. expected = [ { "event": "Deleted bar/foo secret", "level": "info", "logger": "gafaelfawr", }, { "event": "Created new service token", "key": ANY, "level": "info", "logger": "gafaelfawr", "token_scope": ",".join(service_secret_one.scopes), "token_username": service_secret_one.service, }, { "event": (f"Updated {service_secret_one.secret_namespace}" f"/{service_secret_one.secret_name} secret"), "level": "info", "logger": "gafaelfawr", "scopes": service_secret_one.scopes, "service": service_secret_one.service, }, { "event": "Created new service token", "key": ANY, "level": "info", "logger": "gafaelfawr", "token_scope": ",".join(service_secret_two.scopes), "token_username": service_secret_two.service, }, { "event": (f"Updated {service_secret_two.secret_namespace}" f"/{service_secret_two.secret_name} secret"), "level": "info", "logger": "gafaelfawr", "scopes": service_secret_two.scopes, "service": service_secret_two.service, }, ] assert [json.loads(r[2]) for r in caplog.record_tuples] == expected # Replace one secret with a valid token for the wrong service. token = await token_service.create_token_from_admin_request( AdminTokenRequest( username="******", token_type=TokenType.service, scopes=service_secret_one.scopes, ), TokenData.internal_token(), ip_address=None, ) secret = V1Secret( api_version="v1", data={"token": token_as_base64(token)}, metadata=V1ObjectMeta( labels={KUBERNETES_TOKEN_TYPE_LABEL: "service"}, name=service_secret_one.secret_name, namespace=service_secret_one.secret_namespace, ), type="Opaque", ) mock_kubernetes.delete_namespaced_secret( service_secret_one.secret_name, service_secret_one.secret_namespace) mock_kubernetes.create_namespaced_secret( service_secret_one.secret_namespace, secret) # Replace the other token with a valid token with the wrong scopes. token = await token_service.create_token_from_admin_request( AdminTokenRequest( username=service_secret_two.service, token_type=TokenType.service, scopes=["read:all"], ), TokenData.internal_token(), ip_address=None, ) secret = V1Secret( api_version="v1", data={"token": token_as_base64(token)}, metadata=V1ObjectMeta( labels={KUBERNETES_TOKEN_TYPE_LABEL: "service"}, name=service_secret_two.secret_name, namespace=service_secret_two.secret_namespace, ), type="Opaque", ) mock_kubernetes.delete_namespaced_secret( service_secret_two.secret_name, service_secret_two.secret_namespace) mock_kubernetes.create_namespaced_secret( service_secret_two.secret_namespace, secret) # Update the secrets. This should create new tokens for both. await kubernetes_service.update_service_secrets() await assert_kubernetes_secrets_match_config(setup, mock_kubernetes) # Finally, replace a secret with one with no token. secret = V1Secret( api_version="v1", data={}, metadata=V1ObjectMeta( labels={KUBERNETES_TOKEN_TYPE_LABEL: "service"}, name=service_secret_one.secret_name, namespace=service_secret_one.secret_namespace, ), type="Opaque", ) mock_kubernetes.delete_namespaced_secret( service_secret_one.secret_name, service_secret_one.secret_namespace) mock_kubernetes.create_namespaced_secret( service_secret_one.secret_namespace, secret) # Update the secrets. This should create a new token for the first secret # but not for the second. await kubernetes_service.update_service_secrets() await assert_kubernetes_secrets_match_config(setup, mock_kubernetes, is_fresh=False)
async def test_modify( factory: ComponentFactory, mock_kubernetes: MockKubernetesApi, caplog: LogCaptureFixture, ) -> None: await create_test_service_tokens(mock_kubernetes) kubernetes_service = factory.create_kubernetes_service(MagicMock()) token_service = factory.create_token_service() # Valid secret but with a bogus token. secret = V1Secret( api_version="v1", kind="Secret", data={"token": "bogus"}, metadata=V1ObjectMeta(name="gafaelfawr-secret", namespace="mobu"), type="Opaque", ) await mock_kubernetes.create_namespaced_secret("mobu", secret) # Valid secret but with a nonexistent token. secret = V1Secret( api_version="v1", kind="Secret", data={"token": token_as_base64(Token())}, metadata=V1ObjectMeta( name="gafaelfawr", namespace="nublado2", labels={ "foo": "bar", "other": "blah", }, annotations={ "argocd.argoproj.io/compare-options": "IgnoreExtraneous", "argocd.argoproj.io/sync-options": "Prune=false", }, ), type="Opaque", ) await mock_kubernetes.create_namespaced_secret("nublado2", secret) # Update the secrets. This should replace both with fresh secrets. await kubernetes_service.update_service_tokens() await assert_kubernetes_secrets_are_correct(factory, mock_kubernetes) # Check the logging. assert parse_log(caplog) == [ { "event": "Created new service token", "key": ANY, "severity": "info", "token_scope": "admin:token", "token_username": "******", }, { "event": "Updated mobu/gafaelfawr-secret secret", "scopes": ["admin:token"], "severity": "info", "service": "mobu", }, { "event": "Created new service token", "key": ANY, "severity": "info", "token_scope": "", "token_username": "******", }, { "event": "Updated nublado2/gafaelfawr secret", "scopes": [], "severity": "info", "service": "nublado-hub", }, ] # Replace one secret with a valid token for the wrong service. async with factory.session.begin(): token = await token_service.create_token_from_admin_request( AdminTokenRequest( username="******", token_type=TokenType.service, scopes=["admin:token"], ), TokenData.internal_token(), ip_address=None, ) secret = V1Secret( api_version="v1", kind="Secret", data={"token": token_as_base64(token)}, metadata=V1ObjectMeta(name="gafaelfawr-secret", namespace="mobu"), type="Opaque", ) await mock_kubernetes.replace_namespaced_secret( "gafaelfawr-secret", "mobu", secret ) # Replace the other token with a valid token with the wrong scopes. async with factory.session.begin(): token = await token_service.create_token_from_admin_request( AdminTokenRequest( username="******", token_type=TokenType.service, scopes=["read:all"], ), TokenData.internal_token(), ip_address=None, ) secret = V1Secret( api_version="v1", kind="Secret", data={"token": token_as_base64(token)}, metadata=V1ObjectMeta(name="gafaelfawr", namespace="nublado2"), type="Opaque", ) await mock_kubernetes.replace_namespaced_secret( "gafaelfawr", "nublado2", secret ) # Update the secrets. This should create new tokens for both. await kubernetes_service.update_service_tokens() await assert_kubernetes_secrets_are_correct(factory, mock_kubernetes) nublado_secret = await mock_kubernetes.read_namespaced_secret( "gafaelfawr", "nublado2" ) # Finally, replace a secret with one with no token. secret = V1Secret( api_version="v1", data={}, metadata=V1ObjectMeta(name="gafaelfawr-secret", namespace="mobu"), type="Opaque", ) await mock_kubernetes.replace_namespaced_secret( "gafaelfawr-secret", "mobu", secret ) # Update the secrets. This should create a new token for the first secret # but not for the second. await kubernetes_service.update_service_tokens() await assert_kubernetes_secrets_are_correct( factory, mock_kubernetes, is_fresh=False ) assert nublado_secret == await mock_kubernetes.read_namespaced_secret( "gafaelfawr", "nublado2" )