├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── SECURITY.md ├── dependabot-readme.md ├── dependabot.yml └── workflows │ ├── dependabot-go-mod-tidy.yml │ ├── publish-doc.yaml │ ├── validate-merge-queue-e2e-test.yaml │ ├── validate-pull-request-golangci-lint.yaml │ └── validate-pull-request-presubmit.yaml ├── .gitignore ├── .golangci.yaml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── Makefile ├── NOTICE ├── PROJECT ├── README.md ├── cmd └── aws-application-networking-k8s │ └── main.go ├── code-of-conduct.md ├── config ├── crds │ ├── bases │ │ ├── application-networking.k8s.aws_accesslogpolicies.yaml │ │ ├── application-networking.k8s.aws_iamauthpolicies.yaml │ │ ├── application-networking.k8s.aws_serviceexports.yaml │ │ ├── application-networking.k8s.aws_serviceimports.yaml │ │ ├── application-networking.k8s.aws_targetgrouppolicies.yaml │ │ ├── application-networking.k8s.aws_vpcassociationpolicies.yaml │ │ ├── externaldns.k8s.io_dnsendpoints.yaml │ │ └── gateway.networking.k8s.io_tlsroutes.yaml │ └── kustomization.yaml ├── default │ ├── kustomization.yaml │ ├── manager_auth_proxy_patch.yaml │ └── manager_config_patch.yaml ├── iam │ └── recommended-inline-policy.json ├── manager │ ├── controller_manager_config.yaml │ ├── kustomization.yaml │ └── manager.yaml ├── overlays │ └── namespaced │ │ ├── kustomization.yaml │ │ ├── role-binding.json │ │ └── role.json ├── prometheus │ ├── kustomization.yaml │ └── monitor.yaml ├── rbac │ ├── auth_proxy_client_clusterrole.yaml │ ├── auth_proxy_role.yaml │ ├── auth_proxy_role_binding.yaml │ ├── auth_proxy_service.yaml │ ├── cluster-role-binding.yaml │ ├── cluster-role-controller.yaml │ ├── kustomization.yaml │ └── service-account.yaml └── webhook │ ├── kustomization.yaml │ └── manifests.yaml ├── docgen ├── .gitignore ├── README.md ├── api-reference-base.md ├── config.json └── template │ ├── members.tpl │ ├── pkg.tpl │ ├── placeholder.go │ └── type.tpl ├── docs ├── api-reference.md ├── api-types │ ├── access-log-policy.md │ ├── gateway.md │ ├── grpc-route.md │ ├── http-route.md │ ├── iam-auth-policy.md │ ├── service-export.md │ ├── service-import.md │ ├── service.md │ ├── target-group-policy.md │ ├── tls-route.md │ └── vpc-association-policy.md ├── concepts │ ├── concepts.md │ └── overview.md ├── conformance-test.md ├── contributing │ ├── developer-cheat-sheet.md │ └── developer.md ├── faq.md ├── guides │ ├── advanced-configurations.md │ ├── custom-domain-name.md │ ├── deploy.md │ ├── environment.md │ ├── getstarted.md │ ├── grpc.md │ ├── https.md │ ├── pod-readiness-gates.md │ ├── ram-sharing.md │ ├── tls-passthrough.md │ └── upgrading-v1-0-x-to-v1-1-y.md ├── images │ ├── GatewayUserGuideFigures.pptx │ ├── accept-resource-share.png │ ├── controller.png │ ├── example1.png │ ├── example2.png │ ├── fundamentals-mapping.png │ ├── kubernetes_icon.svg │ ├── multi-cluster.png │ ├── personae.png │ ├── resource-share1.png │ ├── resource-share2.png │ ├── resource-share3.png │ ├── resource-share4.png │ ├── resource-share5.png │ ├── resource-share6.png │ ├── service-network.png │ ├── service.png │ ├── serviceimport.png │ └── tlsroute-multi-cluster.png ├── index.md └── overrides │ └── home.html ├── files ├── controller-installation │ ├── deploy-namesystem.yaml │ ├── deploy-v1.0.4.yaml │ ├── deploy-v1.0.6.yaml │ ├── deploy-v1.0.7.yaml │ ├── deploy-v1.1.0.yaml │ ├── deploy-v1.1.1.yaml │ ├── gatewayclass.yaml │ └── recommended-inline-policy.json └── examples │ ├── greeter-grpc-route.yaml │ ├── greeter-grpc-server.yaml │ ├── inventory-route-bluegreen.yaml │ ├── inventory-route.yaml │ ├── inventory-ver1.yaml │ ├── inventory-ver2-export.yaml │ ├── inventory-ver2-import.yaml │ ├── inventory-ver2.yaml │ ├── my-gateway-tls-passthrough.yaml │ ├── my-gateway.yaml │ ├── my-hotel-gateway-multi-listeners.yaml │ ├── my-hotel-gateway.yaml │ ├── my-httproute.yaml │ ├── nginx-server-tls-passthrough.yaml │ ├── parking.yaml │ ├── rate-route-path.yaml │ ├── rate-tlsroute-bluegreen.yaml │ ├── review.yaml │ ├── service-1-export.yaml │ ├── service-1-import.yaml │ ├── service-1.yaml │ ├── service-2-export.yaml │ ├── service-2-import.yaml │ ├── service-2.yaml │ ├── tls-rate1.yaml │ ├── tls-rate2-export.yaml │ ├── tls-rate2-import.yaml │ ├── tls-rate2-targetgrouppolicy.yaml │ ├── tls-rate2.yaml │ └── tlsroute-nginx.yaml ├── go.mod ├── go.sum ├── hack └── boilerplate.go.txt ├── helm ├── Chart.yaml ├── crds │ ├── application-networking.k8s.aws_accesslogpolicies.yaml │ ├── application-networking.k8s.aws_iamauthpolicies.yaml │ ├── application-networking.k8s.aws_serviceexports.yaml │ ├── application-networking.k8s.aws_serviceimports.yaml │ ├── application-networking.k8s.aws_targetgrouppolicies.yaml │ ├── application-networking.k8s.aws_vpcassociationpolicies.yaml │ ├── externaldns.k8s.io_dnsendpoints.yaml │ └── gateway.networking.k8s.io_tlsroutes.yaml ├── templates │ ├── NOTES.txt │ ├── _helpers.tpl │ ├── cluster-role-binding.yaml │ ├── cluster-role-controller.yaml │ ├── deployment.yaml │ ├── metrics-service.yaml │ ├── pdb.yaml │ ├── service-account.yaml │ └── webhook.yaml ├── values.schema.json └── values.yaml ├── mkdocs.yml ├── mocks └── controller-runtime │ └── client │ ├── client_mocks.go │ ├── generate.go │ └── record_mock.go ├── pkg ├── apis │ └── applicationnetworking │ │ └── v1alpha1 │ │ ├── accesslogpolicy_types.go │ │ ├── authpolicy_types.go │ │ ├── common.go │ │ ├── common_test.go │ │ ├── doc.go │ │ ├── generated.register.go │ │ ├── serviceexport_types.go │ │ ├── serviceimport_types.go │ │ ├── targetgrouppolicy_types.go │ │ ├── vpcassociationpolicy_types.go │ │ └── zz_generated.deepcopy.go ├── aws │ ├── cloud.go │ ├── cloud_mocks.go │ ├── cloud_test.go │ ├── metrics │ │ ├── collector.go │ │ ├── collector_test.go │ │ └── instruments.go │ └── services │ │ ├── tagging.go │ │ ├── tagging_mocks.go │ │ ├── tagging_test.go │ │ ├── vpclattice.go │ │ ├── vpclattice_mocks.go │ │ └── vpclattice_test.go ├── config │ ├── controller_config.go │ ├── controller_config_test.go │ └── ec2_metadata.go ├── controllers │ ├── accesslogpolicy_controller.go │ ├── errors.go │ ├── eventhandlers │ │ ├── gateway.go │ │ ├── gatewayclass.go │ │ ├── mapper.go │ │ ├── mapper_test.go │ │ ├── service.go │ │ ├── service_test.go │ │ ├── serviceimport.go │ │ ├── serviceimport_test.go │ │ └── vpc_association_policy.go │ ├── gateway_controller.go │ ├── gatewayclass_controller.go │ ├── iamauthpolicy_controller.go │ ├── iamauthpolicy_controller_test.go │ ├── pod_controller.go │ ├── route_controller.go │ ├── route_controller_test.go │ ├── service_controller.go │ ├── serviceexport_controller.go │ ├── serviceimport_controller.go │ ├── suite_test.go │ ├── targetgrouppolicy_controller.go │ └── vpcassociationpolicy_controller.go ├── deploy │ ├── externaldns │ │ ├── dnsendpoint_manager.go │ │ ├── dnsendpoint_manager_mock.go │ │ └── dnsendpoint_manager_test.go │ ├── lattice │ │ ├── access_log_subscription_manager.go │ │ ├── access_log_subscription_manager_mock.go │ │ ├── access_log_subscription_manager_test.go │ │ ├── access_log_subscription_synthesizer.go │ │ ├── access_log_subscription_synthesizer_test.go │ │ ├── config_test.go │ │ ├── error.go │ │ ├── iamauthpolicy_manager.go │ │ ├── iamauthpolicy_manager_test.go │ │ ├── listener_manager.go │ │ ├── listener_manager_mock.go │ │ ├── listener_manager_test.go │ │ ├── listener_synthesizer.go │ │ ├── listener_synthesizer_test.go │ │ ├── rule_manager.go │ │ ├── rule_manager_mock.go │ │ ├── rule_manager_test.go │ │ ├── rule_synthesizer.go │ │ ├── rule_synthesizer_test.go │ │ ├── service_manager.go │ │ ├── service_manager_mock.go │ │ ├── service_manager_test.go │ │ ├── service_network_manager.go │ │ ├── service_network_manager_mock.go │ │ ├── service_network_manager_test.go │ │ ├── service_synthesizer.go │ │ ├── service_synthesizer_test.go │ │ ├── target_group_manager.go │ │ ├── target_group_manager_mock.go │ │ ├── target_group_manager_test.go │ │ ├── target_group_synthesizer.go │ │ ├── target_group_synthesizer_test.go │ │ ├── targets_manager.go │ │ ├── targets_manager_mock.go │ │ ├── targets_manager_test.go │ │ ├── targets_synthesizer.go │ │ └── targets_synthesizer_test.go │ ├── stack_deployer.go │ ├── stack_deployer_test.go │ ├── stack_marshaller.go │ └── stack_schema_builder.go ├── gateway │ ├── model_build_access_log_subscription.go │ ├── model_build_access_log_subscription_test.go │ ├── model_build_lattice_service.go │ ├── model_build_lattice_service_mock.go │ ├── model_build_lattice_service_test.go │ ├── model_build_listener.go │ ├── model_build_listener_test.go │ ├── model_build_rule.go │ ├── model_build_rule_test.go │ ├── model_build_targetgroup.go │ ├── model_build_targetgroup_mock.go │ ├── model_build_targetgroup_test.go │ ├── model_build_targets.go │ └── model_build_targets_test.go ├── k8s │ ├── events.go │ ├── finalizer.go │ ├── finalizer_mock.go │ ├── policyhelper │ │ ├── kind.go │ │ ├── kind_test.go │ │ ├── policy.go │ │ └── policy_test.go │ └── utils.go ├── model │ ├── core │ │ ├── fake_resource.go │ │ ├── graph │ │ │ ├── resource_graph.go │ │ │ ├── resource_graph_test.go │ │ │ └── typological_traversal.go │ │ ├── grpcroute.go │ │ ├── grpcroute_test.go │ │ ├── httproute.go │ │ ├── httproute_test.go │ │ ├── policy.go │ │ ├── resource.go │ │ ├── route.go │ │ ├── stack.go │ │ ├── stack_id.go │ │ ├── stack_mock.go │ │ ├── stack_test.go │ │ ├── tlsroute.go │ │ ├── tlsroute_test.go │ │ ├── token_types.go │ │ └── token_types_test.go │ └── lattice │ │ ├── accesslogsubscription.go │ │ ├── iamauthpolicy.go │ │ ├── listener.go │ │ ├── rule.go │ │ ├── service.go │ │ ├── servicenetwork.go │ │ ├── targetgroup.go │ │ ├── targets.go │ │ └── types.go ├── runtime │ ├── errors.go │ └── reconcile.go ├── utils │ ├── common.go │ ├── common_test.go │ ├── condition.go │ ├── gwlog │ │ ├── actions.go │ │ ├── gwlog.go │ │ ├── metadata.go │ │ └── metadata_test.go │ ├── pod_condition.go │ ├── priority_queue.go │ ├── retry │ │ ├── backoff.go │ │ ├── errors.go │ │ └── retry.go │ └── ttime │ │ └── ttime.go └── webhook │ ├── core │ ├── context.go │ ├── context_test.go │ ├── mutating_handler.go │ ├── mutating_handler_test.go │ ├── mutator.go │ └── mutator_mocks.go │ ├── pod_mutator.go │ ├── pod_mutator_test.go │ └── pod_readiness_gate_injector.go ├── requirements.txt ├── scripts ├── gen-webhook-secret.sh ├── github-release.sh ├── lib │ ├── aws.sh │ ├── common.sh │ ├── config.sh │ ├── logging.sh │ └── login.sh ├── load_env_variables.sh ├── patch-deploy-yaml.sh ├── release-controller.sh └── setup.sh └── test ├── go.mod ├── go.sum ├── pkg └── test │ ├── context.go │ ├── framework.go │ ├── gateway.go │ ├── grpc_helloworld.go │ ├── grpcbin.go │ ├── grpcroute.go │ ├── grpcurl_runner.go │ ├── header_match_httproute.go │ ├── httpapp.go │ ├── httproute.go │ ├── httpsapp.go │ ├── metadata.go │ ├── method_match_httproute.go │ ├── nginxapp.go │ ├── path_match_httproute.go │ ├── pod_manager.go │ ├── service_export_import.go │ ├── target_group_policy.go │ ├── tlsroute.go │ └── weighted_routing_httproute.go └── suites ├── integration ├── access_log_policy_test.go ├── byoc_test.go ├── defined_target_ports_test.go ├── delete_target_groups_test.go ├── grpcroute_test.go ├── httproute_creation_test.go ├── httproute_header_match_test.go ├── httproute_method_match_test.go ├── httproute_mutation_do_not_leak_target_group_test.go ├── httproute_path_match_test.go ├── httproute_rule_priority_test.go ├── httproute_update_test.go ├── https_listener_weighted_rule_with_service_export_import_test.go ├── iamauthpolicy_test.go ├── ram_share_test.go ├── readiness_gate_test.go ├── serviceexport_mutation_test.go ├── suite_test.go ├── target_group_policy_test.go ├── tlsroute_serviceexport_test.go ├── tlsroute_test.go └── vpc_association_policy_test.go └── webhook ├── readiness_gate_inject_test.go └── suite_test.go /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Additional context** 27 | Add any other context about the problem here. 28 | 29 | **Possible Solution** 30 | If you have any ideas on how to solve the issue, please describe them here. 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 10 | **What type of PR is this?** 11 | 12 | 19 | 20 | **Which issue does this PR fix**: 21 | 22 | 23 | **What does this PR do / Why do we need it**: 24 | 25 | 26 | **If an issue # is not available please add repro steps and logs from aws-gateway-controller showing the issue**: 27 | 28 | 29 | **Testing done on this change**: 30 | 34 | 35 | **Automation added to e2e**: 36 | 40 | 41 | **Will this PR introduce any new dependencies?**: 42 | 45 | 46 | **Will this break upgrades or downgrades. Has updating a running cluster been tested?**: 47 | 48 | **Does this PR introduce any user-facing change?**: 49 | 54 | 55 | ```release-note 56 | 57 | ``` 58 | 59 | **Do all end-to-end tests successfully pass when running `make e2e-test`?**: 60 | 63 | ``` 64 | 65 | ``` 66 | 67 | By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | ## Reporting Security Issues 2 | 3 | We take all security reports seriously. 4 | When we receive such reports, 5 | we will investigate and subsequently address 6 | any potential vulnerabilities as quickly as possible. 7 | If you discover a potential security issue in this project, 8 | please notify AWS/Amazon Security via our 9 | [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/) 10 | or directly via email to [AWS Security](mailto:aws-security@amazon.com). 11 | Please do *not* create a public GitHub issue in this project. 12 | 13 | -------------------------------------------------------------------------------- /.github/dependabot-readme.md: -------------------------------------------------------------------------------- 1 | # Dependabot Configuration for Go Modules 2 | 3 | This repository is configured to use Dependabot for automated dependency updates with `go mod tidy` support. 4 | 5 | ## Configuration Files 6 | 7 | 1. `.github/dependabot.yml` - Configures Dependabot to check for Go module updates weekly 8 | 2. `.github/workflows/dependabot-go-mod-tidy.yml` - GitHub Actions workflow that runs `go mod tidy` on Dependabot PRs 9 | 10 | ## How It Works 11 | 12 | 1. Dependabot creates PRs to update Go dependencies according to the schedule in `dependabot.yml` 13 | 2. When a PR is created that modifies `go.mod` or `go.sum`, the workflow is triggered 14 | 3. The workflow checks if the PR was created by Dependabot 15 | 4. If so, it runs `go mod tidy` and commits any changes back to the PR 16 | 17 | ## Required Repository Settings 18 | 19 | For the workflow to function properly, you need to configure the repository to allow Dependabot to trigger workflows with write permissions: 20 | 21 | 1. Go to the repository on GitHub 22 | 2. Navigate to Settings > Code and automation > Actions > General 23 | 3. Scroll down to "Workflow permissions" 24 | 4. Enable "Read and write permissions" 25 | 5. Check "Allow GitHub Actions to create and approve pull requests" 26 | 6. Save changes 27 | 28 | Additionally, you need to configure Dependabot to have write access to PRs: 29 | 30 | 1. Go to the repository on GitHub 31 | 2. Navigate to Settings > Code and automation > Actions > General 32 | 3. Scroll down to "Workflow permissions from pull requests" 33 | 4. Select "Allow Dependabot to run workflows" 34 | 5. Save changes 35 | 36 | ## Troubleshooting 37 | 38 | If the workflow isn't running or isn't able to commit changes: 39 | 40 | 1. Check that the repository settings are configured as described above 41 | 2. Verify that the PR was created by Dependabot 42 | 3. Check the workflow run logs for any errors 43 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "gomod" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | ignore: 8 | - dependency-name: "*" 9 | update-types: ["version-update:semver-patch"] 10 | commit-message: 11 | prefix: "chore" 12 | include: "scope" 13 | labels: 14 | - "dependencies" 15 | - "go" 16 | 17 | - package-ecosystem: "gomod" 18 | directory: "/test" 19 | schedule: 20 | interval: "weekly" 21 | ignore: 22 | - dependency-name: "*" 23 | update-types: ["version-update:semver-patch"] 24 | commit-message: 25 | prefix: "chore" 26 | include: "scope" 27 | labels: 28 | - "dependencies" 29 | - "go" -------------------------------------------------------------------------------- /.github/workflows/dependabot-go-mod-tidy.yml: -------------------------------------------------------------------------------- 1 | name: Dependabot Go Mod Tidy 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - 'go.mod' 7 | - 'go.sum' 8 | 9 | permissions: 10 | contents: write 11 | pull-requests: write 12 | 13 | jobs: 14 | go-mod-tidy: 15 | runs-on: ubuntu-latest 16 | if: ${{ github.actor == 'dependabot[bot]' }} 17 | steps: 18 | - name: Checkout code 19 | uses: actions/checkout@v3 20 | with: 21 | ref: ${{ github.head_ref }} 22 | token: ${{ secrets.GITHUB_TOKEN }} 23 | 24 | - name: Setup Go 25 | uses: actions/setup-go@v4 26 | with: 27 | go-version: '1.x' 28 | 29 | - name: Run go mod tidy 30 | run: | 31 | go mod tidy 32 | 33 | - name: Check for changes 34 | id: git-check 35 | run: | 36 | git status --porcelain 37 | if [ -n "$(git status --porcelain)" ]; then 38 | echo "changes=true" >> $GITHUB_OUTPUT 39 | else 40 | echo "changes=false" >> $GITHUB_OUTPUT 41 | fi 42 | 43 | - name: Commit and push changes 44 | if: steps.git-check.outputs.changes == 'true' 45 | run: | 46 | git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" 47 | git config --local user.name "github-actions[bot]" 48 | git add go.mod go.sum 49 | git commit -m "Run go mod tidy for Dependabot PR" 50 | git push 51 | -------------------------------------------------------------------------------- /.github/workflows/publish-doc.yaml: -------------------------------------------------------------------------------- 1 | name: publish-doc 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - 'release-v*.*.*' 8 | permissions: 9 | contents: write 10 | jobs: 11 | publish-docs: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | with: 16 | fetch-depth: 0 17 | persist-credentials: true 18 | - name: Set up Python 19 | uses: actions/setup-python@v4 20 | with: 21 | python-version: '3.x' 22 | - name: Configure git 23 | run: | 24 | git config --global user.email "ci-bot@amazon.com" 25 | git config --global user.name "ci-bot" 26 | - name: Install dependencies 27 | run: | 28 | python -m pip install --upgrade pip 29 | pip install mkdocs-material mike 30 | - name: Deploy to Mike 31 | run: | 32 | if [[ ${{ github.ref }} == refs/heads/main ]]; then 33 | # Deploy to the mike doc version `dev` and update the `latest` alias for the main branch new git commits 34 | mike deploy dev latest --update-aliases --push 35 | mike set-default latest 36 | elif [[ ${{ github.ref }} == refs/heads/release-v* ]]; then 37 | # Deploy to the mike doc version `vx.x.x` for the new git branches `release-vx.x.x` 38 | branch_name=${{ github.ref }} 39 | version=${branch_name##refs/heads/release-} 40 | mike deploy $version --push 41 | fi 42 | -------------------------------------------------------------------------------- /.github/workflows/validate-pull-request-golangci-lint.yaml: -------------------------------------------------------------------------------- 1 | name: Validate pull request with golangci-lint before putting into queue 2 | permissions: 3 | id-token: write 4 | contents: read 5 | on: 6 | pull_request: 7 | jobs: 8 | validate: 9 | name: golangci-lint 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - run: sed -En 's/^go[[:space:]]+([[:digit:].]+)$/GO_VERSION=\1/p' go.mod >> $GITHUB_ENV 14 | - uses: actions/setup-go@v4 15 | with: 16 | go-version: ${{ env.GO_VERSION }} 17 | cache: false 18 | - name: golangci-lint 19 | uses: golangci/golangci-lint-action@v3 20 | with: 21 | version: v1.63.4 22 | args: --verbose --timeout 30m -------------------------------------------------------------------------------- /.github/workflows/validate-pull-request-presubmit.yaml: -------------------------------------------------------------------------------- 1 | name: Validate pull request with presubmit before putting into queue 2 | on: 3 | pull_request: 4 | jobs: 5 | validate: 6 | runs-on: ubuntu-latest 7 | env: 8 | K8S_VERSION: ${{ matrix.k8sVersion }} 9 | steps: 10 | - uses: actions/checkout@v3 11 | - run: sed -En 's/^go[[:space:]]+([[:digit:].]+)$/GO_VERSION=\1/p' go.mod >> $GITHUB_ENV 12 | - uses: actions/setup-python@v4 13 | with: 14 | python-version: '3.11' 15 | cache: 'pip' 16 | - run: pip install -r requirements.txt 17 | - uses: actions/setup-go@v3 18 | with: 19 | go-version: ${{ env.GO_VERSION }} 20 | check-latest: true 21 | - uses: actions/cache@v4 22 | with: 23 | path: | 24 | ~/.cache/go-build 25 | ~/go/pkg/mod 26 | ~/go/bin/ 27 | ~/.kubebuilder/bin 28 | key: ${{ runner.os }}-go-cache-${{ hashFiles('**/go.sum') }} 29 | - run: go install github.com/golang/mock/mockgen@v1.6.0 30 | - run: go install sigs.k8s.io/kustomize/kustomize/v5@v5.6.0 31 | - run: go install sigs.k8s.io/controller-runtime/tools/setup-envtest@v0.0.0-20220421205612-c162794a9b12 32 | - run: go install github.com/mattn/goveralls@b031368 33 | - run: make manifest 34 | - run: make vet 35 | - run: make test 36 | - run: make docs 37 | - name: Send coverage 38 | env: 39 | COVERALLS_TOKEN: ${{ secrets.GITHUB_TOKEN }} 40 | run: goveralls -coverprofile=coverage.out -service=github -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Keep this until the APIs upstreamed 2 | coverage.out 3 | deploy.yaml 4 | 5 | # MkDocs documentation 6 | site*/ 7 | 8 | # Multi-module go project 9 | go.work* 10 | 11 | # envFile from load_env_variables.sh 12 | **/envFile 13 | 14 | # IDE files/directories 15 | .idea/ 16 | 17 | # gomock generated prog.go 18 | pkg/aws/services/gomock_reflect_* 19 | mocks/controller-runtime/client/gomock_reflect_* 20 | pkg/**/prog.* 21 | 22 | # Image build tarballed bundles 23 | *.tgz -------------------------------------------------------------------------------- /.golangci.yaml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | linters: 3 | disable: 4 | - errcheck 5 | exclusions: 6 | generated: lax 7 | presets: 8 | - comments 9 | - common-false-positives 10 | - legacy 11 | - std-error-handling 12 | paths: 13 | - third_party$ 14 | - builtin$ 15 | - examples$ 16 | rules: 17 | - linters: 18 | - staticcheck 19 | text: "ST1012:" 20 | - linters: 21 | - staticcheck 22 | text: "QF1003:" 23 | - linters: 24 | - staticcheck 25 | text: "QF1001:" 26 | formatters: 27 | exclusions: 28 | generated: lax 29 | paths: 30 | - third_party$ 31 | - builtin$ 32 | - examples$ 33 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Build the manager binary 2 | FROM --platform=$BUILDPLATFORM golang:1.23.6 as builder 3 | 4 | WORKDIR /workspace 5 | # Copy the Go Modules manifests 6 | COPY go.mod go.mod 7 | COPY go.sum go.sum 8 | 9 | # cache deps before building and copying source so that we don't need to re-download as much 10 | # and so that source changes don't invalidate our downloaded layer 11 | #RUN go mod download 12 | RUN GOPROXY=direct go mod download 13 | 14 | # Copy the go source 15 | COPY cmd/aws-application-networking-k8s/main.go main.go 16 | COPY pkg/ pkg/ 17 | COPY scripts scripts 18 | 19 | ARG TARGETOS 20 | ARG TARGETARCH 21 | 22 | # Build 23 | RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -a -o manager main.go 24 | 25 | # Use distroless as minimal base image to package the manager binary 26 | # Refer to https://github.com/GoogleContainerTools/distroless for more details 27 | FROM --platform=$TARGETPLATFORM gcr.io/distroless/static:nonroot 28 | WORKDIR / 29 | COPY --from=builder /workspace/manager . 30 | USER 65532:65532 31 | 32 | ENTRYPOINT ["/manager"] 33 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | -------------------------------------------------------------------------------- /PROJECT: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws/aws-application-networking-k8s/84fcfae52776d9b84e1cdcc9b792c64e9907e9b7/PROJECT -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS Gateway API Controller for VPC Lattice 2 | 3 |

4 | Kubernetes logo 5 | AWS Load Balancer logo 6 |

7 | 8 | AWS Application Networking is an implementation of the Kubernetes [Gateway API](https://gateway-api.sigs.k8s.io/). This project is designed to run in a Kubernetes cluster and orchestrates AWS VPC Lattice resources using Kubernetes Custom Resource Definitions like Gateway and HTTPRoute. 9 | 10 | ## Documentation 11 | 12 | ### Website 13 | 14 | The API specification and detailed documentation is available on the project 15 | website: [https://www.gateway-api-controller.eks.aws.dev/][ghp]. 16 | 17 | ### Concepts 18 | 19 | To get started, please read through [API concepts][concepts]. These documents give the necessary background to understand the API and the use-cases it targets. 20 | 21 | ### Getting started 22 | 23 | Once you have a good understanding of the API at a higher-level, check out 24 | [getting started][getting-started] to install your first Gateway controller and try out 25 | one of the guides. 26 | 27 | ### References 28 | 29 | A complete API reference, please refer to: 30 | 31 | - [API reference][spec] 32 | - [Go docs for the package][godoc] 33 | 34 | ## Contributing 35 | 36 | Developer guide can be found on the [developer guide page][dev]. 37 | Our Kubernetes Slack channel is [#aws-gateway-api-controller][slack]. 38 | 39 | ### Code of conduct 40 | 41 | Participation in the Kubernetes community is governed by the 42 | [Kubernetes Code of Conduct](code-of-conduct.md). 43 | 44 | ## Security 45 | 46 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 47 | 48 | ## License 49 | 50 | This project is licensed under the Apache-2.0 License. 51 | 52 | [ghp]: https://www.gateway-api-controller.eks.aws.dev/ 53 | [dev]: https://www.gateway-api-controller.eks.aws.dev/contributing/developer/ 54 | [slack]: https://kubernetes.slack.com/messages/aws-gateway-api-controller 55 | [getting-started]: https://www.gateway-api-controller.eks.aws.dev/guides/getstarted/ 56 | [spec]: https://www.gateway-api-controller.eks.aws.dev/api-reference/ 57 | [concepts]: https://www.gateway-api-controller.eks.aws.dev/concepts/ 58 | [gh_release]: https://github.com/aws/aws-application-networking-k8s/releases/tag/v1.1.0 59 | [godoc]: https://www.gateway-api-controller.eks.aws.dev/ 60 | -------------------------------------------------------------------------------- /code-of-conduct.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | The AWS Gateway API Controller project follows the [CNCF Community Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md). 3 | -------------------------------------------------------------------------------- /config/crds/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - bases/gateway.networking.k8s.io_tlsroutes.yaml 5 | - bases/application-networking.k8s.aws_serviceexports.yaml 6 | - bases/application-networking.k8s.aws_serviceimports.yaml 7 | - bases/application-networking.k8s.aws_targetgrouppolicies.yaml 8 | - bases/application-networking.k8s.aws_vpcassociationpolicies.yaml 9 | - bases/application-networking.k8s.aws_accesslogpolicies.yaml 10 | - bases/application-networking.k8s.aws_iamauthpolicies.yaml 11 | -------------------------------------------------------------------------------- /config/default/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - ../crds 5 | - ../rbac 6 | - ../manager 7 | - ../webhook 8 | # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. 9 | #- ../prometheus 10 | 11 | patchesStrategicMerge: 12 | # Protect the /metrics endpoint by putting it behind auth. 13 | # If you want your controller-manager to expose the /metrics 14 | # endpoint w/o any authn/z, please comment the following line. 15 | - manager_auth_proxy_patch.yaml 16 | 17 | # Mount the controller config file for loading manager configurations 18 | # through a ComponentConfig type 19 | #- manager_config_patch.yaml 20 | -------------------------------------------------------------------------------- /config/default/manager_auth_proxy_patch.yaml: -------------------------------------------------------------------------------- 1 | # This patch inject a sidecar container which is a HTTP proxy for the 2 | # controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. 3 | apiVersion: apps/v1 4 | kind: Deployment 5 | metadata: 6 | name: gateway-api-controller 7 | namespace: aws-application-networking-system 8 | spec: 9 | template: 10 | spec: 11 | containers: 12 | - name: kube-rbac-proxy 13 | image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0 14 | args: 15 | - "--secure-listen-address=0.0.0.0:8443" 16 | - "--upstream=http://127.0.0.1:8080/" 17 | - "--logtostderr=true" 18 | - "--v=10" 19 | ports: 20 | - containerPort: 8443 21 | protocol: TCP 22 | name: https 23 | - name: manager 24 | args: 25 | - "--health-probe-bind-address=:8081" 26 | - "--metrics-bind-address=0.0.0.0:8080" 27 | - "--leader-elect" 28 | -------------------------------------------------------------------------------- /config/default/manager_config_patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: gateway-api-controller 5 | namespace: aws-application-networking-system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: manager 11 | args: 12 | - "--config=controller_manager_config.yaml" 13 | volumeMounts: 14 | - name: manager-config 15 | mountPath: /controller_manager_config.yaml 16 | subPath: controller_manager_config.yaml 17 | volumes: 18 | - name: manager-config 19 | configMap: 20 | name: manager-config 21 | -------------------------------------------------------------------------------- /config/iam/recommended-inline-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": [ 7 | "vpc-lattice:*", 8 | "ec2:DescribeVpcs", 9 | "ec2:DescribeSubnets", 10 | "ec2:DescribeTags", 11 | "ec2:DescribeSecurityGroups", 12 | "logs:CreateLogDelivery", 13 | "logs:GetLogDelivery", 14 | "logs:DescribeLogGroups", 15 | "logs:PutResourcePolicy", 16 | "logs:DescribeResourcePolicies", 17 | "logs:UpdateLogDelivery", 18 | "logs:DeleteLogDelivery", 19 | "logs:ListLogDeliveries", 20 | "tag:GetResources", 21 | "firehose:TagDeliveryStream", 22 | "s3:GetBucketPolicy", 23 | "s3:PutBucketPolicy" 24 | ], 25 | "Resource": "*" 26 | }, 27 | { 28 | "Effect" : "Allow", 29 | "Action" : "iam:CreateServiceLinkedRole", 30 | "Resource" : "arn:aws:iam::*:role/aws-service-role/vpc-lattice.amazonaws.com/AWSServiceRoleForVpcLattice", 31 | "Condition" : { 32 | "StringLike" : { 33 | "iam:AWSServiceName" : "vpc-lattice.amazonaws.com" 34 | } 35 | } 36 | }, 37 | { 38 | "Effect" : "Allow", 39 | "Action" : "iam:CreateServiceLinkedRole", 40 | "Resource" : "arn:aws:iam::*:role/aws-service-role/delivery.logs.amazonaws.com/AWSServiceRoleForLogDelivery", 41 | "Condition" : { 42 | "StringLike" : { 43 | "iam:AWSServiceName" : "delivery.logs.amazonaws.com" 44 | } 45 | } 46 | } 47 | ] 48 | } 49 | -------------------------------------------------------------------------------- /config/manager/controller_manager_config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: controller-runtime.sigs.k8s.io/v1alpha1 2 | kind: ControllerManagerConfig 3 | health: 4 | healthProbeBindAddress: :8081 5 | metrics: 6 | bindAddress: 127.0.0.1:8080 7 | webhook: 8 | port: 9443 9 | leaderElection: 10 | leaderElect: true 11 | resourceName: 6288bc47.amazon-vpc-lattice.io 12 | -------------------------------------------------------------------------------- /config/manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - manager.yaml 5 | 6 | generatorOptions: 7 | disableNameSuffixHash: true 8 | 9 | configMapGenerator: 10 | - files: 11 | - controller_manager_config.yaml 12 | name: manager-config 13 | images: 14 | - name: controller 15 | newName: public.ecr.aws/aws-application-networking-k8s/aws-gateway-controller 16 | newTag: v1.1.1 17 | -------------------------------------------------------------------------------- /config/overlays/namespaced/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - ../../default 3 | patches: 4 | - path: role.json 5 | target: 6 | group: rbac.authorization.k8s.io 7 | version: v1 8 | kind: ClusterRole 9 | name: aws-application-networking-controller 10 | - path: role-binding.json 11 | target: 12 | group: rbac.authorization.k8s.io 13 | version: v1 14 | kind: ClusterRoleBinding 15 | name: aws-application-networking-controller 16 | -------------------------------------------------------------------------------- /config/overlays/namespaced/role-binding.json: -------------------------------------------------------------------------------- 1 | [{"op": "replace", "path": "/kind", "value": "RoleBinding"}, 2 | {"op": "add", "path": "/metadata/namespace", "value": "aws-application-networking-system"}, 3 | {"op": "replace", "path": "/roleRef/kind", "value": "Role"}] 4 | -------------------------------------------------------------------------------- /config/overlays/namespaced/role.json: -------------------------------------------------------------------------------- 1 | [{"op": "replace", "path": "/kind", "value": "Role"}, 2 | {"op": "add", "path": "/metadata/namespace", "value": "aws-application-networking-system"}] 3 | -------------------------------------------------------------------------------- /config/prometheus/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - monitor.yaml 5 | -------------------------------------------------------------------------------- /config/prometheus/monitor.yaml: -------------------------------------------------------------------------------- 1 | 2 | # Prometheus Monitor Service (Metrics) 3 | apiVersion: monitoring.coreos.com/v1 4 | kind: ServiceMonitor 5 | metadata: 6 | labels: 7 | control-plane: gateway-api-controller 8 | name: gateway-api-controller-metrics-monitor 9 | namespace: aws-application-networking-system 10 | spec: 11 | endpoints: 12 | - path: /metrics 13 | port: https 14 | scheme: https 15 | bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token 16 | tlsConfig: 17 | insecureSkipVerify: true 18 | selector: 19 | matchLabels: 20 | control-plane: gateway-api-controller 21 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_client_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: metrics-reader 5 | rules: 6 | - nonResourceURLs: 7 | - "/metrics" 8 | verbs: 9 | - get 10 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: proxy-role 5 | rules: 6 | - apiGroups: 7 | - authentication.k8s.io 8 | resources: 9 | - tokenreviews 10 | verbs: 11 | - create 12 | - apiGroups: 13 | - authorization.k8s.io 14 | resources: 15 | - subjectaccessreviews 16 | verbs: 17 | - create 18 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: proxy-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: proxy-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: gateway-api-controller 12 | namespace: aws-application-networking-system 13 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | control-plane: gateway-api-controller 6 | name: gateway-api-controller-metrics-service 7 | namespace: aws-application-networking-system 8 | spec: 9 | ports: 10 | - name: https 11 | port: 8443 12 | protocol: TCP 13 | targetPort: https 14 | selector: 15 | control-plane: gateway-api-controller 16 | -------------------------------------------------------------------------------- /config/rbac/cluster-role-binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: aws-application-networking-controller 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: aws-application-networking-controller 9 | subjects: 10 | - kind: ServiceAccount 11 | name: gateway-api-controller 12 | namespace: aws-application-networking-system 13 | -------------------------------------------------------------------------------- /config/rbac/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | # All RBAC will be applied under this service account in 5 | # the deployment namespace. You may comment out this resource 6 | # if your manager will use a service account that exists at 7 | # runtime. Be sure to update RoleBinding and ClusterRoleBinding 8 | # subjects if changing service account names. 9 | - cluster-role-controller.yaml 10 | - cluster-role-binding.yaml 11 | - service-account.yaml 12 | # Comment the following 4 lines if you want to disable 13 | # the auth proxy (https://github.com/brancz/kube-rbac-proxy) 14 | # which protects your /metrics endpoint. 15 | - auth_proxy_service.yaml 16 | - auth_proxy_role.yaml 17 | - auth_proxy_role_binding.yaml 18 | - auth_proxy_client_clusterrole.yaml 19 | -------------------------------------------------------------------------------- /config/rbac/service-account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: gateway-api-controller 5 | namespace: aws-application-networking-system 6 | -------------------------------------------------------------------------------- /config/webhook/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - manifests.yaml -------------------------------------------------------------------------------- /config/webhook/manifests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: admissionregistration.k8s.io/v1 3 | kind: MutatingWebhookConfiguration 4 | metadata: 5 | name: aws-appnet-gwc-mutating-webhook 6 | webhooks: 7 | - admissionReviewVersions: 8 | - v1 9 | clientConfig: 10 | service: 11 | name: webhook-service 12 | namespace: aws-application-networking-system 13 | path: /mutate-pod 14 | failurePolicy: Fail 15 | name: mpod.gwc.k8s.aws 16 | rules: 17 | - apiGroups: 18 | - "" 19 | apiVersions: 20 | - v1 21 | operations: 22 | - CREATE 23 | resources: 24 | - pods 25 | sideEffects: None 26 | namespaceSelector: 27 | matchExpressions: 28 | - key: application-networking.k8s.aws/pod-readiness-gate-inject 29 | operator: In 30 | values: 31 | - enabled 32 | objectSelector: 33 | matchExpressions: 34 | - key: app.kubernetes.io/name 35 | operator: NotIn 36 | values: 37 | - gateway-api-controller 38 | --- 39 | apiVersion: v1 40 | kind: Service 41 | metadata: 42 | name: webhook-service 43 | namespace: aws-application-networking-system 44 | spec: 45 | ports: 46 | - port: 443 47 | targetPort: 9443 48 | selector: 49 | control-plane: gateway-api-controller -------------------------------------------------------------------------------- /docgen/.gitignore: -------------------------------------------------------------------------------- 1 | docs.html 2 | api-reference.md -------------------------------------------------------------------------------- /docgen/README.md: -------------------------------------------------------------------------------- 1 | # Generate API Docs 2 | 3 | Install doc generator 4 | 5 | ```sh 6 | go install github.com/ahmetb/gen-crd-api-reference-docs@v0.3.0 7 | ``` 8 | 9 | Generate html docs 10 | 11 | ``` sh 12 | cd docgen 13 | 14 | gen-crd-api-reference-docs -config config.json -api-dir "../pkg/apis/applicationnetworking/v1alpha1/" -out-file docs.html 15 | ``` 16 | 17 | Add generated content to template 18 | 19 | ``` sh 20 | cat api-reference-base.md docs.html > ../docs/api-reference.md 21 | ``` 22 | -------------------------------------------------------------------------------- /docgen/api-reference-base.md: -------------------------------------------------------------------------------- 1 | # API Reference 2 | 3 | This page contains the API specification for Custom Resource Definitions supported by the Application Networking K8s Controller. 4 | 5 | -------------------------------------------------------------------------------- /docgen/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "hideMemberFields": [ 3 | "TypeMeta" 4 | ], 5 | "hideTypePatterns": [ 6 | "ParseError$", 7 | "List$" 8 | ], 9 | "externalPackages": [ 10 | { 11 | "typeMatchPrefix": "^sigs\\.k8s\\.io/gateway-api/apis/v1alpha2\\.PolicyTargetReference", 12 | "docsURLTemplate": "https://gateway-api.sigs.k8s.io/geps/gep-713/?h=policytargetreference#policy-targetref-api" 13 | }, 14 | { 15 | "typeMatchPrefix": "^k8s\\.io/apimachinery/pkg/apis/meta/v1\\.Duration$", 16 | "docsURLTemplate": "https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Duration" 17 | }, 18 | { 19 | "typeMatchPrefix": "^k8s\\.io/(api|apimachinery/pkg/apis)/", 20 | "docsURLTemplate": "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#{{lower .TypeIdentifier}}-{{arrIndex .PackageSegments -1}}-{{arrIndex .PackageSegments -2}}" 21 | }, 22 | { 23 | "typeMatchPrefix": "^github\\.com/knative/pkg/apis/duck/", 24 | "docsURLTemplate": "https://pkg.go.dev/github.com/knative/pkg/apis/duck/{{arrIndex .PackageSegments -1}}#{{.TypeIdentifier}}" 25 | } 26 | ], 27 | "typeDisplayNamePrefixOverrides": { 28 | "k8s.io/api/": "Kubernetes ", 29 | "k8s.io/apimachinery/pkg/apis/": "Kubernetes " 30 | }, 31 | "markdownDisabled": false 32 | } 33 | -------------------------------------------------------------------------------- /docgen/template/members.tpl: -------------------------------------------------------------------------------- 1 | {{ define "members" }} 2 | 3 | {{ range .Members }} 4 | {{ if not (hiddenMember .)}} 5 | 6 | 7 | {{ fieldName . }}
8 | 9 | {{ if linkForType .Type }} 10 | 11 | {{ typeDisplayName .Type }} 12 | 13 | {{ else }} 14 | {{ typeDisplayName .Type }} 15 | {{ end }} 16 | 17 | 18 | 19 | {{ if fieldEmbedded . }} 20 |

21 | (Members of {{ fieldName . }} are embedded into this type.) 22 |

23 | {{ end}} 24 | 25 | {{ if isOptionalMember .}} 26 | (Optional) 27 | {{ end }} 28 | 29 | {{ safe (renderComments .CommentLines) }} 30 | 31 | {{ if and (eq (.Type.Name.Name) "ObjectMeta") }} 32 | Refer to the Kubernetes API documentation for the fields of the 33 | metadata field. 34 | {{ end }} 35 | 36 | {{ if or (eq (fieldName .) "spec") }} 37 |
38 |
39 | 40 | {{ template "members" .Type }} 41 |
42 | {{ end }} 43 | 44 | 45 | {{ end }} 46 | {{ end }} 47 | 48 | {{ end }} 49 | -------------------------------------------------------------------------------- /docgen/template/pkg.tpl: -------------------------------------------------------------------------------- 1 | {{ define "packages" }} 2 | 3 | {{ with .packages}} 4 |

Packages:

5 | 12 | {{ end}} 13 | 14 | {{ range .packages }} 15 |

16 | {{- packageDisplayName . -}} 17 |

18 | 19 | {{ with (index .GoPackages 0 )}} 20 | {{ with .DocComments }} 21 |
22 | {{ safe (renderComments .) }} 23 |
24 | {{ end }} 25 | {{ end }} 26 | 27 | Resource Types: 28 | 37 | 38 | {{ range (visibleTypes (sortedTypes .Types))}} 39 | {{ template "type" . }} 40 | {{ end }} 41 |
42 | {{ end }} 43 | 44 |

45 | Generated with gen-crd-api-reference-docs 46 | {{ with .gitCommit }} on git commit {{ . }}{{end}}. 47 |

48 | 49 | {{ end }} 50 | -------------------------------------------------------------------------------- /docgen/template/placeholder.go: -------------------------------------------------------------------------------- 1 | // Placeholder file to make Go vendor this directory properly. 2 | package template 3 | -------------------------------------------------------------------------------- /docgen/template/type.tpl: -------------------------------------------------------------------------------- 1 | {{ define "type" }} 2 | 3 |

4 | {{- .Name.Name }} 5 | {{ if eq .Kind "Alias" }}({{.Underlying}} alias){{ end -}} 6 |

7 | {{ with (typeReferences .) }} 8 |

9 | (Appears on: 10 | {{- $prev := "" -}} 11 | {{- range . -}} 12 | {{- if $prev -}}, {{ end -}} 13 | {{- $prev = . -}} 14 | {{ typeDisplayName . }} 15 | {{- end -}} 16 | ) 17 |

18 | {{ end }} 19 | 20 |
21 | {{ safe (renderComments .CommentLines) }} 22 |
23 | 24 | {{ with (constantsOfType .) }} 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | {{- range . -}} 34 | 35 | {{- /* 36 | renderComments implicitly creates a

element, so we 37 | add one to the display name as well to make the contents 38 | of the two cells align evenly. 39 | */ -}} 40 |

41 | 42 | 43 | {{- end -}} 44 | 45 |
ValueDescription

{{ typeDisplayName . }}

{{ safe (renderComments .CommentLines) }}
46 | {{ end }} 47 | 48 | {{ if .Members }} 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | {{ if isExportedType . }} 58 | 59 | 62 | 67 | 68 | 69 | 73 | 74 | 75 | {{ end }} 76 | {{ template "members" .}} 77 | 78 |
FieldDescription
60 | apiVersion
61 | string
63 | 64 | {{apiGroup .}} 65 | 66 |
70 | kind
71 | string 72 |
{{.Name.Name}}
79 | {{ end }} 80 | 81 | {{ end }} 82 | -------------------------------------------------------------------------------- /docs/api-types/service-export.md: -------------------------------------------------------------------------------- 1 | # ServiceExport API Reference 2 | 3 | ## Introduction 4 | 5 | In AWS Gateway API Controller, `ServiceExport` enables a Service for multi-cluster traffic setup. 6 | Clusters can import the exported service with [`ServiceImport`](service-import.md) resource. 7 | 8 | Internally, creating a ServiceExport creates a standalone VPC Lattice [target group](https://docs.aws.amazon.com/vpc-lattice/latest/ug/target-groups.html). 9 | Even without ServiceImports, creating ServiceExports can be useful in case you only need the target groups created; 10 | for example, using target groups in the VPC Lattice setup outside Kubernetes. 11 | 12 | Note that ServiceExport is not the implementation of Kubernetes [Multicluster Service APIs](https://multicluster.sigs.k8s.io/concepts/multicluster-services-api/); 13 | instead AWS Gateway API Controller uses its own version of the resource for the purpose of Gateway API integration. 14 | 15 | 16 | ### Limitations 17 | * The exported Service can only be used in HTTPRoutes. GRPCRoute is currently not supported. 18 | * Limited to one ServiceExport per Service. If you need multiple exports representing each port, 19 | you should create multiple Service-ServiceExport pairs. 20 | 21 | ### Annotations 22 | 23 | * `application-networking.k8s.aws/port` 24 | Represents which port of the exported Service will be used. 25 | When a comma-separated list of ports is provided, the traffic will be distributed to all ports in the list. 26 | 27 | ## Example Configuration 28 | 29 | The following yaml will create a ServiceExport for a Service named `service-1`: 30 | ```yaml 31 | apiVersion: application-networking.k8s.aws/v1alpha1 32 | kind: ServiceExport 33 | metadata: 34 | name: service-1 35 | annotations: 36 | application-networking.k8s.aws/port: "9200" 37 | spec: {} 38 | ``` 39 | -------------------------------------------------------------------------------- /docs/api-types/service-import.md: -------------------------------------------------------------------------------- 1 | # ServiceImport API Reference 2 | 3 | ## Introduction 4 | 5 | `ServiceImport` is a resource referring to a Service outside the cluster, paired with [`ServiceExport`](service-export.md) 6 | resource defined in the other clusters. 7 | 8 | Just like Services, ServiceImports can be a backend reference of HTTPRoutes. Along with the cluster's own Services 9 | (and ServiceImports from even more clusters), you can distribute the traffic across multiple VPCs and clusters. 10 | 11 | Note that ServiceImport is not the implementation of Kubernetes [Multicluster Service APIs](https://multicluster.sigs.k8s.io/concepts/multicluster-services-api/); 12 | instead AWS Gateway API Controller uses its own version of the resource for the purpose of Gateway API integration. 13 | 14 | 15 | ### Limitations 16 | * ServiceImport shares the limitations of [ServiceExport](service-export.md). 17 | * The controller only supports ServiceImport through HTTPRoute; sending traffic directly is not supported. 18 | * BackendRef ports pointing to ServiceImport is not respected. Use [port annotation](service-export.md#annotations) of ServiceExport instead. 19 | 20 | ### Annotations 21 | * `application-networking.k8s.aws/aws-eks-cluster-name` 22 | (Optional) When specified, the controller will only find target groups exported from the cluster. 23 | * `application-networking.k8s.aws/aws-vpc` 24 | (Optional) When specified, the controller will only find target groups exported from the cluster with the provided VPC ID. 25 | 26 | ## Example Configuration 27 | 28 | The following yaml imports `service-1` exported from the designated cluster. 29 | ```yaml 30 | apiVersion: application-networking.k8s.aws/v1alpha1 31 | kind: ServiceImport 32 | metadata: 33 | name: service-1 34 | annotations: 35 | application-networking.k8s.aws/aws-eks-cluster-name: "service-1-owner-cluster" 36 | application-networking.k8s.aws/aws-vpc: "service-1-owner-vpc-id" 37 | spec: {} 38 | ``` 39 | 40 | The following example HTTPRoute directs traffic to the above ServiceImport. 41 | ```yaml 42 | apiVersion: gateway.networking.k8s.io/v1 43 | kind: HTTPRoute 44 | metadata: 45 | name: my-route 46 | spec: 47 | parentRefs: 48 | - name: my-gateway 49 | sectionName: http 50 | rules: 51 | - backendRefs: 52 | - name: service-1 53 | kind: ServiceImport 54 | ``` -------------------------------------------------------------------------------- /docs/api-types/service.md: -------------------------------------------------------------------------------- 1 | # Service API Reference 2 | 3 | ## Introduction 4 | 5 | Kubernetes Services define a logical set of Pods and a policy by which to access them, often referred to as a 6 | microservice. The set of Pods targeted by a Service is determined by a `selector`. 7 | 8 | ### Service Key Features & Limitations 9 | 10 | **Features**: 11 | 12 | - **Load Balancing**: Services offer load balancing, distributing network traffic across the Pods. 13 | - **Service Types**: Supports different types, such as ClusterIP (default), NodePort, LoadBalancer, and ExternalName. 14 | - **Stable IP Address**: Each Service has a stable IP address, even when the Pods it routes to change. 15 | 16 | **Limitations**: 17 | 18 | - **Immutable Selector**: Once a Service is created, its `selector` and `type` fields cannot be updated. 19 | - **Single Namespace**: Services can only route to Pods within the same namespace. 20 | - **ExternalName Limitation**: `ExternalName` type is not supported by this controller. 21 | 22 | ## Example Configuration: 23 | 24 | ### Example 1 25 | 26 | Here's a basic example of a Service that routes traffic to Pods with the label `app=MyApp`: 27 | 28 | ```yaml 29 | apiVersion: v1 30 | kind: Service 31 | metadata: 32 | name: my-service 33 | spec: 34 | selector: 35 | app: MyApp 36 | ports: 37 | - protocol: TCP 38 | port: 80 39 | targetPort: 8080 40 | ``` 41 | 42 | In this example: 43 | 44 | - The Service is named `my-service`. 45 | - It targets Pods with the label `app=MyApp`. 46 | - The Service exposes port 80, which routes to target port 8080 on the Pods. 47 | 48 | --- 49 | 50 | This `Service` documentation provides an overview of its key features, limitations, and basic examples of configuration 51 | within Kubernetes. For detailed specifications and advanced configurations, refer to the official 52 | [Kubernetes Service documentation](https://kubernetes.io/docs/concepts/services-networking/service/). 53 | -------------------------------------------------------------------------------- /docs/api-types/tls-route.md: -------------------------------------------------------------------------------- 1 | # TLSRoute API Reference 2 | 3 | ## Introduction 4 | 5 | With integration of the Gateway API, AWS Gateway API Controller supports `TLSRoute`. 6 | This allows you to define and manage end-to-end TLS encrypted traffic routing to your Kubernetes clusters. 7 | 8 | ### Considerations 9 | 10 | - `TLSRoute` sectionName must refer to a `TLS` protocol listener with `mode: Passthrough` in the parentRefs `Gateway`. 11 | - `TLSRoute` only supports to have one rule. 12 | - `TLSRoute` does not support any rule matching condition. 13 | - The `hostnames` field with exactly one host name is required. 14 | 15 | 16 | ## Example Configuration 17 | 18 | Here is a sample configuration that demonstrates how to set up a `TLSRoute` resource to route end-to-end TLS encrypted traffic to a nginx service: 19 | 20 | ```yaml 21 | apiVersion: gateway.networking.k8s.io/v1alpha2 22 | kind: TLSRoute 23 | metadata: 24 | name: nginx-tls-route 25 | spec: 26 | hostnames: 27 | - nginx-test.my-test.com 28 | parentRefs: 29 | - name: my-hotel-tls-passthrough 30 | sectionName: tls 31 | rules: 32 | - backendRefs: 33 | - name: nginx-tls 34 | kind: Service 35 | port: 443 36 | ``` 37 | 38 | In this example: 39 | 40 | - The `TLSRoute` is named ` nginx-tls-route` and is associated with a parent gateway named `my-hotel-tls-passthrough` that has 41 | a listener section named `tls`: 42 | ``` 43 | - name: tls 44 | protocol: TLS 45 | port: 443 46 | tls: 47 | mode: Passthrough 48 | ``` 49 | - The `TLSRoute` is configured to route traffic to a k8s service named `nginx-tls` on port 443. 50 | - The `hostnames` field is set to `nginx-test.my-test.com`. The customer must use this hostname to send traffic to the nginx service. 51 | 52 | 53 | For the detailed tls passthrough traffic connectivity setup, please refer the user guide [here](../guides/tls-passthrough.md). 54 | 55 | For the detailed Gateway API `TLSRoute` resource specifications, you can refer to the 56 | Kubernetes official [documentation](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1alpha2.TLSRoute). 57 | 58 | For the VPC Lattice tls passthrough Listener configuration details, you can refer to the VPC Lattice [documentation](https://docs.aws.amazon.com/vpc-lattice/latest/ug/tls-listeners.html). -------------------------------------------------------------------------------- /docs/concepts/concepts.md: -------------------------------------------------------------------------------- 1 | # AWS Gateway API Controller User Guide 2 | 3 | As part of the VPC Lattice launch, AWS introduced the AWS Gateway API Controller ; an implementation of the Kubernetes Gateway API. Gateway API is an open-source standard interface to enable Kubernetes application networking through expressive, extensible, and role-oriented interfaces. AWS Gateway API controller extends custom resources, defined by Gateway API, which allows you to create VPC Lattice resources using Kubernetes APIs. 4 | 5 | When installed in your cluster, the controller watches for the creation of Gateway API resources such as gateways and routes and provisions corresponding Amazon VPC Lattice objects. This enables users to configure VPC Lattice Services, VPC Lattice service networks and Target Groups using Kubernetes APIs, without needing to write custom code or manage sidecar proxies. The AWS Gateway API Controller is an open-source project and fully supported by Amazon. 6 | 7 | AWS Gateway API Controller integrates with Amazon VPC Lattice and allows you to: 8 | 9 | * Handle network connectivity seamlessly between services across VPCs and accounts. 10 | * Discover VPC Lattice services spanning multiple Kubernetes clusters. 11 | * Implement a defense-in-depth strategy to secure communication between those services. 12 | * Observe the request/response traffic across the services. 13 | 14 | This documentation describes how to set up the AWS Gateway API Controller, provides example use cases, development concepts, and API references. AWS Gateway API Controller will provide developers the ability to publish services running on Kubernetes cluster and other compute platforms on AWS such as AWS Lambda or Amazon EC2. Once the AWS Gateway API controller deployed and running, you will be able to manage services for multiple Kubernetes clusters and other compute targets on AWS through the following: 15 | 16 | * **CLI**: Use `aws` and `eksctl` to create clusters and set up AWS policies. Then use `kubectl` and YAML files to set up Kubernetes objects. 17 | * **AWS Console**: View VPC Lattice assets through the VPC area of the AWS console. 18 | 19 | Integrating with the Kubernetes Gateway API provides a kubernetes-native experience for developers to create services, manage network routing and traffic behaviour without the heavy lifting managing the underlying networking infrastrcuture. This lets you work with Kubernetes service-related resources using Kubernetes APIs and custom resource definitions (CRDs) defined by the Kubernetes [networking.k8s.io specification](https://gateway-api.sigs.k8s.io/references/spec/). 20 | 21 | For more information on this technology, see [Kubernetes Gateway API](https://gateway-api.sigs.k8s.io/). -------------------------------------------------------------------------------- /docs/faq.md: -------------------------------------------------------------------------------- 1 | # Frequently Asked Questions (FAQ) 2 | 3 | 4 | 5 | **How can I get involved with AWS Gateway API Controller?** 6 | 7 | We welcome general feedback, questions, feature requests, or bug reports by creating a [Github issue](https://github.com/aws/aws-application-networking-k8s/issues/new). 8 | 9 | **Where can I find AWS Gateway API Controller releases?** 10 | 11 | AWS Gateway API Controller releases are tags of the Github repository. The [Github releases page](https://github.com/aws/aws-application-networking-k8s/releases) shows all the releases. 12 | 13 | **Which EKS CNI versions are supported?** 14 | 15 | Your AWS VPC CNI must be v1.8.0 or later to work with VPC Lattice. 16 | 17 | **Which versions of Gateway API are supported?** 18 | 19 | AWS Gateway API Controller supports Gateway API CRD bundle versions `v1.1` or greater. Not all features of Gateway API are supported - for detailed features and limitation, please refer to individual API references. Please note that users are required to install Gateway API CRDs themselves as these are no longer bundled as of release `v1.1.0`. The latest Gateway API CRDs are available [here](https://gateway-api.sigs.k8s.io/). Please [follow this installation](https://gateway-api.sigs.k8s.io/guides/#installing-gateway-api) process. -------------------------------------------------------------------------------- /docs/guides/upgrading-v1-0-x-to-v1-1-y.md: -------------------------------------------------------------------------------- 1 | # Update the AWS Gateway API Controller from v1.0.x to v1.1.y 2 | 3 | Release `v1.1.0` of the AWS Gateway API Controller is built against `v1.2` of the Gateway API spec, but the controller is also compatible with the `v1.1` Gateway API. It is _not_ compatible the `v1.0` Gateway API. 4 | 5 | Previous `v1.0.x` builds of the controller were built against `v1.0` of the Gateway API spec. This guide outlines the controller upgrade process from `v1.0.x` to `v1.1.y`. Please note that users are required to install Gateway API CRDs themselves as these are no longer bundled as of release `v1.1.0`. The latest Gateway API CRDs are available [here](https://gateway-api.sigs.k8s.io/). Please [follow this installation](https://gateway-api.sigs.k8s.io/guides/#installing-gateway-api) process. 6 | 7 | # Basic Upgrade Process 8 | 9 | 1. Back up configuration, in particular GRPCRoute objects 10 | 2. Disable `v1.0.x` controller (e.g. scale to zero) 11 | 3. Update Gateway API CRDs to `v1.1.0`, available [here](https://gateway-api.sigs.k8s.io/) 12 | 4. Deploy and launch `v1.1.y` controller version 13 | 14 | With the basic upgrade process, previously created GRPCRoutes on `v1alpha2` will automatically update to `v1` once they are reconciled by the controller. Alternatively, you can manually update your GRPCRoute versions (for example export to YAML, update version number, and apply updates). Creation of new GRPCRoutes objects using `v1alpha2` will be rejected. 15 | 16 | # Upgrading to Gateway API `v1.2` 17 | 18 | Moving to GatewayAPI `v1.2` can require an additional step as `v1alpha2` GRPCRoute objects have been removed. If GRPCRoute objects are not already on `v1`, you will need to follow steps outlined in the `v1.2.0` [release notes](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.2.0). 19 | 20 | 1. Back up configuration, in particular GRPCRoute objects 21 | 2. Disable `v1.0.x` controller (e.g. scale to zero) 22 | 3. Update Gateway API CRDs to `v1.1.0`, available [here](https://gateway-api.sigs.k8s.io/) 23 | 4. Deploy and launch `v1.1.Y` controller version 24 | 5. Take upgrade steps outlined in `v1.2.0` [Gateway API release notes](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.2.0) 25 | 6. Update Gateway API CRDs to `v1.2.0` -------------------------------------------------------------------------------- /docs/images/GatewayUserGuideFigures.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws/aws-application-networking-k8s/84fcfae52776d9b84e1cdcc9b792c64e9907e9b7/docs/images/GatewayUserGuideFigures.pptx -------------------------------------------------------------------------------- /docs/images/accept-resource-share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws/aws-application-networking-k8s/84fcfae52776d9b84e1cdcc9b792c64e9907e9b7/docs/images/accept-resource-share.png -------------------------------------------------------------------------------- /docs/images/controller.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws/aws-application-networking-k8s/84fcfae52776d9b84e1cdcc9b792c64e9907e9b7/docs/images/controller.png -------------------------------------------------------------------------------- /docs/images/example1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws/aws-application-networking-k8s/84fcfae52776d9b84e1cdcc9b792c64e9907e9b7/docs/images/example1.png -------------------------------------------------------------------------------- /docs/images/example2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws/aws-application-networking-k8s/84fcfae52776d9b84e1cdcc9b792c64e9907e9b7/docs/images/example2.png -------------------------------------------------------------------------------- /docs/images/fundamentals-mapping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws/aws-application-networking-k8s/84fcfae52776d9b84e1cdcc9b792c64e9907e9b7/docs/images/fundamentals-mapping.png -------------------------------------------------------------------------------- /docs/images/multi-cluster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws/aws-application-networking-k8s/84fcfae52776d9b84e1cdcc9b792c64e9907e9b7/docs/images/multi-cluster.png -------------------------------------------------------------------------------- /docs/images/personae.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws/aws-application-networking-k8s/84fcfae52776d9b84e1cdcc9b792c64e9907e9b7/docs/images/personae.png -------------------------------------------------------------------------------- /docs/images/resource-share1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws/aws-application-networking-k8s/84fcfae52776d9b84e1cdcc9b792c64e9907e9b7/docs/images/resource-share1.png -------------------------------------------------------------------------------- /docs/images/resource-share2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws/aws-application-networking-k8s/84fcfae52776d9b84e1cdcc9b792c64e9907e9b7/docs/images/resource-share2.png -------------------------------------------------------------------------------- /docs/images/resource-share3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws/aws-application-networking-k8s/84fcfae52776d9b84e1cdcc9b792c64e9907e9b7/docs/images/resource-share3.png -------------------------------------------------------------------------------- /docs/images/resource-share4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws/aws-application-networking-k8s/84fcfae52776d9b84e1cdcc9b792c64e9907e9b7/docs/images/resource-share4.png -------------------------------------------------------------------------------- /docs/images/resource-share5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws/aws-application-networking-k8s/84fcfae52776d9b84e1cdcc9b792c64e9907e9b7/docs/images/resource-share5.png -------------------------------------------------------------------------------- /docs/images/resource-share6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws/aws-application-networking-k8s/84fcfae52776d9b84e1cdcc9b792c64e9907e9b7/docs/images/resource-share6.png -------------------------------------------------------------------------------- /docs/images/service-network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws/aws-application-networking-k8s/84fcfae52776d9b84e1cdcc9b792c64e9907e9b7/docs/images/service-network.png -------------------------------------------------------------------------------- /docs/images/service.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws/aws-application-networking-k8s/84fcfae52776d9b84e1cdcc9b792c64e9907e9b7/docs/images/service.png -------------------------------------------------------------------------------- /docs/images/serviceimport.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws/aws-application-networking-k8s/84fcfae52776d9b84e1cdcc9b792c64e9907e9b7/docs/images/serviceimport.png -------------------------------------------------------------------------------- /docs/images/tlsroute-multi-cluster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws/aws-application-networking-k8s/84fcfae52776d9b84e1cdcc9b792c64e9907e9b7/docs/images/tlsroute-multi-cluster.png -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: AWS Gateway API Controller User Guide 3 | template: home.html 4 | exclude_search: true 5 | --- 6 | -------------------------------------------------------------------------------- /files/controller-installation/deploy-namesystem.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | labels: 5 | control-plane: gateway-api-controller 6 | name: aws-application-networking-system 7 | -------------------------------------------------------------------------------- /files/controller-installation/gatewayclass.yaml: -------------------------------------------------------------------------------- 1 | # Create a new Gateway Class for AWS VPC lattice provider 2 | apiVersion: gateway.networking.k8s.io/v1beta1 3 | kind: GatewayClass 4 | metadata: 5 | name: amazon-vpc-lattice 6 | spec: 7 | controllerName: application-networking.k8s.aws/gateway-api-controller 8 | -------------------------------------------------------------------------------- /files/controller-installation/recommended-inline-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": [ 7 | "vpc-lattice:*", 8 | "ec2:DescribeVpcs", 9 | "ec2:DescribeSubnets", 10 | "ec2:DescribeTags", 11 | "ec2:DescribeSecurityGroups", 12 | "logs:CreateLogDelivery", 13 | "logs:GetLogDelivery", 14 | "logs:DescribeLogGroups", 15 | "logs:PutResourcePolicy", 16 | "logs:DescribeResourcePolicies", 17 | "logs:UpdateLogDelivery", 18 | "logs:DeleteLogDelivery", 19 | "logs:ListLogDeliveries", 20 | "tag:GetResources", 21 | "firehose:TagDeliveryStream", 22 | "s3:GetBucketPolicy", 23 | "s3:PutBucketPolicy" 24 | ], 25 | "Resource": "*" 26 | }, 27 | { 28 | "Effect" : "Allow", 29 | "Action" : "iam:CreateServiceLinkedRole", 30 | "Resource" : "arn:aws:iam::*:role/aws-service-role/vpc-lattice.amazonaws.com/AWSServiceRoleForVpcLattice", 31 | "Condition" : { 32 | "StringLike" : { 33 | "iam:AWSServiceName" : "vpc-lattice.amazonaws.com" 34 | } 35 | } 36 | }, 37 | { 38 | "Effect" : "Allow", 39 | "Action" : "iam:CreateServiceLinkedRole", 40 | "Resource" : "arn:aws:iam::*:role/aws-service-role/delivery.logs.amazonaws.com/AWSServiceRoleForLogDelivery", 41 | "Condition" : { 42 | "StringLike" : { 43 | "iam:AWSServiceName" : "delivery.logs.amazonaws.com" 44 | } 45 | } 46 | } 47 | ] 48 | } 49 | -------------------------------------------------------------------------------- /files/examples/greeter-grpc-route.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: gateway.networking.k8s.io/v1 2 | kind: GRPCRoute 3 | metadata: 4 | name: greeter-grpc-route 5 | spec: 6 | parentRefs: 7 | - name: my-hotel 8 | sectionName: https 9 | rules: 10 | - backendRefs: 11 | - name: greeter-grpc-server 12 | kind: Service 13 | port: 443 14 | weight: 10 15 | matches: 16 | - method: 17 | service: helloworld.Greeter 18 | method: SayHello 19 | -------------------------------------------------------------------------------- /files/examples/greeter-grpc-server.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: greeter-grpc-server 5 | labels: 6 | app: greeter-grpc-server 7 | spec: 8 | replicas: 2 9 | selector: 10 | matchLabels: 11 | app: greeter-grpc-server 12 | template: 13 | metadata: 14 | labels: 15 | app: greeter-grpc-server 16 | spec: 17 | containers: 18 | - name: greeter-grpc-server 19 | image: aguilbau/hello-world-grpc:latest 20 | 21 | 22 | --- 23 | apiVersion: v1 24 | kind: Service 25 | metadata: 26 | name: greeter-grpc-server 27 | spec: 28 | selector: 29 | app: greeter-grpc-server 30 | ports: 31 | - protocol: TCP 32 | port: 443 33 | targetPort: 50051 34 | -------------------------------------------------------------------------------- /files/examples/inventory-route-bluegreen.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: gateway.networking.k8s.io/v1 2 | kind: HTTPRoute 3 | metadata: 4 | name: inventory 5 | spec: 6 | parentRefs: 7 | - name: my-hotel 8 | sectionName: http 9 | rules: 10 | - backendRefs: 11 | - name: inventory-ver1 12 | kind: Service 13 | port: 80 14 | weight: 10 15 | - name: inventory-ver2 16 | kind: ServiceImport 17 | weight: 90 18 | -------------------------------------------------------------------------------- /files/examples/inventory-route.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: gateway.networking.k8s.io/v1 2 | kind: HTTPRoute 3 | metadata: 4 | name: inventory 5 | spec: 6 | parentRefs: 7 | - name: my-hotel 8 | sectionName: http 9 | rules: 10 | - backendRefs: 11 | - name: inventory-ver1 12 | kind: Service 13 | port: 80 14 | weight: 10 15 | -------------------------------------------------------------------------------- /files/examples/inventory-ver1.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: inventory-ver1 5 | labels: 6 | app: inventory-ver1 7 | spec: 8 | replicas: 2 9 | selector: 10 | matchLabels: 11 | app: inventory-ver1 12 | template: 13 | metadata: 14 | labels: 15 | app: inventory-ver1 16 | spec: 17 | containers: 18 | - name: inventory-ver1 19 | image: public.ecr.aws/x2j8p8w7/http-server:latest 20 | env: 21 | - name: PodName 22 | value: "Inventory-ver1 handler pod" 23 | 24 | 25 | --- 26 | apiVersion: v1 27 | kind: Service 28 | metadata: 29 | name: inventory-ver1 30 | spec: 31 | selector: 32 | app: inventory-ver1 33 | ports: 34 | - protocol: TCP 35 | port: 80 36 | targetPort: 8090 37 | -------------------------------------------------------------------------------- /files/examples/inventory-ver2-export.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: application-networking.k8s.aws/v1alpha1 2 | kind: ServiceExport 3 | metadata: 4 | name: inventory-ver2 5 | annotations: 6 | application-networking.k8s.aws/federation: "amazon-vpc-lattice" 7 | -------------------------------------------------------------------------------- /files/examples/inventory-ver2-import.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: application-networking.k8s.aws/v1alpha1 2 | kind: ServiceImport 3 | metadata: 4 | name: inventory-ver2 5 | spec: 6 | type: ClusterSetIP 7 | ports: 8 | - port: 80 9 | protocol: TCP 10 | -------------------------------------------------------------------------------- /files/examples/inventory-ver2.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: inventory-ver2 5 | labels: 6 | app: inventory-ver2 7 | spec: 8 | replicas: 2 9 | selector: 10 | matchLabels: 11 | app: inventory-ver2 12 | template: 13 | metadata: 14 | labels: 15 | app: inventory-ver2 16 | spec: 17 | containers: 18 | - name: inventory-ver2 19 | image: public.ecr.aws/x2j8p8w7/http-server:latest 20 | env: 21 | - name: PodName 22 | value: "Inventory-ver2 handler pod" 23 | 24 | 25 | --- 26 | apiVersion: v1 27 | kind: Service 28 | metadata: 29 | name: inventory-ver2 30 | spec: 31 | selector: 32 | app: inventory-ver2 33 | ports: 34 | - protocol: TCP 35 | port: 80 36 | targetPort: 8090 37 | -------------------------------------------------------------------------------- /files/examples/my-gateway-tls-passthrough.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: gateway.networking.k8s.io/v1 2 | kind: Gateway 3 | metadata: 4 | name: my-hotel-tls-passthrough 5 | spec: 6 | gatewayClassName: amazon-vpc-lattice 7 | listeners: 8 | - name: http 9 | protocol: HTTP 10 | port: 80 11 | - name: tls 12 | protocol: TLS 13 | port: 443 14 | tls: 15 | mode: Passthrough -------------------------------------------------------------------------------- /files/examples/my-gateway.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: gateway.networking.k8s.io/v1 2 | kind: Gateway 3 | metadata: 4 | name: my-gateway 5 | spec: 6 | gatewayClassName: amazon-vpc-lattice 7 | listeners: 8 | - name: http 9 | protocol: HTTP 10 | port: 80 11 | -------------------------------------------------------------------------------- /files/examples/my-hotel-gateway-multi-listeners.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: gateway.networking.k8s.io/v1 2 | kind: Gateway 3 | metadata: 4 | name: my-hotel 5 | spec: 6 | gatewayClassName: amazon-vpc-lattice 7 | listeners: 8 | - name: http 9 | protocol: HTTP 10 | port: 80 11 | - name: https 12 | protocol: HTTPS 13 | port: 443 14 | -------------------------------------------------------------------------------- /files/examples/my-hotel-gateway.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: gateway.networking.k8s.io/v1 2 | kind: Gateway 3 | metadata: 4 | name: my-hotel 5 | spec: 6 | gatewayClassName: amazon-vpc-lattice 7 | listeners: 8 | - name: http 9 | protocol: HTTP 10 | port: 80 11 | -------------------------------------------------------------------------------- /files/examples/my-httproute.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: gateway.networking.k8s.io/v1 2 | kind: HTTPRoute 3 | metadata: 4 | name: my-httproute 5 | spec: 6 | parentRefs: 7 | - name: my-hotel 8 | sectionName: http 9 | rules: 10 | - backendRefs: 11 | - name: service-1 12 | kind: ServiceImport 13 | matches: 14 | - path: 15 | type: PathPrefix 16 | value: /service-1 17 | - backendRefs: 18 | - name: service-2 19 | kind: ServiceImport 20 | matches: 21 | - path: 22 | type: PathPrefix 23 | value: /service-2 24 | -------------------------------------------------------------------------------- /files/examples/nginx-server-tls-passthrough.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: nginx-tls 5 | spec: 6 | selector: 7 | matchLabels: 8 | app: nginx-tls 9 | replicas: 2 10 | template: 11 | metadata: 12 | labels: 13 | app: nginx-tls 14 | spec: 15 | containers: 16 | - name: nginx-tls 17 | image: public.ecr.aws/x2j8p8w7/lattice-test-server:latest 18 | ports: 19 | - containerPort: 443 20 | 21 | --- 22 | apiVersion: v1 23 | kind: Service 24 | metadata: 25 | name: nginx-tls 26 | spec: 27 | selector: 28 | app: nginx-tls 29 | ports: 30 | - protocol: TCP 31 | port: 443 32 | targetPort: 443 33 | 34 | -------------------------------------------------------------------------------- /files/examples/parking.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: parking 5 | labels: 6 | app: parking 7 | spec: 8 | replicas: 2 9 | selector: 10 | matchLabels: 11 | app: parking 12 | template: 13 | metadata: 14 | labels: 15 | app: parking 16 | spec: 17 | containers: 18 | - name: parking 19 | image: public.ecr.aws/x2j8p8w7/http-server:latest 20 | env: 21 | - name: PodName 22 | value: "parking handler pod" 23 | 24 | 25 | --- 26 | apiVersion: v1 27 | kind: Service 28 | metadata: 29 | name: parking 30 | spec: 31 | selector: 32 | app: parking 33 | ports: 34 | - protocol: TCP 35 | port: 80 36 | targetPort: 8090 37 | -------------------------------------------------------------------------------- /files/examples/rate-route-path.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: gateway.networking.k8s.io/v1 2 | kind: HTTPRoute 3 | metadata: 4 | name: rates 5 | spec: 6 | parentRefs: 7 | - name: my-hotel 8 | sectionName: http 9 | rules: 10 | - backendRefs: 11 | - name: parking 12 | kind: Service 13 | port: 80 14 | matches: 15 | - path: 16 | type: PathPrefix 17 | value: /parking 18 | - backendRefs: 19 | - name: review 20 | kind: Service 21 | port: 80 22 | matches: 23 | - path: 24 | type: PathPrefix 25 | value: /review 26 | -------------------------------------------------------------------------------- /files/examples/rate-tlsroute-bluegreen.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: gateway.networking.k8s.io/v1alpha2 2 | kind: TLSRoute 3 | metadata: 4 | name: rate-tls-passthrough 5 | spec: 6 | hostnames: 7 | - tls-rate.my-test.com 8 | parentRefs: 9 | - name: my-hotel-tls-passthrough 10 | sectionName: tls 11 | rules: 12 | - backendRefs: 13 | - name: tls-rate1 14 | kind: Service 15 | port: 443 16 | weight: 10 17 | - name: tls-rate2 18 | kind: ServiceImport 19 | port: 443 20 | weight: 90 -------------------------------------------------------------------------------- /files/examples/review.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: review 5 | labels: 6 | app: review 7 | spec: 8 | replicas: 2 9 | selector: 10 | matchLabels: 11 | app: review 12 | template: 13 | metadata: 14 | labels: 15 | app: review 16 | spec: 17 | containers: 18 | - name: aug24-review 19 | image: public.ecr.aws/x2j8p8w7/http-server:latest 20 | env: 21 | - name: PodName 22 | value: "review handler pod" 23 | 24 | 25 | --- 26 | apiVersion: v1 27 | kind: Service 28 | metadata: 29 | name: review 30 | spec: 31 | selector: 32 | app: review 33 | ports: 34 | - protocol: TCP 35 | port: 80 36 | targetPort: 8090 37 | -------------------------------------------------------------------------------- /files/examples/service-1-export.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: application-networking.k8s.aws/v1alpha1 2 | kind: ServiceExport 3 | metadata: 4 | name: service-1 5 | annotations: 6 | application-networking.k8s.aws/federation: "amazon-vpc-lattice" 7 | -------------------------------------------------------------------------------- /files/examples/service-1-import.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: application-networking.k8s.aws/v1alpha1 2 | kind: ServiceImport 3 | metadata: 4 | name: service-1 5 | spec: 6 | type: ClusterSetIP 7 | ports: 8 | - port: 80 9 | protocol: TCP 10 | -------------------------------------------------------------------------------- /files/examples/service-1.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: service-1 5 | labels: 6 | app: service-1 7 | spec: 8 | replicas: 2 9 | selector: 10 | matchLabels: 11 | app: service-1 12 | template: 13 | metadata: 14 | labels: 15 | app: service-1 16 | spec: 17 | containers: 18 | - name: service-1 19 | image: public.ecr.aws/x2j8p8w7/http-server:latest 20 | env: 21 | - name: PodName 22 | value: "service-1 handler pod" 23 | 24 | 25 | --- 26 | apiVersion: v1 27 | kind: Service 28 | metadata: 29 | name: service-1 30 | spec: 31 | selector: 32 | app: service-1 33 | ports: 34 | - protocol: TCP 35 | port: 80 36 | targetPort: 8090 37 | -------------------------------------------------------------------------------- /files/examples/service-2-export.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: application-networking.k8s.aws/v1alpha1 2 | kind: ServiceExport 3 | metadata: 4 | name: service-2 5 | annotations: 6 | application-networking.k8s.aws/federation: "amazon-vpc-lattice" 7 | -------------------------------------------------------------------------------- /files/examples/service-2-import.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: application-networking.k8s.aws/v1alpha1 2 | kind: ServiceImport 3 | metadata: 4 | name: service-2 5 | spec: 6 | type: ClusterSetIP 7 | ports: 8 | - port: 80 9 | protocol: TCP 10 | -------------------------------------------------------------------------------- /files/examples/service-2.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: service-2 5 | labels: 6 | app: service-2 7 | spec: 8 | replicas: 2 9 | selector: 10 | matchLabels: 11 | app: service-2 12 | template: 13 | metadata: 14 | labels: 15 | app: service-2 16 | spec: 17 | containers: 18 | - name: service-2 19 | image: public.ecr.aws/x2j8p8w7/http-server:latest 20 | env: 21 | - name: PodName 22 | value: "service-2 handler pod" 23 | 24 | 25 | --- 26 | apiVersion: v1 27 | kind: Service 28 | metadata: 29 | name: service-2 30 | spec: 31 | selector: 32 | app: service-2 33 | ports: 34 | - protocol: TCP 35 | port: 80 36 | targetPort: 8090 37 | -------------------------------------------------------------------------------- /files/examples/tls-rate1.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: tls-rate1 5 | labels: 6 | app: tls-rate1 7 | spec: 8 | replicas: 2 9 | selector: 10 | matchLabels: 11 | app: tls-rate1 12 | template: 13 | metadata: 14 | labels: 15 | app: tls-rate1 16 | spec: 17 | containers: 18 | - name: tls-rate1 19 | image: public.ecr.aws/x2j8p8w7/https-server:latest 20 | env: 21 | - name: PodName 22 | value: "tls-rate1 handler pod" 23 | 24 | 25 | --- 26 | apiVersion: v1 27 | kind: Service 28 | metadata: 29 | name: tls-rate1 30 | spec: 31 | selector: 32 | app: tls-rate1 33 | ports: 34 | - protocol: TCP 35 | port: 443 36 | targetPort: 443 -------------------------------------------------------------------------------- /files/examples/tls-rate2-export.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: application-networking.k8s.aws/v1alpha1 2 | kind: ServiceExport 3 | metadata: 4 | name: tls-rate2 5 | annotations: 6 | application-networking.k8s.aws/federation: "amazon-vpc-lattice" -------------------------------------------------------------------------------- /files/examples/tls-rate2-import.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: application-networking.k8s.aws/v1alpha1 2 | kind: ServiceImport 3 | metadata: 4 | name: tls-rate2 5 | spec: 6 | type: ClusterSetIP 7 | ports: 8 | - port: 443 9 | protocol: TCP -------------------------------------------------------------------------------- /files/examples/tls-rate2-targetgrouppolicy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: application-networking.k8s.aws/v1alpha1 2 | kind: TargetGroupPolicy 3 | metadata: 4 | name: tls-rate2 5 | spec: 6 | targetRef: 7 | group: "application-networking.k8s.aws" 8 | kind: ServiceExport 9 | name: tls-rate2 10 | protocol: TCP 11 | healthCheck: 12 | enabled: false -------------------------------------------------------------------------------- /files/examples/tls-rate2.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: tls-rate2 5 | labels: 6 | app: tls-rate2 7 | spec: 8 | replicas: 2 9 | selector: 10 | matchLabels: 11 | app: tls-rate2 12 | template: 13 | metadata: 14 | labels: 15 | app: tls-rate2 16 | spec: 17 | containers: 18 | - name: tls-rate2 19 | image: public.ecr.aws/x2j8p8w7/https-server:latest 20 | env: 21 | - name: PodName 22 | value: "tls-rate2 handler pod" 23 | 24 | 25 | --- 26 | apiVersion: v1 27 | kind: Service 28 | metadata: 29 | name: tls-rate2 30 | spec: 31 | selector: 32 | app: tls-rate2 33 | ports: 34 | - protocol: TCP 35 | port: 443 36 | targetPort: 443 -------------------------------------------------------------------------------- /files/examples/tlsroute-nginx.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: gateway.networking.k8s.io/v1alpha2 2 | kind: TLSRoute 3 | metadata: 4 | name: nginx-tls-route 5 | spec: 6 | hostnames: 7 | - nginx-test.my-test.com 8 | parentRefs: 9 | - name: my-hotel-tls-passthrough 10 | sectionName: tls 11 | rules: 12 | - backendRefs: 13 | - name: nginx-tls 14 | kind: Service 15 | port: 443 -------------------------------------------------------------------------------- /hack/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ -------------------------------------------------------------------------------- /helm/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | name: aws-gateway-controller-chart 3 | description: A Helm chart for the Gateway Controller for AWS VPC Lattice 4 | version: v1.1.1 5 | appVersion: v1.1.1 6 | home: https://github.com/aws/aws-application-networking-k8s 7 | icon: https://raw.githubusercontent.com/aws/eks-charts/master/docs/logo/aws.png 8 | sources: 9 | - https://github.com/aws/aws-application-networking-k8s 10 | maintainers: 11 | - name: AWS Application Networking Kubernetes project admins 12 | url: https://github.com/orgs/aws/teams/aws-application-networking-maintainer 13 | keywords: 14 | - aws 15 | - kubernetes 16 | - gateway-api 17 | - vpc-lattice 18 | -------------------------------------------------------------------------------- /helm/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | {{ .Chart.Name }} has been installed. 2 | This chart deploys "{{ .Values.image.repository }}:{{ .Values.image.tags }}". 3 | 4 | Check its status by running: 5 | kubectl --namespace {{ .Release.Namespace }} get pods -l "app.kubernetes.io/instance={{ .Release.Name }}" 6 | 7 | The controller is running in "{{ .Values.installScope }}" mode. 8 | -------------------------------------------------------------------------------- /helm/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* The name of the application this chart installs */}} 2 | {{- define "app.name" -}} 3 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 4 | {{- end -}} 5 | 6 | {{/* 7 | Create a default fully qualified app name. 8 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 9 | If release name contains chart name it will be used as a full name. 10 | */}} 11 | {{- define "app.fullname" -}} 12 | {{- if .Values.fullnameOverride -}} 13 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} 14 | {{- else -}} 15 | {{- $name := default .Chart.Name .Values.nameOverride -}} 16 | {{- if contains $name .Release.Name -}} 17 | {{- .Release.Name | trunc 63 | trimSuffix "-" -}} 18 | {{- else -}} 19 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 20 | {{- end -}} 21 | {{- end -}} 22 | {{- end -}} 23 | 24 | {{/* The name and version as used by the chart label */}} 25 | {{- define "chart.name-version" -}} 26 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} 27 | {{- end -}} 28 | 29 | {{/* The name of the service account to use */}} 30 | {{- define "service-account.name" -}} 31 | {{ default "default" .Values.serviceAccount.name }} 32 | {{- end -}} 33 | 34 | {{/* Import or generate certificates for webhook */}} 35 | {{- define "aws-gateway-controller.webhookTLS" -}} 36 | {{- if (and .Values.webhookTLS.caCert .Values.webhookTLS.cert .Values.webhookTLS.key) -}} 37 | caCert: {{ .Values.webhookTLS.caCert }} 38 | cert: {{ .Values.webhookTLS.cert }} 39 | key: {{ .Values.webhookTLS.key }} 40 | {{- else -}} 41 | {{- $ca := genCA "aws-gateway-controller-ca" 36500 -}} 42 | {{- $serviceDefaultName:= printf "webhook-service.%s.svc" .Release.Namespace -}} 43 | {{- $secretName := "webhook-cert" -}} 44 | {{- $altNames := list ($serviceDefaultName) (printf "%s.cluster.local" $serviceDefaultName) -}} 45 | {{- $cert := genSignedCert $serviceDefaultName nil $altNames 36500 $ca -}} 46 | caCert: {{ $ca.Cert | b64enc }} 47 | cert: {{ $cert.Cert | b64enc }} 48 | key: {{ $cert.Key | b64enc }} 49 | {{- end -}} 50 | {{- end -}} 51 | -------------------------------------------------------------------------------- /helm/templates/cluster-role-binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | {{ if eq .Values.installScope "cluster" }} 3 | kind: ClusterRoleBinding 4 | metadata: 5 | name: {{ include "app.fullname" . }} 6 | roleRef: 7 | kind: ClusterRole 8 | {{ else }} 9 | kind: RoleBinding 10 | metadata: 11 | name: {{ include "app.fullname" . }} 12 | namespace: {{ .Release.Namespace }} 13 | roleRef: 14 | kind: Role 15 | {{ end }} 16 | apiGroup: rbac.authorization.k8s.io 17 | name: {{ include "app.fullname" . }} 18 | subjects: 19 | - kind: ServiceAccount 20 | name: {{ include "service-account.name" . }} 21 | namespace: {{ .Release.Namespace }} 22 | -------------------------------------------------------------------------------- /helm/templates/metrics-service.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.metrics.service.create }} 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: {{ .Chart.Name | trunc 54 }}-metrics 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | app.kubernetes.io/name: {{ include "app.name" . }} 9 | app.kubernetes.io/instance: {{ .Release.Name }} 10 | app.kubernetes.io/managed-by: Helm 11 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 12 | k8s-app: {{ include "app.name" . }} 13 | helm.sh/chart: {{ include "chart.name-version" . }} 14 | control-plane: controller 15 | spec: 16 | selector: 17 | app.kubernetes.io/name: {{ include "app.name" . }} 18 | app.kubernetes.io/instance: {{ .Release.Name }} 19 | app.kubernetes.io/managed-by: Helm 20 | k8s-app: {{ include "app.name" . }} 21 | {{- range $key, $value := .Values.deployment.labels }} 22 | {{ $key }}: {{ $value | quote }} 23 | {{- end }} 24 | type: {{ .Values.metrics.service.type }} 25 | ports: 26 | - name: metricsport 27 | port: 8080 28 | targetPort: http 29 | protocol: TCP 30 | {{- end }} 31 | -------------------------------------------------------------------------------- /helm/templates/pdb.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.pdb.enabled -}} 2 | apiVersion: policy/v1 3 | kind: PodDisruptionBudget 4 | metadata: 5 | name: {{ include "app.fullname" . }} 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | app.kubernetes.io/name: {{ include "app.name" . }} 9 | app.kubernetes.io/instance: {{ .Release.Name }} 10 | app.kubernetes.io/managed-by: Helm 11 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 12 | k8s-app: {{ include "app.name" . }} 13 | helm.sh/chart: {{ include "chart.name-version" . }} 14 | control-plane: controller 15 | spec: 16 | selector: 17 | matchLabels: 18 | app.kubernetes.io/name: {{ include "app.name" . }} 19 | app.kubernetes.io/instance: {{ .Release.Name }} 20 | {{- with .Values.pdb.minAvailable }} 21 | minAvailable: {{ . }} 22 | {{- end }} 23 | {{- with .Values.pdb.maxUnavailable }} 24 | maxUnavailable: {{ . }} 25 | {{- end }} 26 | {{- end -}} 27 | -------------------------------------------------------------------------------- /helm/templates/service-account.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create }} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: {{ include "app.name" . }} 7 | app.kubernetes.io/instance: {{ .Release.Name }} 8 | app.kubernetes.io/managed-by: Helm 9 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 10 | k8s-app: {{ include "app.name" . }} 11 | helm.sh/chart: {{ include "chart.name-version" . }} 12 | name: {{ include "service-account.name" . }} 13 | namespace: {{ .Release.Namespace }} 14 | annotations: 15 | {{- range $key, $value := .Values.serviceAccount.annotations }} 16 | {{ $key }}: {{ $value | quote }} 17 | {{- end }} 18 | {{- end }} 19 | -------------------------------------------------------------------------------- /helm/templates/webhook.yaml: -------------------------------------------------------------------------------- 1 | {{ $tls := fromYaml ( include "aws-gateway-controller.webhookTLS" . ) }} 2 | --- 3 | apiVersion: admissionregistration.k8s.io/v1 4 | kind: MutatingWebhookConfiguration 5 | metadata: 6 | name: aws-appnet-gwc-mutating-webhook 7 | webhooks: 8 | - admissionReviewVersions: 9 | - v1 10 | clientConfig: 11 | caBundle: {{ $tls.caCert }} 12 | service: 13 | name: webhook-service 14 | namespace: {{ .Release.Namespace }} 15 | path: /mutate-pod 16 | failurePolicy: Fail 17 | name: mpod.gwc.k8s.aws 18 | rules: 19 | - apiGroups: 20 | - "" 21 | apiVersions: 22 | - v1 23 | operations: 24 | - CREATE 25 | resources: 26 | - pods 27 | sideEffects: None 28 | namespaceSelector: 29 | matchExpressions: 30 | - key: application-networking.k8s.aws/pod-readiness-gate-inject 31 | operator: In 32 | values: 33 | - enabled 34 | objectSelector: 35 | matchExpressions: 36 | - key: app.kubernetes.io/name 37 | operator: NotIn 38 | values: 39 | - gateway-api-controller 40 | --- 41 | apiVersion: v1 42 | kind: Service 43 | metadata: 44 | name: webhook-service 45 | namespace: {{ .Release.Namespace }} 46 | spec: 47 | ports: 48 | - port: 443 49 | targetPort: webhook-server 50 | selector: 51 | control-plane: gateway-api-controller 52 | --- 53 | apiVersion: v1 54 | kind: Secret 55 | metadata: 56 | name: webhook-cert 57 | namespace: {{ .Release.Namespace }} 58 | type: kubernetes.io/tls 59 | data: 60 | ca.crt: {{ $tls.caCert }} 61 | tls.crt: {{ $tls.cert }} 62 | tls.key: {{ $tls.key }} -------------------------------------------------------------------------------- /mocks/controller-runtime/client/generate.go: -------------------------------------------------------------------------------- 1 | package mock_client 2 | 3 | //go:generate mockgen -package mock_client -destination client_mocks.go sigs.k8s.io/controller-runtime/pkg/client Client 4 | //go:generate mockgen -package mock_client -destination record_mock.go k8s.io/client-go/tools/record EventRecorder 5 | -------------------------------------------------------------------------------- /pkg/apis/applicationnetworking/v1alpha1/common.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | func toPtrSlice[T any](s []T) []*T { 4 | ps := make([]*T, len(s)) 5 | for i, t := range s { 6 | ct := t 7 | ps[i] = &ct 8 | } 9 | return ps 10 | } 11 | -------------------------------------------------------------------------------- /pkg/apis/applicationnetworking/v1alpha1/common_test.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestToPtrSlice(t *testing.T) { 10 | 11 | type A struct { 12 | x int 13 | } 14 | 15 | type test struct { 16 | name string 17 | in []A 18 | want []*A 19 | } 20 | 21 | tests := []test{ 22 | {"empty", []A{}, []*A{}}, 23 | {"single item", []A{{1}}, []*A{{1}}}, 24 | {"multiple items", []A{{1}, {2}, {3}}, []*A{{1}, {2}, {3}}}, 25 | } 26 | 27 | for _, tt := range tests { 28 | t.Run(tt.name, func(t *testing.T) { 29 | assert.Equal(t, toPtrSlice(tt.in), tt.want) 30 | }) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /pkg/apis/applicationnetworking/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | // +kubebuilder:object:generate=true 2 | // +groupName=application-networking.k8s.aws 3 | package v1alpha1 4 | -------------------------------------------------------------------------------- /pkg/aws/services/tagging_mocks.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: github.com/aws/aws-application-networking-k8s/pkg/aws/services (interfaces: Tagging) 3 | 4 | // Package services is a generated GoMock package. 5 | package services 6 | 7 | import ( 8 | context "context" 9 | reflect "reflect" 10 | 11 | gomock "github.com/golang/mock/gomock" 12 | ) 13 | 14 | // MockTagging is a mock of Tagging interface. 15 | type MockTagging struct { 16 | ctrl *gomock.Controller 17 | recorder *MockTaggingMockRecorder 18 | } 19 | 20 | // MockTaggingMockRecorder is the mock recorder for MockTagging. 21 | type MockTaggingMockRecorder struct { 22 | mock *MockTagging 23 | } 24 | 25 | // NewMockTagging creates a new mock instance. 26 | func NewMockTagging(ctrl *gomock.Controller) *MockTagging { 27 | mock := &MockTagging{ctrl: ctrl} 28 | mock.recorder = &MockTaggingMockRecorder{mock} 29 | return mock 30 | } 31 | 32 | // EXPECT returns an object that allows the caller to indicate expected use. 33 | func (m *MockTagging) EXPECT() *MockTaggingMockRecorder { 34 | return m.recorder 35 | } 36 | 37 | // FindResourcesByTags mocks base method. 38 | func (m *MockTagging) FindResourcesByTags(arg0 context.Context, arg1 ResourceType, arg2 map[string]*string) ([]string, error) { 39 | m.ctrl.T.Helper() 40 | ret := m.ctrl.Call(m, "FindResourcesByTags", arg0, arg1, arg2) 41 | ret0, _ := ret[0].([]string) 42 | ret1, _ := ret[1].(error) 43 | return ret0, ret1 44 | } 45 | 46 | // FindResourcesByTags indicates an expected call of FindResourcesByTags. 47 | func (mr *MockTaggingMockRecorder) FindResourcesByTags(arg0, arg1, arg2 interface{}) *gomock.Call { 48 | mr.mock.ctrl.T.Helper() 49 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindResourcesByTags", reflect.TypeOf((*MockTagging)(nil).FindResourcesByTags), arg0, arg1, arg2) 50 | } 51 | 52 | // GetTagsForArns mocks base method. 53 | func (m *MockTagging) GetTagsForArns(arg0 context.Context, arg1 []string) (map[string]map[string]*string, error) { 54 | m.ctrl.T.Helper() 55 | ret := m.ctrl.Call(m, "GetTagsForArns", arg0, arg1) 56 | ret0, _ := ret[0].(map[string]map[string]*string) 57 | ret1, _ := ret[1].(error) 58 | return ret0, ret1 59 | } 60 | 61 | // GetTagsForArns indicates an expected call of GetTagsForArns. 62 | func (mr *MockTaggingMockRecorder) GetTagsForArns(arg0, arg1 interface{}) *gomock.Call { 63 | mr.mock.ctrl.T.Helper() 64 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTagsForArns", reflect.TypeOf((*MockTagging)(nil).GetTagsForArns), arg0, arg1) 65 | } 66 | -------------------------------------------------------------------------------- /pkg/config/ec2_metadata.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/aws/aws-sdk-go/aws/ec2metadata" 8 | "github.com/aws/aws-sdk-go/aws/session" 9 | ) 10 | 11 | type EC2Metadata interface { 12 | Region() (string, error) 13 | VpcID() (string, error) 14 | AccountId() (string, error) 15 | } 16 | 17 | // NewEC2Metadata constructs new EC2Metadata implementation. 18 | func NewEC2Metadata(session *session.Session) EC2Metadata { 19 | return &defaultEC2Metadata{ 20 | EC2Metadata: ec2metadata.New(session), 21 | } 22 | } 23 | 24 | type defaultEC2Metadata struct { 25 | *ec2metadata.EC2Metadata 26 | } 27 | 28 | func (c *defaultEC2Metadata) VpcID() (string, error) { 29 | mac, err := c.GetMetadata("mac") 30 | if err != nil { 31 | return "", err 32 | } 33 | vpcID, err := c.GetMetadata(fmt.Sprintf("network/interfaces/macs/%s/vpc-id", mac)) 34 | if err != nil { 35 | return "", err 36 | } 37 | return vpcID, nil 38 | } 39 | 40 | func (c *defaultEC2Metadata) Region() (string, error) { 41 | region, err := c.GetMetadata("placement/region") 42 | if err != nil { 43 | return "", err 44 | } 45 | return region, nil 46 | } 47 | 48 | func (c *defaultEC2Metadata) AccountId() (string, error) { 49 | ec2Info, err := c.GetMetadata("identity-credentials/ec2/info") 50 | type accountInfo struct { 51 | Code string `json:"code"` 52 | LastUpdated string `json:"LastUpdated"` 53 | AccountId string `json:"AccountId"` 54 | } 55 | 56 | var acc accountInfo 57 | json.Unmarshal([]byte(ec2Info), &acc) 58 | if err != nil { 59 | return "", err 60 | } 61 | return acc.AccountId, nil 62 | } 63 | -------------------------------------------------------------------------------- /pkg/controllers/errors.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import "errors" 4 | 5 | var ( 6 | GroupNameError = errors.New("wrong group name") 7 | KindError = errors.New("target kind error") 8 | TargetRefNotFound = errors.New("targetRef not found") 9 | TargetRefConflict = errors.New("targetRef has conflict") 10 | ) 11 | -------------------------------------------------------------------------------- /pkg/controllers/eventhandlers/gatewayclass.go: -------------------------------------------------------------------------------- 1 | package eventhandlers 2 | 3 | import ( 4 | "context" 5 | 6 | "k8s.io/apimachinery/pkg/types" 7 | "k8s.io/client-go/util/workqueue" 8 | "sigs.k8s.io/controller-runtime/pkg/client" 9 | "sigs.k8s.io/controller-runtime/pkg/event" 10 | "sigs.k8s.io/controller-runtime/pkg/handler" 11 | "sigs.k8s.io/controller-runtime/pkg/reconcile" 12 | gwv1 "sigs.k8s.io/gateway-api/apis/v1" 13 | 14 | "github.com/aws/aws-application-networking-k8s/pkg/config" 15 | "github.com/aws/aws-application-networking-k8s/pkg/utils/gwlog" 16 | ) 17 | 18 | func NewEnqueueRequestsForGatewayClassEvent(log gwlog.Logger, client client.Client) handler.EventHandler { 19 | return &enqueueRequestsForGatewayClassEvent{ 20 | log: log, 21 | client: client, 22 | } 23 | } 24 | 25 | type enqueueRequestsForGatewayClassEvent struct { 26 | log gwlog.Logger 27 | client client.Client 28 | } 29 | 30 | func (h *enqueueRequestsForGatewayClassEvent) Create(ctx context.Context, e event.CreateEvent, queue workqueue.TypedRateLimitingInterface[reconcile.Request]) { 31 | gwClassNew := e.Object.(*gwv1.GatewayClass) 32 | h.enqueueImpactedGateway(ctx, queue, gwClassNew) 33 | } 34 | 35 | func (h *enqueueRequestsForGatewayClassEvent) Update(ctx context.Context, e event.UpdateEvent, queue workqueue.TypedRateLimitingInterface[reconcile.Request]) { 36 | } 37 | 38 | func (h *enqueueRequestsForGatewayClassEvent) Delete(ctx context.Context, e event.DeleteEvent, queue workqueue.TypedRateLimitingInterface[reconcile.Request]) { 39 | } 40 | 41 | func (h *enqueueRequestsForGatewayClassEvent) Generic(ctx context.Context, e event.GenericEvent, queue workqueue.TypedRateLimitingInterface[reconcile.Request]) { 42 | } 43 | 44 | func (h *enqueueRequestsForGatewayClassEvent) enqueueImpactedGateway( 45 | ctx context.Context, 46 | queue workqueue.TypedRateLimitingInterface[reconcile.Request], 47 | gwClass *gwv1.GatewayClass, 48 | ) { 49 | gwList := &gwv1.GatewayList{} 50 | err := h.client.List(ctx, gwList) 51 | if err != nil { 52 | h.log.Errorf(ctx, "Error listing Gateways during GatewayClass event %s", err) 53 | return 54 | } 55 | 56 | for _, gw := range gwList.Items { 57 | if string(gw.Spec.GatewayClassName) == gwClass.Name { 58 | if gwClass.Spec.ControllerName == config.LatticeGatewayControllerName { 59 | h.log.Debugf(ctx, "Found matching gateway, %s-%s", gw.Name, gw.Namespace) 60 | queue.Add(reconcile.Request{ 61 | NamespacedName: types.NamespacedName{ 62 | Namespace: gw.Namespace, 63 | Name: gw.Name, 64 | }, 65 | }) 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /pkg/controllers/eventhandlers/serviceimport.go: -------------------------------------------------------------------------------- 1 | package eventhandlers 2 | 3 | import ( 4 | "context" 5 | 6 | anv1alpha1 "github.com/aws/aws-application-networking-k8s/pkg/apis/applicationnetworking/v1alpha1" 7 | "github.com/aws/aws-application-networking-k8s/pkg/model/core" 8 | "github.com/aws/aws-application-networking-k8s/pkg/utils/gwlog" 9 | 10 | "sigs.k8s.io/controller-runtime/pkg/client" 11 | "sigs.k8s.io/controller-runtime/pkg/handler" 12 | "sigs.k8s.io/controller-runtime/pkg/reconcile" 13 | 14 | "github.com/aws/aws-application-networking-k8s/pkg/k8s" 15 | ) 16 | 17 | type serviceImportEventHandler struct { 18 | log gwlog.Logger 19 | client client.Client 20 | mapper *resourceMapper 21 | } 22 | 23 | func NewServiceImportEventHandler(log gwlog.Logger, client client.Client) *serviceImportEventHandler { 24 | return &serviceImportEventHandler{ 25 | log: log, 26 | client: client, 27 | mapper: &resourceMapper{log: log, client: client}, 28 | } 29 | } 30 | 31 | func (h *serviceImportEventHandler) MapToRoute(routeType core.RouteType) handler.EventHandler { 32 | return handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request { 33 | return h.mapToRoute(ctx, obj, routeType) 34 | }) 35 | } 36 | 37 | func (h *serviceImportEventHandler) mapToRoute(ctx context.Context, obj client.Object, routeType core.RouteType) []reconcile.Request { 38 | routes := h.mapper.ServiceImportToRoutes(ctx, obj.(*anv1alpha1.ServiceImport), routeType) 39 | 40 | var requests []reconcile.Request 41 | for _, route := range routes { 42 | routeName := k8s.NamespacedName(route.K8sObject()) 43 | requests = append(requests, reconcile.Request{NamespacedName: routeName}) 44 | h.log.Infow(ctx, "ServiceImport resource change triggered Route update", 45 | "serviceName", obj.GetNamespace()+"/"+obj.GetName(), "routeName", routeName, "routeType", routeType) 46 | } 47 | return requests 48 | } 49 | -------------------------------------------------------------------------------- /pkg/controllers/eventhandlers/serviceimport_test.go: -------------------------------------------------------------------------------- 1 | package eventhandlers 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/golang/mock/gomock" 8 | "github.com/stretchr/testify/assert" 9 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | "k8s.io/utils/ptr" 11 | gwv1 "sigs.k8s.io/gateway-api/apis/v1" 12 | 13 | mock_client "github.com/aws/aws-application-networking-k8s/mocks/controller-runtime/client" 14 | anv1alpha1 "github.com/aws/aws-application-networking-k8s/pkg/apis/applicationnetworking/v1alpha1" 15 | "github.com/aws/aws-application-networking-k8s/pkg/model/core" 16 | "github.com/aws/aws-application-networking-k8s/pkg/utils/gwlog" 17 | ) 18 | 19 | func TestServiceImportEventHandler_MapToRoute(t *testing.T) { 20 | c := gomock.NewController(t) 21 | defer c.Finish() 22 | 23 | routes := []gwv1.HTTPRoute{ 24 | createHTTPRoute("valid-route", "ns1", gwv1.BackendObjectReference{ 25 | Group: (*gwv1.Group)(ptr.To("application-networking.k8s.aws")), 26 | Kind: (*gwv1.Kind)(ptr.To("ServiceImport")), 27 | Namespace: (*gwv1.Namespace)(ptr.To("ns1")), 28 | Name: "test-service", 29 | }), 30 | } 31 | mockClient := mock_client.NewMockClient(c) 32 | h := NewServiceImportEventHandler(gwlog.FallbackLogger, mockClient) 33 | mockClient.EXPECT().List(gomock.Any(), gomock.Any()).DoAndReturn( 34 | func(ctx context.Context, routeList *gwv1.HTTPRouteList, _ ...interface{}) error { 35 | routeList.Items = append(routeList.Items, routes...) 36 | return nil 37 | }, 38 | ).AnyTimes() 39 | 40 | reqs := h.mapToRoute(context.Background(), &anv1alpha1.ServiceImport{ 41 | ObjectMeta: metav1.ObjectMeta{ 42 | Name: "test-service", 43 | Namespace: "ns1", 44 | }, 45 | }, core.HttpRouteType) 46 | assert.Len(t, reqs, 1) 47 | assert.Equal(t, "valid-route", reqs[0].Name) 48 | } 49 | -------------------------------------------------------------------------------- /pkg/controllers/eventhandlers/vpc_association_policy.go: -------------------------------------------------------------------------------- 1 | package eventhandlers 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/aws/aws-application-networking-k8s/pkg/apis/applicationnetworking/v1alpha1" 7 | "github.com/aws/aws-application-networking-k8s/pkg/k8s" 8 | "github.com/aws/aws-application-networking-k8s/pkg/utils/gwlog" 9 | 10 | "sigs.k8s.io/controller-runtime/pkg/client" 11 | "sigs.k8s.io/controller-runtime/pkg/handler" 12 | "sigs.k8s.io/controller-runtime/pkg/reconcile" 13 | ) 14 | 15 | type vpcAssociationPolicyEventHandler struct { 16 | log gwlog.Logger 17 | client client.Client 18 | mapper *resourceMapper 19 | } 20 | 21 | func NewVpcAssociationPolicyEventHandler(log gwlog.Logger, client client.Client) *vpcAssociationPolicyEventHandler { 22 | return &vpcAssociationPolicyEventHandler{log: log, client: client, 23 | mapper: &resourceMapper{log: log, client: client}} 24 | } 25 | 26 | func (h *vpcAssociationPolicyEventHandler) MapToGateway() handler.EventHandler { 27 | return handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request { 28 | if vap, ok := obj.(*v1alpha1.VpcAssociationPolicy); ok { 29 | if gw := h.mapper.VpcAssociationPolicyToGateway(ctx, vap); gw != nil { 30 | return []reconcile.Request{{NamespacedName: k8s.NamespacedName(gw)}} 31 | } 32 | } 33 | return nil 34 | }) 35 | } 36 | -------------------------------------------------------------------------------- /pkg/controllers/iamauthpolicy_controller_test.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | -------------------------------------------------------------------------------- /pkg/controllers/pod_controller.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package controllers 18 | 19 | import ( 20 | "context" 21 | 22 | "github.com/aws/aws-application-networking-k8s/pkg/utils/gwlog" 23 | corev1 "k8s.io/api/core/v1" 24 | "k8s.io/apimachinery/pkg/runtime" 25 | ctrl "sigs.k8s.io/controller-runtime" 26 | "sigs.k8s.io/controller-runtime/pkg/client" 27 | ) 28 | 29 | type podReconciler struct { 30 | log gwlog.Logger 31 | client client.Client 32 | scheme *runtime.Scheme 33 | } 34 | 35 | func RegisterPodController(log gwlog.Logger, mgr ctrl.Manager) error { 36 | pr := &podReconciler{ 37 | log: log, 38 | client: mgr.GetClient(), 39 | scheme: mgr.GetScheme(), 40 | } 41 | err := ctrl.NewControllerManagedBy(mgr). 42 | For(&corev1.Pod{}). 43 | Complete(pr) 44 | return err 45 | } 46 | 47 | //+kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch;create;update;patch;delete 48 | //+kubebuilder:rbac:groups=core,resources=pods/status,verbs=get;update;patch 49 | //+kubebuilder:rbac:groups=core,resources=pods/finalizers,verbs=update 50 | 51 | func (r *podReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { 52 | pod := &corev1.Pod{} 53 | if err := r.client.Get(ctx, req.NamespacedName, pod); err != nil { 54 | return ctrl.Result{}, client.IgnoreNotFound(err) 55 | 56 | } 57 | return ctrl.Result{}, nil 58 | } 59 | -------------------------------------------------------------------------------- /pkg/controllers/suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package controllers 18 | 19 | import ( 20 | "path/filepath" 21 | "testing" 22 | 23 | . "github.com/onsi/ginkgo" 24 | . "github.com/onsi/gomega" 25 | corev1 "k8s.io/api/core/v1" 26 | "k8s.io/client-go/kubernetes/scheme" 27 | "sigs.k8s.io/controller-runtime/pkg/client" 28 | "sigs.k8s.io/controller-runtime/pkg/envtest" 29 | logf "sigs.k8s.io/controller-runtime/pkg/log" 30 | "sigs.k8s.io/controller-runtime/pkg/log/zap" 31 | //+kubebuilder:scaffold:imports 32 | ) 33 | 34 | var k8sClient client.Client 35 | var testEnv *envtest.Environment 36 | 37 | func TestAPIs(t *testing.T) { 38 | RegisterFailHandler(Fail) 39 | 40 | /* TODO 41 | RunSpecsWithDefaultAndCustomReporters(t, 42 | "Controller Suite", 43 | []Reporter{printer.NewlineReporter{}}) 44 | 45 | */ 46 | } 47 | 48 | var _ = BeforeSuite(func() { 49 | logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) 50 | 51 | By("bootstrapping test environment") 52 | testEnv = &envtest.Environment{ 53 | CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")}, 54 | ErrorIfCRDPathMissing: false, 55 | } 56 | 57 | cfg, err := testEnv.Start() 58 | Expect(err).NotTo(HaveOccurred()) 59 | Expect(cfg).NotTo(BeNil()) 60 | 61 | err = corev1.AddToScheme(scheme.Scheme) 62 | Expect(err).NotTo(HaveOccurred()) 63 | 64 | //+kubebuilder:scaffold:scheme 65 | 66 | k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) 67 | Expect(err).NotTo(HaveOccurred()) 68 | Expect(k8sClient).NotTo(BeNil()) 69 | 70 | }, 60) 71 | 72 | var _ = AfterSuite(func() { 73 | By("tearing down the test environment") 74 | err := testEnv.Stop() 75 | Expect(err).NotTo(HaveOccurred()) 76 | }) 77 | -------------------------------------------------------------------------------- /pkg/controllers/targetgrouppolicy_controller.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "context" 5 | 6 | corev1 "k8s.io/api/core/v1" 7 | ctrl "sigs.k8s.io/controller-runtime" 8 | "sigs.k8s.io/controller-runtime/pkg/builder" 9 | "sigs.k8s.io/controller-runtime/pkg/client" 10 | "sigs.k8s.io/controller-runtime/pkg/predicate" 11 | 12 | anv1alpha1 "github.com/aws/aws-application-networking-k8s/pkg/apis/applicationnetworking/v1alpha1" 13 | policy "github.com/aws/aws-application-networking-k8s/pkg/k8s/policyhelper" 14 | "github.com/aws/aws-application-networking-k8s/pkg/utils/gwlog" 15 | ) 16 | 17 | type ( 18 | TGP = anv1alpha1.TargetGroupPolicy 19 | ) 20 | 21 | type TargetGroupPolicyController struct { 22 | log gwlog.Logger 23 | client client.Client 24 | ph *policy.PolicyHandler[*TGP] 25 | } 26 | 27 | func RegisterTargetGroupPolicyController(log gwlog.Logger, mgr ctrl.Manager) error { 28 | ph := policy.NewTargetGroupPolicyHandler(log, mgr.GetClient()) 29 | controller := &TargetGroupPolicyController{ 30 | log: log, 31 | client: mgr.GetClient(), 32 | ph: ph, 33 | } 34 | 35 | b := ctrl.NewControllerManagedBy(mgr). 36 | For(&TGP{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})) 37 | ph.AddWatchers(b, &corev1.Service{}) 38 | ph.AddWatchers(b, &anv1alpha1.ServiceExport{}) 39 | 40 | return b.Complete(controller) 41 | } 42 | 43 | func (c *TargetGroupPolicyController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { 44 | ctx = gwlog.StartReconcileTrace(ctx, c.log, "targetgrouppolicy", req.Name, req.Namespace) 45 | defer func() { 46 | gwlog.EndReconcileTrace(ctx, c.log) 47 | }() 48 | 49 | tgPolicy := &TGP{} 50 | err := c.client.Get(ctx, req.NamespacedName, tgPolicy) 51 | if err != nil { 52 | return ctrl.Result{}, client.IgnoreNotFound(err) 53 | } 54 | c.log.Infow(ctx, "reconcile target group policy", "req", req, "targetRef", tgPolicy.Spec.TargetRef) 55 | 56 | _, err = c.ph.ValidateAndUpdateCondition(ctx, tgPolicy) 57 | if err != nil { 58 | return ctrl.Result{}, err 59 | } 60 | 61 | c.log.Infow(ctx, "reconciled target group policy", 62 | "req", req, 63 | "targetRef", tgPolicy.Spec.TargetRef, 64 | ) 65 | return ctrl.Result{}, nil 66 | } 67 | -------------------------------------------------------------------------------- /pkg/deploy/externaldns/dnsendpoint_manager_mock.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: github.com/aws/aws-application-networking-k8s/pkg/deploy/externaldns (interfaces: DnsEndpointManager) 3 | 4 | // Package externaldns is a generated GoMock package. 5 | package externaldns 6 | 7 | import ( 8 | context "context" 9 | reflect "reflect" 10 | 11 | lattice "github.com/aws/aws-application-networking-k8s/pkg/model/lattice" 12 | gomock "github.com/golang/mock/gomock" 13 | ) 14 | 15 | // MockDnsEndpointManager is a mock of DnsEndpointManager interface. 16 | type MockDnsEndpointManager struct { 17 | ctrl *gomock.Controller 18 | recorder *MockDnsEndpointManagerMockRecorder 19 | } 20 | 21 | // MockDnsEndpointManagerMockRecorder is the mock recorder for MockDnsEndpointManager. 22 | type MockDnsEndpointManagerMockRecorder struct { 23 | mock *MockDnsEndpointManager 24 | } 25 | 26 | // NewMockDnsEndpointManager creates a new mock instance. 27 | func NewMockDnsEndpointManager(ctrl *gomock.Controller) *MockDnsEndpointManager { 28 | mock := &MockDnsEndpointManager{ctrl: ctrl} 29 | mock.recorder = &MockDnsEndpointManagerMockRecorder{mock} 30 | return mock 31 | } 32 | 33 | // EXPECT returns an object that allows the caller to indicate expected use. 34 | func (m *MockDnsEndpointManager) EXPECT() *MockDnsEndpointManagerMockRecorder { 35 | return m.recorder 36 | } 37 | 38 | // Create mocks base method. 39 | func (m *MockDnsEndpointManager) Create(arg0 context.Context, arg1 *lattice.Service) error { 40 | m.ctrl.T.Helper() 41 | ret := m.ctrl.Call(m, "Create", arg0, arg1) 42 | ret0, _ := ret[0].(error) 43 | return ret0 44 | } 45 | 46 | // Create indicates an expected call of Create. 47 | func (mr *MockDnsEndpointManagerMockRecorder) Create(arg0, arg1 interface{}) *gomock.Call { 48 | mr.mock.ctrl.T.Helper() 49 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockDnsEndpointManager)(nil).Create), arg0, arg1) 50 | } 51 | -------------------------------------------------------------------------------- /pkg/deploy/lattice/access_log_subscription_synthesizer.go: -------------------------------------------------------------------------------- 1 | package lattice 2 | 3 | import ( 4 | "context" 5 | 6 | "sigs.k8s.io/controller-runtime/pkg/client" 7 | 8 | "github.com/aws/aws-application-networking-k8s/pkg/model/core" 9 | model "github.com/aws/aws-application-networking-k8s/pkg/model/lattice" 10 | "github.com/aws/aws-application-networking-k8s/pkg/utils/gwlog" 11 | ) 12 | 13 | type accessLogSubscriptionSynthesizer struct { 14 | log gwlog.Logger 15 | client client.Client 16 | accessLogSubscriptionManager AccessLogSubscriptionManager 17 | stack core.Stack 18 | } 19 | 20 | func NewAccessLogSubscriptionSynthesizer( 21 | log gwlog.Logger, 22 | client client.Client, 23 | accessLogSubscriptionManager AccessLogSubscriptionManager, 24 | stack core.Stack, 25 | ) *accessLogSubscriptionSynthesizer { 26 | return &accessLogSubscriptionSynthesizer{ 27 | log: log, 28 | client: client, 29 | accessLogSubscriptionManager: accessLogSubscriptionManager, 30 | stack: stack, 31 | } 32 | } 33 | 34 | func (s *accessLogSubscriptionSynthesizer) Synthesize(ctx context.Context) error { 35 | var accessLogSubscriptions []*model.AccessLogSubscription 36 | err := s.stack.ListResources(&accessLogSubscriptions) 37 | if err != nil { 38 | return err 39 | } 40 | 41 | for _, als := range accessLogSubscriptions { 42 | switch als.Spec.EventType { 43 | case core.CreateEvent: 44 | s.log.Debugf(ctx, "Started creating Access Log Subscription %s", als.ID()) 45 | alsStatus, err := s.accessLogSubscriptionManager.Create(ctx, als) 46 | if err != nil { 47 | return err 48 | } 49 | als.Status = alsStatus 50 | case core.UpdateEvent: 51 | s.log.Debugf(ctx, "Started updating Access Log Subscription %s", als.ID()) 52 | alsStatus, err := s.accessLogSubscriptionManager.Update(ctx, als) 53 | if err != nil { 54 | return err 55 | } 56 | als.Status = alsStatus 57 | case core.DeleteEvent: 58 | s.log.Debugf(ctx, "Started deleting Access Log Subscription %s", als.ID()) 59 | if als.Status == nil { 60 | s.log.Debugf(ctx, "Ignoring deletion of Access Log Subscription because als %s has no ARN", als.ID()) 61 | return nil 62 | } 63 | err := s.accessLogSubscriptionManager.Delete(ctx, als.Status.Arn) 64 | if err != nil { 65 | return err 66 | } 67 | } 68 | } 69 | 70 | return nil 71 | } 72 | 73 | func (s *accessLogSubscriptionSynthesizer) PostSynthesize(ctx context.Context) error { 74 | // nothing to do here 75 | return nil 76 | } 77 | -------------------------------------------------------------------------------- /pkg/deploy/lattice/config_test.go: -------------------------------------------------------------------------------- 1 | package lattice 2 | 3 | import pkg_aws "github.com/aws/aws-application-networking-k8s/pkg/aws" 4 | 5 | var TestCloudConfig = pkg_aws.CloudConfig{ 6 | VpcId: "vpc-id", 7 | AccountId: "account-id", 8 | Region: "region", 9 | ClusterName: "cluster", 10 | } 11 | -------------------------------------------------------------------------------- /pkg/deploy/lattice/error.go: -------------------------------------------------------------------------------- 1 | package lattice 2 | 3 | import "errors" 4 | 5 | const ( 6 | LATTICE_RETRY = "LATTICE_RETRY" 7 | ) 8 | 9 | var RetryErr = errors.New(LATTICE_RETRY) 10 | -------------------------------------------------------------------------------- /pkg/deploy/lattice/iamauthpolicy_manager_test.go: -------------------------------------------------------------------------------- 1 | package lattice 2 | -------------------------------------------------------------------------------- /pkg/deploy/lattice/service_manager_mock.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: github.com/aws/aws-application-networking-k8s/pkg/deploy/lattice (interfaces: ServiceManager) 3 | 4 | // Package lattice is a generated GoMock package. 5 | package lattice 6 | 7 | import ( 8 | context "context" 9 | reflect "reflect" 10 | 11 | lattice "github.com/aws/aws-application-networking-k8s/pkg/model/lattice" 12 | gomock "github.com/golang/mock/gomock" 13 | ) 14 | 15 | // MockServiceManager is a mock of ServiceManager interface. 16 | type MockServiceManager struct { 17 | ctrl *gomock.Controller 18 | recorder *MockServiceManagerMockRecorder 19 | } 20 | 21 | // MockServiceManagerMockRecorder is the mock recorder for MockServiceManager. 22 | type MockServiceManagerMockRecorder struct { 23 | mock *MockServiceManager 24 | } 25 | 26 | // NewMockServiceManager creates a new mock instance. 27 | func NewMockServiceManager(ctrl *gomock.Controller) *MockServiceManager { 28 | mock := &MockServiceManager{ctrl: ctrl} 29 | mock.recorder = &MockServiceManagerMockRecorder{mock} 30 | return mock 31 | } 32 | 33 | // EXPECT returns an object that allows the caller to indicate expected use. 34 | func (m *MockServiceManager) EXPECT() *MockServiceManagerMockRecorder { 35 | return m.recorder 36 | } 37 | 38 | // Delete mocks base method. 39 | func (m *MockServiceManager) Delete(arg0 context.Context, arg1 *lattice.Service) error { 40 | m.ctrl.T.Helper() 41 | ret := m.ctrl.Call(m, "Delete", arg0, arg1) 42 | ret0, _ := ret[0].(error) 43 | return ret0 44 | } 45 | 46 | // Delete indicates an expected call of Delete. 47 | func (mr *MockServiceManagerMockRecorder) Delete(arg0, arg1 interface{}) *gomock.Call { 48 | mr.mock.ctrl.T.Helper() 49 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockServiceManager)(nil).Delete), arg0, arg1) 50 | } 51 | 52 | // Upsert mocks base method. 53 | func (m *MockServiceManager) Upsert(arg0 context.Context, arg1 *lattice.Service) (lattice.ServiceStatus, error) { 54 | m.ctrl.T.Helper() 55 | ret := m.ctrl.Call(m, "Upsert", arg0, arg1) 56 | ret0, _ := ret[0].(lattice.ServiceStatus) 57 | ret1, _ := ret[1].(error) 58 | return ret0, ret1 59 | } 60 | 61 | // Upsert indicates an expected call of Upsert. 62 | func (mr *MockServiceManagerMockRecorder) Upsert(arg0, arg1 interface{}) *gomock.Call { 63 | mr.mock.ctrl.T.Helper() 64 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Upsert", reflect.TypeOf((*MockServiceManager)(nil).Upsert), arg0, arg1) 65 | } 66 | -------------------------------------------------------------------------------- /pkg/deploy/lattice/service_synthesizer.go: -------------------------------------------------------------------------------- 1 | package lattice 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | 8 | "github.com/aws/aws-application-networking-k8s/pkg/utils" 9 | 10 | "github.com/aws/aws-application-networking-k8s/pkg/deploy/externaldns" 11 | "github.com/aws/aws-application-networking-k8s/pkg/model/core" 12 | model "github.com/aws/aws-application-networking-k8s/pkg/model/lattice" 13 | "github.com/aws/aws-application-networking-k8s/pkg/utils/gwlog" 14 | ) 15 | 16 | func NewServiceSynthesizer( 17 | log gwlog.Logger, 18 | serviceManager ServiceManager, 19 | dnsEndpointManager externaldns.DnsEndpointManager, 20 | stack core.Stack, 21 | ) *serviceSynthesizer { 22 | return &serviceSynthesizer{ 23 | log: log, 24 | serviceManager: serviceManager, 25 | dnsEndpointManager: dnsEndpointManager, 26 | stack: stack, 27 | } 28 | } 29 | 30 | type serviceSynthesizer struct { 31 | log gwlog.Logger 32 | serviceManager ServiceManager 33 | dnsEndpointManager externaldns.DnsEndpointManager 34 | stack core.Stack 35 | } 36 | 37 | func (s *serviceSynthesizer) Synthesize(ctx context.Context) error { 38 | var resServices []*model.Service 39 | s.stack.ListResources(&resServices) 40 | 41 | var svcErr error 42 | for _, resService := range resServices { 43 | svcName := utils.LatticeServiceName(resService.Spec.RouteName, resService.Spec.RouteNamespace) 44 | s.log.Debugf(ctx, "Synthesizing service: %s", svcName) 45 | if resService.IsDeleted { 46 | err := s.serviceManager.Delete(ctx, resService) 47 | if err != nil { 48 | svcErr = errors.Join(svcErr, 49 | fmt.Errorf("failed ServiceManager.Delete %s due to %w", svcName, err)) 50 | continue 51 | } 52 | } else { 53 | serviceStatus, err := s.serviceManager.Upsert(ctx, resService) 54 | if err != nil { 55 | svcErr = errors.Join(svcErr, 56 | fmt.Errorf("failed ServiceManager.Upsert %s due to %w", svcName, err)) 57 | continue 58 | } 59 | 60 | resService.Status = &serviceStatus 61 | err = s.dnsEndpointManager.Create(ctx, resService) 62 | if err != nil { 63 | svcErr = errors.Join(svcErr, 64 | fmt.Errorf("failed DnsEndpointManager.Create %s due to %w", svcName, err)) 65 | continue 66 | } 67 | } 68 | } 69 | 70 | return svcErr 71 | } 72 | 73 | func (s *serviceSynthesizer) PostSynthesize(ctx context.Context) error { 74 | // nothing to do here 75 | return nil 76 | } 77 | -------------------------------------------------------------------------------- /pkg/deploy/lattice/targets_manager_mock.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: github.com/aws/aws-application-networking-k8s/pkg/deploy/lattice (interfaces: TargetsManager) 3 | 4 | // Package lattice is a generated GoMock package. 5 | package lattice 6 | 7 | import ( 8 | context "context" 9 | reflect "reflect" 10 | 11 | lattice "github.com/aws/aws-application-networking-k8s/pkg/model/lattice" 12 | vpclattice "github.com/aws/aws-sdk-go/service/vpclattice" 13 | gomock "github.com/golang/mock/gomock" 14 | ) 15 | 16 | // MockTargetsManager is a mock of TargetsManager interface. 17 | type MockTargetsManager struct { 18 | ctrl *gomock.Controller 19 | recorder *MockTargetsManagerMockRecorder 20 | } 21 | 22 | // MockTargetsManagerMockRecorder is the mock recorder for MockTargetsManager. 23 | type MockTargetsManagerMockRecorder struct { 24 | mock *MockTargetsManager 25 | } 26 | 27 | // NewMockTargetsManager creates a new mock instance. 28 | func NewMockTargetsManager(ctrl *gomock.Controller) *MockTargetsManager { 29 | mock := &MockTargetsManager{ctrl: ctrl} 30 | mock.recorder = &MockTargetsManagerMockRecorder{mock} 31 | return mock 32 | } 33 | 34 | // EXPECT returns an object that allows the caller to indicate expected use. 35 | func (m *MockTargetsManager) EXPECT() *MockTargetsManagerMockRecorder { 36 | return m.recorder 37 | } 38 | 39 | // List mocks base method. 40 | func (m *MockTargetsManager) List(arg0 context.Context, arg1 *lattice.TargetGroup) ([]*vpclattice.TargetSummary, error) { 41 | m.ctrl.T.Helper() 42 | ret := m.ctrl.Call(m, "List", arg0, arg1) 43 | ret0, _ := ret[0].([]*vpclattice.TargetSummary) 44 | ret1, _ := ret[1].(error) 45 | return ret0, ret1 46 | } 47 | 48 | // List indicates an expected call of List. 49 | func (mr *MockTargetsManagerMockRecorder) List(arg0, arg1 interface{}) *gomock.Call { 50 | mr.mock.ctrl.T.Helper() 51 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockTargetsManager)(nil).List), arg0, arg1) 52 | } 53 | 54 | // Update mocks base method. 55 | func (m *MockTargetsManager) Update(arg0 context.Context, arg1 *lattice.Targets, arg2 *lattice.TargetGroup) error { 56 | m.ctrl.T.Helper() 57 | ret := m.ctrl.Call(m, "Update", arg0, arg1, arg2) 58 | ret0, _ := ret[0].(error) 59 | return ret0 60 | } 61 | 62 | // Update indicates an expected call of Update. 63 | func (mr *MockTargetsManagerMockRecorder) Update(arg0, arg1, arg2 interface{}) *gomock.Call { 64 | mr.mock.ctrl.T.Helper() 65 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockTargetsManager)(nil).Update), arg0, arg1, arg2) 66 | } 67 | -------------------------------------------------------------------------------- /pkg/deploy/stack_deployer_test.go: -------------------------------------------------------------------------------- 1 | package deploy 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "testing" 8 | "time" 9 | 10 | "github.com/aws/aws-application-networking-k8s/pkg/utils/gwlog" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestTgGc(t *testing.T) { 15 | 16 | type test struct { 17 | name string 18 | gcFn TgGcCycleFn 19 | } 20 | 21 | tests := []test{ 22 | { 23 | name: "empty cycle", 24 | gcFn: func(context.Context) (TgGcResult, error) { return TgGcResult{}, nil }, 25 | }, 26 | { 27 | name: "cycle with results", 28 | gcFn: func(context.Context) (TgGcResult, error) { 29 | return TgGcResult{ 30 | att: 10, 31 | succ: 10, 32 | duration: time.Second, 33 | }, nil 34 | }, 35 | }, 36 | { 37 | name: "cycle panic", 38 | gcFn: func(context.Context) (TgGcResult, error) { panic("") }, 39 | }, 40 | { 41 | name: "cycle error", 42 | gcFn: func(context.Context) (TgGcResult, error) { return TgGcResult{}, errors.New("") }, 43 | }, 44 | } 45 | 46 | nCycles := 10 // run each test at least 10 cycles 47 | 48 | for _, tt := range tests { 49 | t.Run(tt.name, func(t *testing.T) { 50 | ctx, cancel := context.WithCancel(context.Background()) 51 | n := 0 52 | f := func(ctx context.Context) (TgGcResult, error) { 53 | defer func() { 54 | n += 1 55 | if n >= nCycles { 56 | cancel() 57 | } 58 | }() 59 | return tt.gcFn(ctx) 60 | } 61 | ivl := time.Millisecond * 10 62 | tgGc := &TgGc{ 63 | log: gwlog.FallbackLogger, 64 | ctx: ctx, 65 | ivl: ivl, 66 | cycleFn: f, 67 | } 68 | tgGc.start() 69 | time.Sleep(ivl * (time.Duration(nCycles) + 2)) // sleep enough cycles to terminate 70 | assert.Equal(t, nCycles, n, fmt.Sprintf("should run only %d cycles", nCycles)) 71 | assert.True(t, tgGc.isDone.Load(), "gc must terminate") 72 | }) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /pkg/deploy/stack_marshaller.go: -------------------------------------------------------------------------------- 1 | package deploy 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/aws/aws-application-networking-k8s/pkg/model/core" 6 | ) 7 | 8 | // StackMarshaller will marshall a resource stack into JSON. 9 | type StackMarshaller interface { 10 | Marshal(stack core.Stack) (string, error) 11 | } 12 | 13 | func NewDefaultStackMarshaller() *defaultStackMarshaller { 14 | return &defaultStackMarshaller{} 15 | } 16 | 17 | var _ StackMarshaller = &defaultStackMarshaller{} 18 | 19 | type defaultStackMarshaller struct{} 20 | 21 | func (m *defaultStackMarshaller) Marshal(stack core.Stack) (string, error) { 22 | builder := NewStackSchemaBuilder(stack.StackID()) 23 | if err := stack.TopologicalTraversal(builder); err != nil { 24 | return "", err 25 | } 26 | stackSchema := builder.Build() 27 | payload, err := json.Marshal(stackSchema) 28 | if err != nil { 29 | return "", err 30 | } 31 | return string(payload), nil 32 | } 33 | -------------------------------------------------------------------------------- /pkg/deploy/stack_schema_builder.go: -------------------------------------------------------------------------------- 1 | package deploy 2 | 3 | import ( 4 | coremodel "github.com/aws/aws-application-networking-k8s/pkg/model/core" 5 | ) 6 | 7 | // StackSchema represents the JSON model for stack. 8 | type StackSchema struct { 9 | // Stack's ID 10 | ID string `json:"id"` 11 | 12 | // all resources within stack. 13 | Resources map[string]map[string]interface{} `json:"resources"` 14 | } 15 | 16 | // NewStackSchemaBuilder constructs new stackSchemaBuilder. 17 | func NewStackSchemaBuilder(stackID coremodel.StackID) *stackSchemaBuilder { 18 | return &stackSchemaBuilder{ 19 | stackID: stackID, 20 | resources: make(map[string]map[string]interface{}), 21 | } 22 | } 23 | 24 | var _ coremodel.ResourceVisitor = &stackSchemaBuilder{} 25 | 26 | type stackSchemaBuilder struct { 27 | stackID coremodel.StackID 28 | resources map[string]map[string]interface{} 29 | } 30 | 31 | // Visit will visit a resource. 32 | func (b *stackSchemaBuilder) Visit(res coremodel.Resource) error { 33 | if _, ok := b.resources[res.Type()]; !ok { 34 | b.resources[res.Type()] = make(map[string]interface{}) 35 | } 36 | b.resources[res.Type()][res.ID()] = res 37 | return nil 38 | } 39 | 40 | // Build will build StackSchema based on resources visited. 41 | func (b *stackSchemaBuilder) Build() StackSchema { 42 | return StackSchema{ 43 | ID: b.stackID.String(), 44 | Resources: b.resources, 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /pkg/gateway/model_build_lattice_service_mock.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: github.com/aws/aws-application-networking-k8s/pkg/gateway (interfaces: LatticeServiceBuilder) 3 | 4 | // Package gateway is a generated GoMock package. 5 | package gateway 6 | 7 | import ( 8 | context "context" 9 | reflect "reflect" 10 | 11 | core "github.com/aws/aws-application-networking-k8s/pkg/model/core" 12 | gomock "github.com/golang/mock/gomock" 13 | ) 14 | 15 | // MockLatticeServiceBuilder is a mock of LatticeServiceBuilder interface. 16 | type MockLatticeServiceBuilder struct { 17 | ctrl *gomock.Controller 18 | recorder *MockLatticeServiceBuilderMockRecorder 19 | } 20 | 21 | // MockLatticeServiceBuilderMockRecorder is the mock recorder for MockLatticeServiceBuilder. 22 | type MockLatticeServiceBuilderMockRecorder struct { 23 | mock *MockLatticeServiceBuilder 24 | } 25 | 26 | // NewMockLatticeServiceBuilder creates a new mock instance. 27 | func NewMockLatticeServiceBuilder(ctrl *gomock.Controller) *MockLatticeServiceBuilder { 28 | mock := &MockLatticeServiceBuilder{ctrl: ctrl} 29 | mock.recorder = &MockLatticeServiceBuilderMockRecorder{mock} 30 | return mock 31 | } 32 | 33 | // EXPECT returns an object that allows the caller to indicate expected use. 34 | func (m *MockLatticeServiceBuilder) EXPECT() *MockLatticeServiceBuilderMockRecorder { 35 | return m.recorder 36 | } 37 | 38 | // Build mocks base method. 39 | func (m *MockLatticeServiceBuilder) Build(arg0 context.Context, arg1 core.Route) (core.Stack, error) { 40 | m.ctrl.T.Helper() 41 | ret := m.ctrl.Call(m, "Build", arg0, arg1) 42 | ret0, _ := ret[0].(core.Stack) 43 | ret1, _ := ret[1].(error) 44 | return ret0, ret1 45 | } 46 | 47 | // Build indicates an expected call of Build. 48 | func (mr *MockLatticeServiceBuilderMockRecorder) Build(arg0, arg1 interface{}) *gomock.Call { 49 | mr.mock.ctrl.T.Helper() 50 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Build", reflect.TypeOf((*MockLatticeServiceBuilder)(nil).Build), arg0, arg1) 51 | } 52 | -------------------------------------------------------------------------------- /pkg/k8s/events.go: -------------------------------------------------------------------------------- 1 | package k8s 2 | 3 | const ( 4 | // Generic events 5 | ReconcilingEvent = "Reconciling" 6 | ReconciledEvent = "Reconciled" 7 | FailedReconcileEvent = "FailedReconcile" 8 | 9 | // Gateway events 10 | GatewayEventReasonFailedAddFinalizer = "FailedAddFinalizer" 11 | GatewayEventReasonFailedBuildModel = "FailedBuildModel" 12 | GatewayEventReasonFailedDeployModel = "FailedDeployModel" 13 | 14 | // Route events 15 | RouteEventReasonReconcile = "Reconcile" 16 | RouteEventReasonDeploySucceed = "DeploySucceed" 17 | RouteEventReasonFailedAddFinalizer = "FailedAddFinalizer" 18 | RouteEventReasonFailedBuildModel = "FailedBuildModel" 19 | RouteEventReasonFailedDeployModel = "FailedDeployModel" 20 | RouteEventReasonRetryReconcile = "Retry-Reconcile" 21 | 22 | // Service events 23 | ServiceEventReasonFailedAddFinalizer = "FailedAddFinalizer" 24 | ServiceEventReasonFailedBuildModel = "FailedBuildModel" 25 | ServiceEventReasonFailedDeployModel = "FailedDeployModel" 26 | 27 | // ServiceExport events 28 | ServiceExportEventReasonFailedAddFinalizer = "FailedAddFinalizer" 29 | ServiceExportEventReasonFailedBuildModel = "FailedBuildModel" 30 | ServiceExportEventReasonFailedDeployModel = "FailedDeployModel" 31 | 32 | // ServiceImport events 33 | ServiceImportEventReasonFailedAddFinalizer = "FailedAddFinalizer" 34 | ServiceImportEventReasonFailedBuildModel = "FailedBuildModel" 35 | ServiceImportEventReasonFailedDeployModel = "FailedDeployModel" 36 | ) 37 | -------------------------------------------------------------------------------- /pkg/k8s/finalizer.go: -------------------------------------------------------------------------------- 1 | package k8s 2 | 3 | import ( 4 | "context" 5 | 6 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 7 | "k8s.io/client-go/util/retry" 8 | "sigs.k8s.io/controller-runtime/pkg/client" 9 | "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 10 | ) 11 | 12 | //go:generate mockgen -destination finalizer_mock.go -package k8s github.com/aws/aws-application-networking-k8s/pkg/k8s FinalizerManager 13 | 14 | type FinalizerManager interface { 15 | AddFinalizers(ctx context.Context, object client.Object, finalizers ...string) error 16 | RemoveFinalizers(ctx context.Context, object client.Object, finalizers ...string) error 17 | } 18 | 19 | func NewDefaultFinalizerManager(k8sClient client.Client) FinalizerManager { 20 | return &defaultFinalizerManager{ 21 | k8sClient: k8sClient, 22 | } 23 | } 24 | 25 | type defaultFinalizerManager struct { 26 | k8sClient client.Client 27 | } 28 | 29 | func (m *defaultFinalizerManager) AddFinalizers(ctx context.Context, obj client.Object, finalizers ...string) error { 30 | return retry.RetryOnConflict(retry.DefaultBackoff, func() error { 31 | if err := m.k8sClient.Get(ctx, NamespacedName(obj), obj); err != nil { 32 | return err 33 | } 34 | 35 | oldObj := obj.DeepCopyObject().(client.Object) 36 | needsUpdate := false 37 | for _, finalizer := range finalizers { 38 | if !HasFinalizer(obj, finalizer) { 39 | controllerutil.AddFinalizer(obj, finalizer) 40 | needsUpdate = true 41 | } 42 | } 43 | if !needsUpdate { 44 | return nil 45 | } 46 | return m.k8sClient.Patch(ctx, obj, client.MergeFromWithOptions(oldObj, client.MergeFromWithOptimisticLock{})) 47 | }) 48 | } 49 | 50 | func (m *defaultFinalizerManager) RemoveFinalizers(ctx context.Context, obj client.Object, finalizers ...string) error { 51 | return retry.RetryOnConflict(retry.DefaultBackoff, func() error { 52 | if err := m.k8sClient.Get(ctx, NamespacedName(obj), obj); err != nil { 53 | return err 54 | } 55 | 56 | oldObj := obj.DeepCopyObject().(client.Object) 57 | needsUpdate := false 58 | for _, finalizer := range finalizers { 59 | if HasFinalizer(obj, finalizer) { 60 | controllerutil.RemoveFinalizer(obj, finalizer) 61 | needsUpdate = true 62 | } 63 | } 64 | if !needsUpdate { 65 | return nil 66 | } 67 | return m.k8sClient.Patch(ctx, obj, client.MergeFromWithOptions(oldObj, client.MergeFromWithOptimisticLock{})) 68 | }) 69 | } 70 | 71 | // HasFinalizer tests whether k8s object has specified finalizer 72 | func HasFinalizer(obj metav1.Object, finalizer string) bool { 73 | f := obj.GetFinalizers() 74 | for _, e := range f { 75 | if e == finalizer { 76 | return true 77 | } 78 | } 79 | return false 80 | } 81 | -------------------------------------------------------------------------------- /pkg/k8s/policyhelper/kind.go: -------------------------------------------------------------------------------- 1 | package policyhelper 2 | 3 | import ( 4 | corev1 "k8s.io/api/core/v1" 5 | "sigs.k8s.io/controller-runtime/pkg/client" 6 | gwv1 "sigs.k8s.io/gateway-api/apis/v1" 7 | gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" 8 | 9 | anv1alpha1 "github.com/aws/aws-application-networking-k8s/pkg/apis/applicationnetworking/v1alpha1" 10 | ) 11 | 12 | type GroupKind struct { 13 | Group string 14 | Kind string 15 | } 16 | 17 | func ObjToGroupKind(obj client.Object) GroupKind { 18 | switch obj.(type) { 19 | case *gwv1.Gateway: 20 | return GroupKind{gwv1.GroupName, "Gateway"} 21 | case *gwv1.HTTPRoute: 22 | return GroupKind{gwv1.GroupName, "HTTPRoute"} 23 | case *gwv1.GRPCRoute: 24 | return GroupKind{gwv1alpha2.GroupName, "GRPCRoute"} 25 | case *gwv1alpha2.TCPRoute: 26 | return GroupKind{gwv1alpha2.GroupName, "TCPRoute"} 27 | case *anv1alpha1.ServiceExport: 28 | return GroupKind{anv1alpha1.GroupName, "ServiceExport"} 29 | case *corev1.Service: 30 | return GroupKind{corev1.GroupName, "Service"} 31 | default: 32 | return GroupKind{} 33 | } 34 | } 35 | 36 | func TargetRefGroupKind(tr *TargetRef) GroupKind { 37 | return GroupKind{ 38 | Group: string(tr.Group), 39 | Kind: string(tr.Kind), 40 | } 41 | } 42 | 43 | func GroupKindToObj(gk GroupKind) (client.Object, bool) { 44 | switch gk { 45 | case GroupKind{gwv1.GroupName, "Gateway"}: 46 | return &gwv1.Gateway{}, true 47 | case GroupKind{gwv1.GroupName, "HTTPRoute"}: 48 | return &gwv1.HTTPRoute{}, true 49 | case GroupKind{gwv1alpha2.GroupName, "GRPCRoute"}: 50 | return &gwv1.GRPCRoute{}, true 51 | case GroupKind{gwv1alpha2.GroupName, "TCPRoute"}: 52 | return &gwv1alpha2.TCPRoute{}, true 53 | case GroupKind{corev1.GroupName, "Service"}: 54 | return &corev1.Service{}, true 55 | case GroupKind{anv1alpha1.GroupName, "ServiceExport"}: 56 | return &anv1alpha1.ServiceExport{}, true 57 | default: 58 | return nil, false 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /pkg/k8s/policyhelper/kind_test.go: -------------------------------------------------------------------------------- 1 | package policyhelper 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | corev1 "k8s.io/api/core/v1" 8 | "sigs.k8s.io/controller-runtime/pkg/client" 9 | gwv1 "sigs.k8s.io/gateway-api/apis/v1" 10 | ) 11 | 12 | func TestGroupKind(t *testing.T) { 13 | type Test struct { 14 | obj client.Object 15 | kind GroupKind 16 | } 17 | 18 | tests := []Test{ 19 | {&gwv1.Gateway{}, GroupKind{Group: gwv1.GroupName, Kind: "Gateway"}}, 20 | {&gwv1.HTTPRoute{}, GroupKind{Group: gwv1.GroupName, Kind: "HTTPRoute"}}, 21 | {&gwv1.GRPCRoute{}, GroupKind{Group: gwv1.GroupName, Kind: "GRPCRoute"}}, 22 | {&corev1.Service{}, GroupKind{Group: corev1.GroupName, Kind: "Service"}}, 23 | } 24 | 25 | t.Run("obj to kind", func(t *testing.T) { 26 | for _, tt := range tests { 27 | assert.Equal(t, ObjToGroupKind(tt.obj), tt.kind) 28 | } 29 | }) 30 | 31 | t.Run("kind to obj", func(t *testing.T) { 32 | for _, tt := range tests { 33 | kind, _ := GroupKindToObj(tt.kind) 34 | assert.Equal(t, kind, tt.obj) 35 | } 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /pkg/k8s/policyhelper/policy_test.go: -------------------------------------------------------------------------------- 1 | package policyhelper 2 | 3 | import ( 4 | "testing" 5 | 6 | anv1alpha1 "github.com/aws/aws-application-networking-k8s/pkg/apis/applicationnetworking/v1alpha1" 7 | "github.com/stretchr/testify/assert" 8 | "sigs.k8s.io/controller-runtime/pkg/client" 9 | gwv1 "sigs.k8s.io/gateway-api/apis/v1" 10 | ) 11 | 12 | func TestPolicyClient(t *testing.T) { 13 | type iap = anv1alpha1.IAMAuthPolicy 14 | type iapl = anv1alpha1.IAMAuthPolicyList 15 | 16 | t.Run("new list and policy", func(t *testing.T) { 17 | c := newK8sPolicyClient[iap, iapl](nil) 18 | assert.NotNil(t, c.newPolicy()) 19 | assert.NotNil(t, c.newList()) 20 | }) 21 | } 22 | 23 | func TestPolicyHandler(t *testing.T) { 24 | type iap = anv1alpha1.IAMAuthPolicy 25 | type iapl = anv1alpha1.IAMAuthPolicyList 26 | 27 | phcfg := PolicyHandlerConfig{} 28 | _ = NewPolicyHandler[iap, iapl](phcfg) 29 | } 30 | 31 | func TestGroupKindSet(t *testing.T) { 32 | objs := []client.Object{&gwv1.Gateway{}, &gwv1.HTTPRoute{}, &gwv1.GRPCRoute{}} 33 | gks := NewGroupKindSet(objs...) 34 | assert.True(t, gks.Contains(GroupKind{gwv1.GroupName, "Gateway"})) 35 | assert.True(t, gks.Contains(GroupKind{gwv1.GroupName, "HTTPRoute"})) 36 | assert.True(t, gks.Contains(GroupKind{gwv1.GroupName, "GRPCRoute"})) 37 | } 38 | -------------------------------------------------------------------------------- /pkg/model/core/fake_resource.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/pkg/errors" 7 | ) 8 | 9 | var _ Resource = &FakeResource{} 10 | 11 | func NewFakeResource(stack Stack, resType string, id string, spec FakeResourceSpec, status *FakeResourceStatus) *FakeResource { 12 | r := &FakeResource{ 13 | ResourceMeta: NewResourceMeta(stack, resType, id), 14 | Spec: spec, 15 | Status: status, 16 | } 17 | stack.AddResource(r) 18 | return r 19 | } 20 | 21 | func (r *FakeResource) FieldB() StringToken { 22 | return NewResourceFieldStringToken(r, "status/fieldB", 23 | func(ctx context.Context, res Resource, fieldPath string) (s string, err error) { 24 | r := res.(*FakeResource) 25 | if r.Status == nil { 26 | return "", errors.Errorf("FakeResource is not fulfilled yet: %v", r.ID()) 27 | } 28 | return r.Status.FieldB, nil 29 | }, 30 | ) 31 | } 32 | 33 | type FakeResource struct { 34 | ResourceMeta `json:"-"` 35 | 36 | Spec FakeResourceSpec `json:"spec"` 37 | Status *FakeResourceStatus `json:"status,omitempty"` 38 | } 39 | 40 | type FakeResourceSpec struct { 41 | FieldA []StringToken `json:"fieldA"` 42 | } 43 | 44 | type FakeResourceStatus struct { 45 | FieldB string `json:"fieldB"` 46 | } 47 | -------------------------------------------------------------------------------- /pkg/model/core/graph/resource_graph.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import "reflect" 4 | 5 | // unique ID for a resource. 6 | type ResourceUID struct { 7 | ResType reflect.Type 8 | ResID string 9 | } 10 | 11 | // ResourceGraph is an abstraction of resource DAG. 12 | type ResourceGraph interface { 13 | // Add a node into ResourceGraph. 14 | AddNode(node ResourceUID) 15 | 16 | // Add a edge into ResourceGraph, where dstNode depends on srcNode. 17 | AddEdge(srcNode ResourceUID, dstNode ResourceUID) 18 | 19 | // Nodes returns all nodes in ResourceGraph. 20 | Nodes() []ResourceUID 21 | 22 | // OutEdgeNodes returns all nodes that depends on this node. 23 | OutEdgeNodes(node ResourceUID) []ResourceUID 24 | } 25 | 26 | // NewDefaultResourceGraph constructs new defaultResourceGraph. 27 | func NewDefaultResourceGraph() *defaultResourceGraph { 28 | return &defaultResourceGraph{ 29 | nodes: nil, 30 | outEdges: make(map[ResourceUID][]ResourceUID), 31 | } 32 | } 33 | 34 | var _ ResourceGraph = &defaultResourceGraph{} 35 | 36 | // defaultResourceGraph is the default implementation for ResourceGraph. 37 | type defaultResourceGraph struct { 38 | nodes []ResourceUID 39 | outEdges map[ResourceUID][]ResourceUID 40 | } 41 | 42 | // Add a node into ResourceGraph. 43 | func (g *defaultResourceGraph) AddNode(node ResourceUID) { 44 | g.nodes = append(g.nodes, node) 45 | } 46 | 47 | // Add a edge into ResourceGraph, where dstNode depends on srcNode. 48 | func (g *defaultResourceGraph) AddEdge(srcNode ResourceUID, dstNode ResourceUID) { 49 | g.outEdges[srcNode] = append(g.outEdges[srcNode], dstNode) 50 | } 51 | 52 | // Nodes returns all nodes in ResourceGraph. 53 | func (g *defaultResourceGraph) Nodes() []ResourceUID { 54 | return g.nodes 55 | } 56 | 57 | // OutEdgeNodes returns all nodes that depends on this node. 58 | func (g *defaultResourceGraph) OutEdgeNodes(node ResourceUID) []ResourceUID { 59 | return g.outEdges[node] 60 | } 61 | -------------------------------------------------------------------------------- /pkg/model/core/graph/typological_traversal.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import ( 4 | "github.com/pkg/errors" 5 | ) 6 | 7 | // TopologicalTraversal will traversal nodes in typological order. 8 | // @TODO: change this traversal to be parallel. 9 | func TopologicalTraversal(graph ResourceGraph, visitFunc func(uid ResourceUID) error) error { 10 | nodes := graph.Nodes() 11 | indegreeByNode := make(map[ResourceUID]int, len(nodes)) 12 | for _, node := range nodes { 13 | if _, ok := indegreeByNode[node]; !ok { 14 | indegreeByNode[node] = 0 15 | } 16 | for _, outEdgeNode := range graph.OutEdgeNodes(node) { 17 | indegreeByNode[outEdgeNode]++ 18 | } 19 | 20 | } 21 | 22 | var queue []ResourceUID 23 | for node, indegree := range indegreeByNode { 24 | if indegree == 0 { 25 | queue = append(queue, node) 26 | } 27 | } 28 | 29 | for len(queue) > 0 { 30 | node := queue[len(queue)-1] 31 | queue = queue[:len(queue)-1] 32 | if err := visitFunc(node); err != nil { 33 | return err 34 | } 35 | 36 | for _, outEdgeNode := range graph.OutEdgeNodes(node) { 37 | indegreeByNode[outEdgeNode]-- 38 | if indegreeByNode[outEdgeNode] == 0 { 39 | queue = append(queue, outEdgeNode) 40 | } 41 | } 42 | } 43 | 44 | for _, indegree := range indegreeByNode { 45 | if indegree > 0 { 46 | return errors.New("ResourceGraph is not a DAG") 47 | } 48 | } 49 | return nil 50 | } 51 | -------------------------------------------------------------------------------- /pkg/model/core/policy.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | apimachineryv1 "k8s.io/apimachinery/pkg/apis/meta/v1" 5 | "k8s.io/apimachinery/pkg/types" 6 | "sigs.k8s.io/controller-runtime/pkg/client" 7 | gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" 8 | ) 9 | 10 | type Policy interface { 11 | client.Object 12 | GetNamespacedName() types.NamespacedName 13 | GetTargetRef() *gwv1alpha2.NamespacedPolicyTargetReference 14 | GetStatusConditions() []apimachineryv1.Condition 15 | SetStatusConditions(conditions []apimachineryv1.Condition) 16 | } 17 | 18 | type PolicyList interface { 19 | GetItems() []Policy 20 | } 21 | -------------------------------------------------------------------------------- /pkg/model/core/resource.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | // Resource represents a deployment unit. 4 | type Resource interface { 5 | // resource's stack. 6 | Stack() Stack 7 | 8 | // resource's Type. 9 | Type() string 10 | 11 | // resource's ID within stack. 12 | ID() string 13 | } 14 | 15 | // NewResourceMeta constructs new resource metadata. 16 | func NewResourceMeta(stack Stack, resType string, id string) ResourceMeta { 17 | return ResourceMeta{ 18 | stack: stack, 19 | resType: resType, 20 | id: id, 21 | } 22 | } 23 | 24 | // Metadata for all resources. 25 | type ResourceMeta struct { 26 | stack Stack 27 | resType string 28 | id string 29 | } 30 | 31 | func (m *ResourceMeta) Stack() Stack { 32 | return m.stack 33 | } 34 | 35 | func (m *ResourceMeta) Type() string { 36 | return m.resType 37 | } 38 | 39 | func (m *ResourceMeta) ID() string { 40 | return m.id 41 | } 42 | 43 | // ResourceVisitor represents a functor that can operate on a resource. 44 | type ResourceVisitor interface { 45 | Visit(res Resource) error 46 | } 47 | 48 | type EventType string 49 | 50 | const ( 51 | CreateEvent EventType = "Create" 52 | UpdateEvent EventType = "Update" 53 | DeleteEvent EventType = "Delete" 54 | ) 55 | -------------------------------------------------------------------------------- /pkg/model/core/stack_id.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "fmt" 5 | "k8s.io/apimachinery/pkg/types" 6 | ) 7 | 8 | // stackId is the identifier of a stack, it must be compatible with Kubernetes namespaced name. 9 | type StackID types.NamespacedName 10 | 11 | // String returns the string representation of a StackID. 12 | // It will be used as AWS resource tags for resources provisioned for this stack. 13 | func (stackID StackID) String() string { 14 | if stackID.Namespace == "" { 15 | return stackID.Name 16 | } 17 | return fmt.Sprintf("%s/%s", stackID.Namespace, stackID.Name) 18 | } 19 | -------------------------------------------------------------------------------- /pkg/model/core/token_types.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | ) 7 | 8 | // Token represent a value that can be resolved at resolution time. 9 | type Token interface { 10 | // token's value resolution may depends on 0 or more Resources. 11 | Dependencies() []Resource 12 | } 13 | 14 | // StringToken represent a string value that can be resolved at resolution time. 15 | type StringToken interface { 16 | Token 17 | Resolve(ctx context.Context) (string, error) 18 | } 19 | 20 | var _ StringToken = LiteralStringToken("") 21 | 22 | // LiteralStringToken represents a literal string value. 23 | type LiteralStringToken string 24 | 25 | func (t LiteralStringToken) Resolve(ctx context.Context) (string, error) { 26 | return string(t), nil 27 | } 28 | 29 | func (t LiteralStringToken) Dependencies() []Resource { 30 | return nil 31 | } 32 | 33 | // NewResourceFieldStringToken constructs new ResourceFieldStringToken. 34 | // @TODO: ideally the resolverFunc can be a shared implementation which dump Resource as json and obtain the fieldPath. 35 | func NewResourceFieldStringToken(res Resource, fieldPath string, 36 | resolverFunc func(ctx context.Context, res Resource, fieldPath string) (string, error)) *ResourceFieldStringToken { 37 | return &ResourceFieldStringToken{ 38 | res: res, 39 | fieldPath: fieldPath, 40 | resolveFunc: resolverFunc, 41 | } 42 | } 43 | 44 | var _ StringToken = &ResourceFieldStringToken{} 45 | 46 | type ResourceFieldStringToken struct { 47 | res Resource 48 | fieldPath string 49 | resolveFunc func(ctx context.Context, res Resource, fieldPath string) (string, error) 50 | } 51 | 52 | func (t *ResourceFieldStringToken) Resolve(ctx context.Context) (string, error) { 53 | return t.resolveFunc(ctx, t.res, t.fieldPath) 54 | } 55 | 56 | func (t *ResourceFieldStringToken) Dependencies() []Resource { 57 | return []Resource{t.res} 58 | } 59 | 60 | func (t *ResourceFieldStringToken) MarshalJSON() ([]byte, error) { 61 | payload := fmt.Sprintf(`{"$ref": "#/resources/%v/%v/%v"}`, t.res.Type(), t.res.ID(), t.fieldPath) 62 | return []byte(payload), nil 63 | } 64 | -------------------------------------------------------------------------------- /pkg/model/lattice/accesslogsubscription.go: -------------------------------------------------------------------------------- 1 | package lattice 2 | 3 | import ( 4 | "fmt" 5 | 6 | "k8s.io/apimachinery/pkg/types" 7 | 8 | "github.com/aws/aws-application-networking-k8s/pkg/aws" 9 | "github.com/aws/aws-application-networking-k8s/pkg/model/core" 10 | ) 11 | 12 | const AccessLogPolicyTagKey = aws.TagBase + "AccessLogPolicy" 13 | 14 | type SourceType string 15 | 16 | const ( 17 | ServiceNetworkSourceType SourceType = "ServiceNetwork" 18 | ServiceSourceType SourceType = "Service" 19 | ) 20 | 21 | type AccessLogSubscription struct { 22 | core.ResourceMeta `json:"-"` 23 | Spec AccessLogSubscriptionSpec `json:"spec"` 24 | Status *AccessLogSubscriptionStatus `json:"status,omitempty"` 25 | } 26 | 27 | type AccessLogSubscriptionSpec struct { 28 | SourceType SourceType 29 | SourceName string 30 | DestinationArn string 31 | ALPNamespacedName types.NamespacedName 32 | EventType core.EventType 33 | } 34 | 35 | type AccessLogSubscriptionStatus struct { 36 | Arn string `json:"arn"` 37 | } 38 | 39 | func NewAccessLogSubscription( 40 | stack core.Stack, 41 | spec AccessLogSubscriptionSpec, 42 | status *AccessLogSubscriptionStatus, 43 | ) *AccessLogSubscription { 44 | id := fmt.Sprintf("%s-%s-%s", spec.SourceType, spec.SourceName, spec.DestinationArn) 45 | return &AccessLogSubscription{ 46 | ResourceMeta: core.NewResourceMeta(stack, "AWS::VPCServiceNetwork::AccessLogSubscription", id), 47 | Spec: spec, 48 | Status: status, 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /pkg/model/lattice/iamauthpolicy.go: -------------------------------------------------------------------------------- 1 | package lattice 2 | 3 | import ( 4 | "fmt" 5 | 6 | anv1alpha1 "github.com/aws/aws-application-networking-k8s/pkg/apis/applicationnetworking/v1alpha1" 7 | "github.com/aws/aws-application-networking-k8s/pkg/utils" 8 | ) 9 | 10 | type IAMAuthPolicy struct { 11 | Type string 12 | Name string 13 | ResourceId string 14 | Policy string 15 | } 16 | 17 | type IAMAuthPolicyStatus struct { 18 | ResourceId string 19 | } 20 | 21 | func NewIAMAuthPolicy(k8sPolicy *anv1alpha1.IAMAuthPolicy) IAMAuthPolicy { 22 | kind := k8sPolicy.Spec.TargetRef.Kind 23 | policy := k8sPolicy.Spec.Policy 24 | switch kind { 25 | case "Gateway": 26 | return IAMAuthPolicy{ 27 | Type: ServiceNetworkType, 28 | Name: string(k8sPolicy.Spec.TargetRef.Name), 29 | Policy: policy, 30 | } 31 | case "HTTPRoute", "GRPCRoute": 32 | return IAMAuthPolicy{ 33 | Type: ServiceType, 34 | Name: utils.LatticeServiceName(string(k8sPolicy.Spec.TargetRef.Name), k8sPolicy.Namespace), 35 | Policy: policy, 36 | } 37 | default: 38 | panic(fmt.Sprintf("unexpected targetRef, Kind=%s", kind)) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /pkg/model/lattice/service.go: -------------------------------------------------------------------------------- 1 | package lattice 2 | 3 | import ( 4 | "github.com/aws/aws-application-networking-k8s/pkg/aws/services" 5 | "github.com/aws/aws-application-networking-k8s/pkg/model/core" 6 | "github.com/aws/aws-application-networking-k8s/pkg/utils" 7 | ) 8 | 9 | type Service struct { 10 | core.ResourceMeta `json:"-"` 11 | Spec ServiceSpec `json:"spec"` 12 | Status *ServiceStatus `json:"status,omitempty"` 13 | IsDeleted bool `json:"isdeleted"` 14 | } 15 | 16 | type ServiceSpec struct { 17 | ServiceTagFields 18 | ServiceNetworkNames []string `json:"servicenetworkhnames"` 19 | CustomerDomainName string `json:"customerdomainname"` 20 | CustomerCertARN string `json:"customercertarn"` 21 | } 22 | 23 | type ServiceStatus struct { 24 | Arn string `json:"arn"` 25 | Id string `json:"id"` 26 | Dns string `json:"dns"` 27 | } 28 | 29 | type ServiceTagFields struct { 30 | RouteName string 31 | RouteNamespace string 32 | RouteType core.RouteType 33 | } 34 | 35 | func ServiceTagFieldsFromTags(tags map[string]*string) ServiceTagFields { 36 | return ServiceTagFields{ 37 | RouteName: getMapValue(tags, K8SRouteNameKey), 38 | RouteNamespace: getMapValue(tags, K8SRouteNamespaceKey), 39 | RouteType: core.RouteType(getMapValue(tags, K8SRouteTypeKey)), 40 | } 41 | } 42 | 43 | func (t *ServiceTagFields) ToTags() services.Tags { 44 | rt := string(t.RouteType) 45 | return services.Tags{ 46 | K8SRouteNameKey: &t.RouteName, 47 | K8SRouteNamespaceKey: &t.RouteNamespace, 48 | K8SRouteTypeKey: &rt, 49 | } 50 | } 51 | 52 | func NewLatticeService(stack core.Stack, spec ServiceSpec) (*Service, error) { 53 | id := spec.LatticeServiceName() 54 | 55 | service := &Service{ 56 | ResourceMeta: core.NewResourceMeta(stack, "AWS::VPCServiceNetwork::Service", id), 57 | Spec: spec, 58 | Status: nil, 59 | } 60 | 61 | err := stack.AddResource(service) 62 | if err != nil { 63 | return nil, err 64 | } 65 | 66 | return service, nil 67 | } 68 | 69 | func (s *Service) LatticeServiceName() string { 70 | return s.Spec.LatticeServiceName() 71 | } 72 | 73 | func (s *ServiceSpec) LatticeServiceName() string { 74 | return utils.LatticeServiceName(s.RouteName, s.RouteNamespace) 75 | } 76 | -------------------------------------------------------------------------------- /pkg/model/lattice/servicenetwork.go: -------------------------------------------------------------------------------- 1 | package lattice 2 | 3 | import ( 4 | "github.com/aws/aws-application-networking-k8s/pkg/model/core" 5 | ) 6 | 7 | const ( 8 | K8SServiceNetworkOwnedByVPC = "K8SServiceNetworkOwnedByVPC" 9 | K8SServiceOwnedByVPC = "K8SServiceOwnedByVPC" 10 | ) 11 | 12 | type ServiceNetwork struct { 13 | core.ResourceMeta `json:"-"` 14 | Spec ServiceNetworkSpec `json:"spec"` 15 | Status *ServiceNetworkStatus `json:"status,omitempty"` 16 | } 17 | 18 | type ServiceNetworkSpec struct { 19 | // The name of the ServiceNetwork 20 | Name string `json:"name"` 21 | Namespace string `json:"namespace"` 22 | Account string `json:"account"` 23 | SecurityGroupIds []*string `json:"securityGroupIds"` 24 | AssociateToVPC bool 25 | IsDeleted bool 26 | } 27 | 28 | type ServiceNetworkStatus struct { 29 | ServiceNetworkARN string `json:"servicenetworkARN"` 30 | ServiceNetworkID string `json:"servicenetworkID"` 31 | SnvaSecurityGroupIds []*string `json:"securityGroupIds"` 32 | } 33 | 34 | func NewServiceNetwork(stack core.Stack, id string, spec ServiceNetworkSpec) *ServiceNetwork { 35 | 36 | servicenetwork := &ServiceNetwork{ 37 | //TODO right name 38 | ResourceMeta: core.NewResourceMeta(stack, "AWS::VPCServiceNetwork::ServiceNetwork", id), 39 | Spec: spec, 40 | Status: nil, 41 | } 42 | 43 | stack.AddResource(servicenetwork) 44 | // TODO: servicenetwork.registerDependencies(stack) 45 | 46 | return servicenetwork 47 | 48 | } 49 | -------------------------------------------------------------------------------- /pkg/model/lattice/targets.go: -------------------------------------------------------------------------------- 1 | package lattice 2 | 3 | import ( 4 | "github.com/aws/aws-application-networking-k8s/pkg/model/core" 5 | "k8s.io/apimachinery/pkg/types" 6 | ) 7 | 8 | type Targets struct { 9 | core.ResourceMeta `json:"-"` 10 | Spec TargetsSpec `json:"spec"` 11 | } 12 | 13 | // unlike target groups, which can reference a service export, targets 14 | // are always sourced from the local cluster. When we update targets, 15 | // we find all the target groups linked to the specific service 16 | type TargetsSpec struct { 17 | StackTargetGroupId string `json:"stacktargetgroupid"` 18 | TargetList []Target `json:"targetlist"` 19 | } 20 | 21 | type Target struct { 22 | TargetIP string `json:"targetip"` 23 | Port int64 `json:"port"` 24 | Ready bool `json:"ready"` 25 | TargetRef types.NamespacedName 26 | } 27 | 28 | func NewTargets(stack core.Stack, spec TargetsSpec) (*Targets, error) { 29 | id, err := core.IdFromHash(spec) 30 | if err != nil { 31 | return nil, err 32 | } 33 | targets := &Targets{ 34 | ResourceMeta: core.NewResourceMeta(stack, "AWS:VPCServiceNetwork::Targets", id), 35 | Spec: spec, 36 | } 37 | 38 | stack.AddResource(targets) 39 | 40 | return targets, nil 41 | } 42 | -------------------------------------------------------------------------------- /pkg/model/lattice/types.go: -------------------------------------------------------------------------------- 1 | package lattice 2 | 3 | const ( 4 | ServiceNetworkType = "ServiceNetwork" 5 | ServiceType = "Service" 6 | ) 7 | -------------------------------------------------------------------------------- /pkg/runtime/errors.go: -------------------------------------------------------------------------------- 1 | package runtime 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/aws/aws-application-networking-k8s/pkg/deploy/lattice" 9 | ) 10 | 11 | type RetryError error 12 | 13 | func NewRetryError() RetryError { 14 | return RetryError(errors.New(lattice.LATTICE_RETRY)) 15 | } 16 | 17 | // NewRequeueNeeded constructs new RequeueError to 18 | // instruct controller-runtime to requeue the processing item without been logged as error. 19 | func NewRequeueNeeded(reason string) *RequeueNeeded { 20 | return &RequeueNeeded{ 21 | reason: reason, 22 | } 23 | } 24 | 25 | // NewRequeueNeededAfter constructs new RequeueNeededAfter to 26 | // instruct controller-runtime to requeue the processing item after specified duration without been logged as error. 27 | func NewRequeueNeededAfter(reason string, duration time.Duration) *RequeueNeededAfter { 28 | return &RequeueNeededAfter{ 29 | reason: reason, 30 | duration: duration, 31 | } 32 | } 33 | 34 | var _ error = &RequeueNeeded{} 35 | 36 | // An error to instruct controller-runtime to requeue the processing item without been logged as error. 37 | // This should be used when a "error condition" occurrence is sort of expected and can be resolved by retry. 38 | // e.g. a dependency haven't been fulfilled yet. 39 | type RequeueNeeded struct { 40 | reason string 41 | } 42 | 43 | func (e *RequeueNeeded) Reason() string { 44 | return e.reason 45 | } 46 | 47 | func (e *RequeueNeeded) Error() string { 48 | return fmt.Sprintf("requeue needed: %v", e.reason) 49 | } 50 | 51 | var _ error = &RequeueNeededAfter{} 52 | 53 | // An error to instruct controller-runtime to requeue the processing item after specified duration without been logged as error. 54 | // This should be used when a "error condition" occurrence is sort of expected and can be resolved by retry. 55 | // e.g. a dependency haven't been fulfilled yet, and expected it to be fulfilled after duration. 56 | // Note: use this with care,a simple wait might suits your use case better. 57 | type RequeueNeededAfter struct { 58 | reason string 59 | duration time.Duration 60 | } 61 | 62 | func (e *RequeueNeededAfter) Reason() string { 63 | return e.reason 64 | } 65 | 66 | func (e *RequeueNeededAfter) Duration() time.Duration { 67 | return e.duration 68 | } 69 | 70 | func (e *RequeueNeededAfter) Error() string { 71 | return fmt.Sprintf("requeue needed after %v: %v", e.duration, e.reason) 72 | } 73 | -------------------------------------------------------------------------------- /pkg/runtime/reconcile.go: -------------------------------------------------------------------------------- 1 | package runtime 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "time" 7 | 8 | ctrl "sigs.k8s.io/controller-runtime" 9 | ) 10 | 11 | // HandleReconcileError will handle errors from reconcile handlers, which respects runtime errors. 12 | func HandleReconcileError(err error) (ctrl.Result, error) { 13 | if err == nil { 14 | return ctrl.Result{}, nil 15 | } 16 | 17 | retryErr := NewRetryError() 18 | if errors.As(err, &retryErr) { 19 | return ctrl.Result{RequeueAfter: time.Second * 20}, nil 20 | } 21 | 22 | var requeueNeededAfter *RequeueNeededAfter 23 | if errors.As(err, &requeueNeededAfter) { 24 | return ctrl.Result{RequeueAfter: requeueNeededAfter.Duration()}, nil 25 | } 26 | 27 | var requeueNeeded *RequeueNeeded 28 | if errors.As(err, &requeueNeeded) { 29 | fmt.Print("requeue", "reason", requeueNeeded.Reason()) 30 | return ctrl.Result{Requeue: true}, nil 31 | } 32 | 33 | return ctrl.Result{RequeueAfter: time.Minute * 10}, err 34 | } 35 | -------------------------------------------------------------------------------- /pkg/utils/common_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestChunks(t *testing.T) { 11 | 12 | type test struct { 13 | sliceLen int 14 | chunkSize int 15 | wantChunksLen int 16 | wantLastChunkLen int 17 | } 18 | 19 | tests := []test{ 20 | {0, -1, 0, 0}, 21 | {0, 0, 0, 0}, 22 | {0, 1, 0, 0}, 23 | {1, 1, 1, 1}, 24 | {2, 1, 2, 1}, 25 | {10, 2, 5, 2}, 26 | {102, 10, 11, 2}, 27 | } 28 | 29 | for _, tt := range tests { 30 | t.Run(fmt.Sprintf("sliceLen=%d, chunkSize=%d", tt.sliceLen, tt.chunkSize), func(t *testing.T) { 31 | slice := makeIntSlice(tt.sliceLen) 32 | chunks := Chunks(slice, tt.chunkSize) 33 | gotChunksLen := len(chunks) 34 | if gotChunksLen != tt.wantChunksLen { 35 | t.Errorf("number of chunks does not match, want=%d, got=%d", tt.wantChunksLen, gotChunksLen) 36 | } 37 | if gotChunksLen > 0 { 38 | lastChunk := chunks[gotChunksLen-1] 39 | if len(lastChunk) != tt.wantLastChunkLen { 40 | t.Errorf("last chunk size does not match, want=%d, got=%d", tt.wantLastChunkLen, len(lastChunk)) 41 | } 42 | } 43 | }) 44 | } 45 | } 46 | 47 | func makeIntSlice(s int) []int { 48 | out := make([]int, s) 49 | for i := 0; i < s; i++ { 50 | out[i] = i 51 | } 52 | return out 53 | } 54 | 55 | func TestSet(t *testing.T) { 56 | 57 | t.Run("empty set", func(t *testing.T) { 58 | s := Set[int]{} 59 | s.Put(1) 60 | assert.True(t, s.Contains(1)) 61 | }) 62 | 63 | } 64 | -------------------------------------------------------------------------------- /pkg/utils/condition.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 5 | ) 6 | 7 | func GetNewConditions(conditions []v1.Condition, newCond v1.Condition) []v1.Condition { 8 | newConditions := make([]v1.Condition, 0) 9 | 10 | found := false 11 | for _, cond := range conditions { 12 | if cond.Type == newCond.Type { 13 | // Update existing condition. Time is kept only if status is unchanged. 14 | newCond.LastTransitionTime = cond.LastTransitionTime 15 | if cond.Status != newCond.Status { 16 | newCond.LastTransitionTime = v1.Now() 17 | } 18 | newConditions = append(newConditions, newCond) 19 | found = true 20 | } else { 21 | newConditions = append(newConditions, cond) 22 | } 23 | } 24 | 25 | if !found { 26 | // Add new condition instead. 27 | newCond.LastTransitionTime = v1.Now() 28 | newConditions = append(newConditions, newCond) 29 | } 30 | 31 | return newConditions 32 | } 33 | -------------------------------------------------------------------------------- /pkg/utils/gwlog/actions.go: -------------------------------------------------------------------------------- 1 | package gwlog 2 | 3 | const ReconcileStart = "reconcile_start" 4 | const ReconcileEnd = "reconcile_end" 5 | -------------------------------------------------------------------------------- /pkg/utils/gwlog/metadata.go: -------------------------------------------------------------------------------- 1 | package gwlog 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/google/uuid" 7 | ) 8 | 9 | type key string 10 | 11 | const metadataKey key = "metadata_key" 12 | const traceID string = "trace_id" 13 | 14 | type metadata struct { 15 | m map[string]string 16 | } 17 | 18 | func (mv *metadata) set(key, val string) { 19 | mv.m[key] = val 20 | } 21 | 22 | func newMetadata() *metadata { 23 | return &metadata{ 24 | m: make(map[string]string), 25 | } 26 | } 27 | 28 | func NewTrace(ctx context.Context) context.Context { 29 | currID := uuid.New() 30 | 31 | newCtx := context.WithValue(ctx, metadataKey, newMetadata()) 32 | AddMetadata(newCtx, traceID, currID.String()) 33 | 34 | return newCtx 35 | } 36 | 37 | func AddMetadata(ctx context.Context, key, value string) { 38 | if ctx.Value(metadataKey) != nil { 39 | ctx.Value(metadataKey).(*metadata).set(key, value) 40 | } 41 | } 42 | 43 | func getMetadata(ctx context.Context) []interface{} { 44 | var fields []interface{} 45 | 46 | if ctx.Value(metadataKey) != nil { 47 | for k, v := range ctx.Value(metadataKey).(*metadata).m { 48 | if k == traceID { 49 | // skip since there's a separate method to grab the trace id 50 | continue 51 | } 52 | fields = append(fields, k) 53 | fields = append(fields, v) 54 | } 55 | } 56 | return fields 57 | } 58 | 59 | func GetTraceID(ctx context.Context) string { 60 | if ctx.Value(metadataKey) != nil { 61 | m := ctx.Value(metadataKey).(*metadata).m 62 | if m == nil { 63 | return "" 64 | } 65 | return ctx.Value(metadataKey).(*metadata).m[traceID] 66 | } 67 | return "" 68 | } 69 | 70 | func StartReconcileTrace(ctx context.Context, log Logger, k8sresourcetype, name, namespace string) context.Context { 71 | ctx = NewTrace(ctx) 72 | AddMetadata(ctx, "type", k8sresourcetype) 73 | AddMetadata(ctx, "name", name) 74 | AddMetadata(ctx, "namespace", namespace) 75 | 76 | log.Infow(ctx, ReconcileStart, getMetadata(ctx)...) 77 | 78 | return ctx 79 | } 80 | 81 | func EndReconcileTrace(ctx context.Context, log Logger) { 82 | log.Infow(ctx, ReconcileEnd, getMetadata(ctx)...) 83 | } 84 | -------------------------------------------------------------------------------- /pkg/utils/gwlog/metadata_test.go: -------------------------------------------------------------------------------- 1 | package gwlog 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func TestGetTrace(t *testing.T) { 10 | if GetTraceID(context.TODO()) != "" { 11 | t.Errorf("expected context with no trace_id to return empty string") 12 | } 13 | 14 | if GetTraceID(NewTrace(context.TODO())) == "" { 15 | t.Errorf("expected context with trace_id to return non-empty string") 16 | } 17 | } 18 | 19 | func TestMetadata(t *testing.T) { 20 | ctx := NewTrace(context.TODO()) 21 | AddMetadata(ctx, "foo", "bar") 22 | 23 | md := getMetadata(ctx) 24 | mdMap := map[string]bool{} 25 | for _, m := range md { 26 | mdMap[fmt.Sprint(m)] = true 27 | } 28 | 29 | if !mdMap["foo"] || !mdMap["bar"] { 30 | t.Errorf("expected context to have metadata with key foo and val bar, got %s", md) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /pkg/utils/pod_condition.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | corev1 "k8s.io/api/core/v1" 5 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 6 | "time" 7 | ) 8 | 9 | func PodHasReadinessGate(pod *corev1.Pod, conditionType corev1.PodConditionType) bool { 10 | if pod == nil { 11 | return false 12 | } 13 | for _, gate := range pod.Spec.ReadinessGates { 14 | if gate.ConditionType == conditionType { 15 | return true 16 | } 17 | } 18 | return false 19 | } 20 | 21 | // Copied from: k8s.io/apimachinery/pkg/apis/meta 22 | func FindPodStatusCondition(conditions []corev1.PodCondition, conditionType corev1.PodConditionType) *corev1.PodCondition { 23 | for i := range conditions { 24 | if conditions[i].Type == conditionType { 25 | return &conditions[i] 26 | } 27 | } 28 | return nil 29 | } 30 | 31 | // Copied from: k8s.io/apimachinery/pkg/apis/meta 32 | func SetPodStatusCondition(conditions *[]corev1.PodCondition, newCondition corev1.PodCondition) { 33 | if conditions == nil { 34 | return 35 | } 36 | existingCondition := FindPodStatusCondition(*conditions, newCondition.Type) 37 | if existingCondition == nil { 38 | if newCondition.LastTransitionTime.IsZero() { 39 | newCondition.LastTransitionTime = metav1.NewTime(time.Now()) 40 | } 41 | *conditions = append(*conditions, newCondition) 42 | return 43 | } 44 | 45 | if existingCondition.Status != newCondition.Status { 46 | existingCondition.Status = newCondition.Status 47 | if !newCondition.LastTransitionTime.IsZero() { 48 | existingCondition.LastTransitionTime = newCondition.LastTransitionTime 49 | } else { 50 | existingCondition.LastTransitionTime = metav1.NewTime(time.Now()) 51 | } 52 | } 53 | 54 | existingCondition.Reason = newCondition.Reason 55 | existingCondition.Message = newCondition.Message 56 | } 57 | -------------------------------------------------------------------------------- /pkg/utils/priority_queue.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "fmt" 4 | 5 | // An Item is something we manage in a priority queue. 6 | type Item struct { 7 | Value any 8 | Priority int32 // The priority of the item in the queue. 9 | // The Index is needed by update and is maintained by the heap.Interface methods. 10 | Index int // The Index of the item in the heap. 11 | } 12 | 13 | // A PriorityQueue implements heap.Interface and holds Items. 14 | type PriorityQueue []*Item 15 | 16 | func (pq PriorityQueue) Len() int { return len(pq) } 17 | 18 | func (pq PriorityQueue) Less(i, j int) bool { 19 | // We want Pop to give us the highest, not lowest, priority so we use greater than here. 20 | return pq[i].Priority < pq[j].Priority 21 | } 22 | 23 | func (pq PriorityQueue) Swap(i, j int) { 24 | pq[i], pq[j] = pq[j], pq[i] 25 | pq[i].Index = i 26 | pq[j].Index = j 27 | } 28 | 29 | func (pq *PriorityQueue) Push(x any) { 30 | n := len(*pq) 31 | item := x.(*Item) 32 | item.Index = n 33 | *pq = append(*pq, item) 34 | } 35 | 36 | // Peek returns the highest priority item without removing it from the queue. 37 | // Returns nil and an error if the queue is empty. 38 | func (pq *PriorityQueue) Peek() (*Item, error) { 39 | if len(*pq) == 0 { 40 | return nil, fmt.Errorf("priority queue is empty") 41 | } 42 | return (*pq)[len(*pq)-1], nil 43 | } 44 | 45 | func (pq *PriorityQueue) Pop() any { 46 | old := *pq 47 | n := len(old) 48 | item := old[n-1] 49 | old[n-1] = nil // don't stop the GC from reclaiming the item eventually 50 | item.Index = -1 // for safety 51 | *pq = old[0 : n-1] 52 | return item 53 | } 54 | -------------------------------------------------------------------------------- /pkg/utils/retry/backoff.go: -------------------------------------------------------------------------------- 1 | package retry 2 | 3 | import ( 4 | "math" 5 | "math/rand" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | // Backoff is the backoff interface 11 | type Backoff interface { 12 | Reset() 13 | Duration() time.Duration 14 | } 15 | 16 | // SimpleBackoff is the default simple backoff 17 | type SimpleBackoff struct { 18 | current time.Duration 19 | start time.Duration 20 | max time.Duration 21 | jitterMultiple float64 22 | multiple float64 23 | mu sync.Mutex 24 | } 25 | 26 | // NewSimpleBackoff creates a Backoff which ranges from min to max increasing by multiple each time. 27 | // It also adds (and yes, the jitter is always added, never subtracted) a random amount of jitter up to jitterMultiple 28 | // percent (that is, jitterMultiple = 0.0 is no jitter, 0.15 is 15% added jitter). The total time/ may exceed "max" 29 | // when accounting for jitter, such that the absolute max is max + max * jitterMultiple 30 | func NewSimpleBackoff(min, max time.Duration, jitterMultiple, multiple float64) *SimpleBackoff { 31 | return &SimpleBackoff{ 32 | start: min, 33 | current: min, 34 | max: max, 35 | jitterMultiple: jitterMultiple, 36 | multiple: multiple, 37 | } 38 | } 39 | 40 | // Duration gets the current duration including jitter 41 | func (sb *SimpleBackoff) Duration() time.Duration { 42 | sb.mu.Lock() 43 | defer sb.mu.Unlock() 44 | ret := sb.current 45 | sb.current = time.Duration(math.Min(float64(sb.max.Nanoseconds()), float64(sb.current.Nanoseconds())*sb.multiple)) 46 | return AddJitter(ret, time.Duration(int64(float64(ret)*sb.jitterMultiple))) 47 | } 48 | 49 | // Reset resets the backoff 50 | func (sb *SimpleBackoff) Reset() { 51 | sb.mu.Lock() 52 | defer sb.mu.Unlock() 53 | sb.current = sb.start 54 | } 55 | 56 | // AddJitter adds an amount of jitter between 0 and the given jitter to the given duration 57 | func AddJitter(duration time.Duration, jitter time.Duration) time.Duration { 58 | var randJitter int64 59 | if jitter.Nanoseconds() == 0 { 60 | randJitter = 0 61 | } else { 62 | randJitter = rand.Int63n(jitter.Nanoseconds()) 63 | } 64 | return time.Duration(duration.Nanoseconds() + randJitter) 65 | } 66 | -------------------------------------------------------------------------------- /pkg/utils/retry/errors.go: -------------------------------------------------------------------------------- 1 | package retry 2 | 3 | // Retriable is the retry interface 4 | type Retriable interface { 5 | Retry() bool 6 | } 7 | 8 | // DefaultRetriable is the default retryable 9 | type DefaultRetriable struct { 10 | retry bool 11 | } 12 | 13 | // Retry does the retry 14 | func (dr DefaultRetriable) Retry() bool { 15 | return dr.retry 16 | } 17 | 18 | // NewRetriable creates a new Retriable 19 | func NewRetriable(retry bool) Retriable { 20 | return DefaultRetriable{ 21 | retry: retry, 22 | } 23 | } 24 | 25 | // RetriableError interface 26 | type RetriableError interface { 27 | Retriable 28 | error 29 | } 30 | 31 | // DefaultRetriableError is the default retriable error 32 | type DefaultRetriableError struct { 33 | Retriable 34 | error 35 | } 36 | 37 | // NewRetriableError returns a new retriable error 38 | func NewRetriableError(retriable Retriable, err error) RetriableError { 39 | return &DefaultRetriableError{ 40 | retriable, 41 | err, 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /pkg/utils/retry/retry.go: -------------------------------------------------------------------------------- 1 | // Package retry is a retry with backoff implementation 2 | package retry 3 | 4 | import ( 5 | "context" 6 | 7 | "github.com/aws/aws-application-networking-k8s/pkg/utils/ttime" 8 | ) 9 | 10 | var _time ttime.Time = &ttime.DefaultTime{} 11 | 12 | // WithBackoff takes a Backoff and a function to call that returns an error 13 | // If the error is nil then the function will no longer be called 14 | // If the error is Retriable then that will be used to determine if it should be retried 15 | func WithBackoff(backoff Backoff, fn func() error) error { 16 | return WithBackoffCtx(context.Background(), backoff, fn) 17 | } 18 | 19 | // WithBackoffCtx takes a context, a Backoff, and a function to call that returns an error 20 | // If the context is done, nil will be returned 21 | // If the error is nil then the function will no longer be called 22 | // If the error is Retriable then that will be used to determine if it should be retried 23 | func WithBackoffCtx(ctx context.Context, backoff Backoff, fn func() error) error { 24 | var err error 25 | for { 26 | select { 27 | case <-ctx.Done(): 28 | return nil 29 | default: 30 | } 31 | err = fn() 32 | retriableErr, isRetriableErr := err.(Retriable) 33 | if err == nil || (isRetriableErr && !retriableErr.Retry()) { 34 | return err 35 | } 36 | _time.Sleep(backoff.Duration()) 37 | } 38 | } 39 | 40 | // NWithBackoff takes a Backoff, a maximum number of tries 'n', and a 41 | // function that returns an error. The function is called until either it does 42 | // not return an error or the maximum tries have been reached. 43 | // If the error returned is Retriable, the Retriability of it will be respected. 44 | // If the number of tries is exhausted, the last error will be returned. 45 | func NWithBackoff(backoff Backoff, n int, fn func() error) error { 46 | return NWithBackoffCtx(context.Background(), backoff, n, fn) 47 | } 48 | 49 | // NWithBackoffCtx takes a context, a Backoff, a maximum number of tries 'n', and a function that returns an error. 50 | // The function is called until it does not return an error, the context is done, or the maximum tries have been 51 | // reached. 52 | // If the error returned is Retriable, the Retriability of it will be respected. 53 | // If the number of tries is exhausted, the last error will be returned. 54 | func NWithBackoffCtx(ctx context.Context, backoff Backoff, n int, fn func() error) error { 55 | var err error 56 | _ = WithBackoffCtx(ctx, backoff, func() error { 57 | err = fn() 58 | n-- 59 | if n == 0 { 60 | // Break out after n tries 61 | return nil 62 | } 63 | return err 64 | }) 65 | return err 66 | } 67 | -------------------------------------------------------------------------------- /pkg/utils/ttime/ttime.go: -------------------------------------------------------------------------------- 1 | package ttime 2 | 3 | import "time" 4 | 5 | // Time represents an implementation for this package's methods 6 | type Time interface { 7 | Now() time.Time 8 | Sleep(d time.Duration) 9 | After(d time.Duration) <-chan time.Time 10 | AfterFunc(d time.Duration, f func()) Timer 11 | } 12 | 13 | // Timer is the timer interface 14 | type Timer interface { 15 | Reset(d time.Duration) bool 16 | Stop() bool 17 | } 18 | 19 | // DefaultTime is a Time that behaves normally 20 | type DefaultTime struct{} 21 | 22 | // Now returns the current time 23 | func (*DefaultTime) Now() time.Time { 24 | return time.Now() 25 | } 26 | 27 | // Sleep sleeps for the given duration 28 | func (*DefaultTime) Sleep(d time.Duration) { 29 | time.Sleep(d) 30 | } 31 | 32 | // After sleeps for the given duration and then writes to to the returned channel 33 | func (*DefaultTime) After(d time.Duration) <-chan time.Time { 34 | return time.After(d) 35 | } 36 | 37 | // AfterFunc waits for the duration to elapse and then calls f in its own 38 | // goroutine. It returns a Timer that can be used to cancel the call using its 39 | // Stop method. 40 | func (*DefaultTime) AfterFunc(d time.Duration, f func()) Timer { 41 | return time.AfterFunc(d, f) 42 | } 43 | -------------------------------------------------------------------------------- /pkg/webhook/core/context.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | "sigs.k8s.io/controller-runtime/pkg/webhook/admission" 6 | ) 7 | 8 | type contextKey string 9 | 10 | const ( 11 | contextKeyAdmissionRequest contextKey = "admissionRequest" 12 | ) 13 | 14 | func ContextGetAdmissionRequest(ctx context.Context) *admission.Request { 15 | if v := ctx.Value(contextKeyAdmissionRequest); v != nil { 16 | return v.(*admission.Request) 17 | } 18 | return nil 19 | } 20 | 21 | func ContextWithAdmissionRequest(ctx context.Context, req admission.Request) context.Context { 22 | return context.WithValue(ctx, contextKeyAdmissionRequest, &req) 23 | } 24 | -------------------------------------------------------------------------------- /pkg/webhook/core/context_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | "github.com/stretchr/testify/assert" 6 | admissionv1 "k8s.io/api/admission/v1" 7 | "sigs.k8s.io/controller-runtime/pkg/webhook/admission" 8 | "testing" 9 | ) 10 | 11 | func TestContextGetAdmissionRequestAndContextWithAdmissionRequest(t *testing.T) { 12 | type args struct { 13 | req *admission.Request 14 | } 15 | tests := []struct { 16 | name string 17 | args args 18 | want *admission.Request 19 | }{ 20 | { 21 | name: "with request", 22 | args: args{ 23 | req: &admission.Request{ 24 | AdmissionRequest: admissionv1.AdmissionRequest{ 25 | UID: "1", 26 | }, 27 | }, 28 | }, 29 | want: &admission.Request{ 30 | AdmissionRequest: admissionv1.AdmissionRequest{ 31 | UID: "1", 32 | }, 33 | }, 34 | }, 35 | { 36 | name: "without request", 37 | args: args{ 38 | req: nil, 39 | }, 40 | want: nil, 41 | }, 42 | } 43 | for _, tt := range tests { 44 | t.Run(tt.name, func(t *testing.T) { 45 | ctx := context.Background() 46 | if tt.args.req != nil { 47 | ctx = ContextWithAdmissionRequest(ctx, *tt.args.req) 48 | } 49 | got := ContextGetAdmissionRequest(ctx) 50 | assert.Equal(t, tt.want, got) 51 | }) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /pkg/webhook/core/mutator.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | "github.com/aws/aws-application-networking-k8s/pkg/utils/gwlog" 6 | "k8s.io/apimachinery/pkg/runtime" 7 | "sigs.k8s.io/controller-runtime/pkg/webhook/admission" 8 | ) 9 | 10 | //go:generate mockgen -destination mutator_mocks.go -package core github.com/aws/aws-application-networking-k8s/pkg/webhook/core Mutator 11 | type Mutator interface { 12 | // Prototype returns a prototype of Object for this admission request. 13 | Prototype(req admission.Request) (runtime.Object, error) 14 | 15 | // MutateCreate handles Object creation and returns the object after mutation and error if any. 16 | MutateCreate(ctx context.Context, obj runtime.Object) (runtime.Object, error) 17 | // MutateUpdate handles Object update and returns the object after mutation and error if any. 18 | MutateUpdate(ctx context.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) 19 | } 20 | 21 | // MutatingWebhookForMutator creates a new mutating Webhook. 22 | func MutatingWebhookForMutator(log gwlog.Logger, scheme *runtime.Scheme, mutator Mutator) *admission.Webhook { 23 | return &admission.Webhook{ 24 | Handler: &mutatingHandler{ 25 | log: log, 26 | mutator: mutator, 27 | decoder: admission.NewDecoder(scheme), 28 | }, 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /pkg/webhook/pod_mutator.go: -------------------------------------------------------------------------------- 1 | package webhook 2 | 3 | import ( 4 | "context" 5 | "github.com/aws/aws-application-networking-k8s/pkg/utils/gwlog" 6 | "github.com/aws/aws-application-networking-k8s/pkg/webhook/core" 7 | corev1 "k8s.io/api/core/v1" 8 | "k8s.io/apimachinery/pkg/runtime" 9 | ctrl "sigs.k8s.io/controller-runtime" 10 | "sigs.k8s.io/controller-runtime/pkg/webhook/admission" 11 | ) 12 | 13 | const ( 14 | apiPathMutatePod = "/mutate-pod" 15 | ) 16 | 17 | func NewPodMutator(log gwlog.Logger, scheme *runtime.Scheme, podReadinessGateInjector *PodReadinessGateInjector) *podMutator { 18 | return &podMutator{ 19 | log: log, 20 | podReadinessGateInjector: podReadinessGateInjector, 21 | scheme: scheme, 22 | } 23 | } 24 | 25 | var _ core.Mutator = &podMutator{} 26 | 27 | type podMutator struct { 28 | log gwlog.Logger 29 | podReadinessGateInjector *PodReadinessGateInjector 30 | scheme *runtime.Scheme 31 | } 32 | 33 | func (m *podMutator) Prototype(_ admission.Request) (runtime.Object, error) { 34 | return &corev1.Pod{}, nil 35 | } 36 | 37 | func (m *podMutator) MutateCreate(ctx context.Context, obj runtime.Object) (runtime.Object, error) { 38 | pod := obj.(*corev1.Pod) 39 | if err := m.podReadinessGateInjector.MutateCreate(ctx, pod); err != nil { 40 | return pod, err 41 | } 42 | return pod, nil 43 | } 44 | 45 | func (m *podMutator) MutateUpdate(ctx context.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) { 46 | return obj, nil 47 | } 48 | 49 | func (m *podMutator) SetupWithManager(log gwlog.Logger, mgr ctrl.Manager) { 50 | mgr.GetWebhookServer().Register(apiPathMutatePod, core.MutatingWebhookForMutator(log, m.scheme, m)) 51 | } 52 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Babel==2.13.1 2 | certifi==2024.7.4 3 | charset-normalizer==3.3.2 4 | click==8.1.7 5 | colorama==0.4.6 6 | ghp-import==2.1.0 7 | idna==3.7 8 | Jinja2==3.1.6 9 | Markdown==3.5.1 10 | MarkupSafe==2.1.3 11 | mergedeep==1.3.4 12 | mkdocs==1.5.3 13 | mkdocs-material==9.4.12 14 | mkdocs-material-extensions==1.3.1 15 | mike==2.1.1 16 | packaging==23.2 17 | paginate==0.5.6 18 | pathspec==0.11.2 19 | platformdirs==4.0.0 20 | Pygments==2.17.2 21 | pymdown-extensions==10.4 22 | python-dateutil==2.8.2 23 | PyYAML==6.0.1 24 | pyyaml_env_tag==0.1 25 | regex==2023.10.3 26 | requests==2.32.0 27 | six==1.16.0 28 | urllib3==2.2.2 29 | watchdog==3.0.0 30 | -------------------------------------------------------------------------------- /scripts/gen-webhook-secret.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Use this script to update the webhook secret post-deployment. Alternatively, 4 | # you can use patch-deploy-yaml.sh prior to deployment. 5 | # 6 | # Generates a certificate for use by the controller webhook 7 | # You can also manually re-create the webhook secret using your own PKI 8 | 9 | TEMP_KEY=`mktemp` 10 | TEMP_CERT=`mktemp` 11 | 12 | WEBHOOK_SVC_NAME=webhook-service 13 | WEBHOOK_NAME=aws-appnet-gwc-mutating-webhook 14 | WEBHOOK_NAMESPACE=aws-application-networking-system 15 | WEBHOOK_SECRET_NAME=webhook-cert 16 | 17 | echo "Generating certificate for webhook" 18 | HOST=${WEBHOOK_SVC_NAME}.${WEBHOOK_NAMESPACE}.svc 19 | openssl req -x509 -nodes -days 36500 -newkey rsa:2048 -keyout ${TEMP_KEY} -out ${TEMP_CERT} -subj "/CN=${HOST}/O=${HOST}" \ 20 | -addext "subjectAltName = DNS:${HOST}, DNS:${HOST}.cluster.local" 21 | 22 | CERT_B64=`cat $TEMP_CERT | base64` 23 | 24 | echo "Recreating webhook secret" 25 | kubectl delete secret $WEBHOOK_SECRET_NAME --namespace $WEBHOOK_NAMESPACE 26 | kubectl create secret tls $WEBHOOK_SECRET_NAME --namespace $WEBHOOK_NAMESPACE --cert=${TEMP_CERT} --key=${TEMP_KEY} 27 | 28 | echo "Patching webhook with new cert" 29 | kubectl patch mutatingwebhookconfigurations.admissionregistration.k8s.io $WEBHOOK_NAME \ 30 | --namespace $WEBHOOK_NAMESPACE --type='json' \ 31 | -p="[{'op': 'replace', 'path': '/webhooks/0/clientConfig/caBundle', 'value': '${CERT_B64}'}]" 32 | 33 | rm $TEMP_KEY $TEMP_CERT 34 | echo "Done" -------------------------------------------------------------------------------- /scripts/lib/common.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # common.sh contains commonly used functions, meant to be imported by other 4 | # bash scripts. 5 | 6 | LIB_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 7 | 8 | source "$LIB_DIR/logging.sh" 9 | 10 | # check_is_installed checks to see if the supplied executable is installed and 11 | # exits if not. An optional second argument is an extra message to display when 12 | # the supplied executable is not installed. 13 | # 14 | # Usage: 15 | # 16 | # check_is_installed PROGRAM [ MSG ] 17 | # 18 | # Example: 19 | # 20 | # check_is_installed kind "You can install kind with the helper scripts/install-kind.sh" 21 | check_is_installed() { 22 | local __name="$1" 23 | local __extra_msg="$2" 24 | if ! is_installed "$__name"; then 25 | error_msg "Missing required binary in PATH: '$__name'" 26 | error_msg "Please install $__name before running this script." 27 | if [[ -n $__extra_msg ]]; then 28 | error_msg "" 29 | error_msg "$__extra_msg" 30 | error_msg "" 31 | fi 32 | exit 1 33 | fi 34 | } 35 | 36 | is_installed() { 37 | local __name="$1" 38 | if $(which $__name >/dev/null 2>&1); then 39 | return 0 40 | else 41 | return 1 42 | fi 43 | } 44 | 45 | function sed_inplace() { 46 | local pattern=$1 47 | local replacement=$2 48 | local file=$3 49 | 50 | if [[ "$OSTYPE" == "darwin"* ]]; then 51 | # macOS uses BSD sed 52 | sed -i '' "s|$pattern|$replacement|g" "$file" 53 | else 54 | # Assuming Linux uses GNU sed 55 | sed -i "s|$pattern|$replacement|g" "$file" 56 | fi 57 | } -------------------------------------------------------------------------------- /scripts/lib/logging.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # logging.sh contains functions for writing log messages to the console for 4 | # three different severities: debug, info and error. 5 | 6 | # Prints out a message with an optional second argument indicating the 7 | # "indentation level" for the message. If the indentation level argument is 8 | # missing, we look for the existence of an environs variable called 9 | # "indent_level" and use that. 10 | _indented_msg() { 11 | local __msg=${1:-} 12 | local __indent_level=${2:-} 13 | local indent="" 14 | if [ -n "$__indent_level" ]; then 15 | indent="$( for each in $( seq 0 $__indent_level ); do printf " "; done )" 16 | fi 17 | 18 | local timestamp=$(date -Is) 19 | 20 | echo "$timestamp $__indent$__msg" 21 | } 22 | 23 | # debug_msg prints out a supplied message if the ACK_TEST_DEBUGGING_MODE environ 24 | # variable is set. 25 | debug_msg() { 26 | local __debug="${ACK_TEST_DEBUGGING_MODE:-""}" 27 | if [ ! -n "$__debug" ]; then 28 | return 0 29 | fi 30 | 31 | local __msg=${1:-} 32 | local __indent=${2:-} 33 | local __debug_prefix="${DEBUG_PREFIX:-"[DEBUG] "}" 34 | _indented_msg "$__debug_prefix$__msg" $__indent 35 | } 36 | 37 | # info_msg prints out a supplied message if the DEBUG environs variable is 38 | # set. 39 | info_msg() { 40 | local __msg=${1:-} 41 | local __indent=${2:-} 42 | local __info_prefix="${INFO_PREFIX:-"[INFO] "}" 43 | _indented_msg "$__info_prefix$__msg" $__indent 44 | } 45 | 46 | # debug_msg prints out a supplied message if the DEBUG environs variable is 47 | # set. 48 | error_msg() { 49 | local __msg=${1:-} 50 | local __indent=${2:-} 51 | local __error_prefix="${ERROR_PREFIX:-"[ERROR] "}" 52 | >&2 _indented_msg "$__error_prefix$__msg" $__indent 53 | } -------------------------------------------------------------------------------- /scripts/lib/login.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # login.sh contains functions for logging into container repositories. 4 | # Note: These functions are not placed inside tools specific file like 5 | # helm.sh, because those files ensure binaries(ex: kind) that are not 6 | # really needed for simple actions like repository login. 7 | # A future refactor can make those files more modular and then login 8 | # functions can be refactored in tool specific file like docker.sh 9 | # and helm.sh 10 | 11 | perform_docker_and_helm_login() { 12 | local full_url="$1" 13 | local registry_url=$(echo "$full_url" | cut -d'/' -f1) 14 | 15 | # Check if the registry URL is for the public ECR 16 | if [ "$registry_url" = "public.ecr.aws" ]; then 17 | local __pw=$(aws ecr-public get-login-password --region us-east-1) 18 | else 19 | local __pw=$(aws ecr get-login-password) 20 | fi 21 | echo "$__pw" | docker login --username AWS --password-stdin $registry_url 22 | export HELM_EXPERIMENTAL_OCI=1 23 | echo "$__pw" | helm registry login -u AWS --password-stdin $registry_url 24 | } 25 | 26 | ensure_binaries() { 27 | check_is_installed "aws" 28 | check_is_installed "docker" 29 | check_is_installed "helm" 30 | } 31 | 32 | ensure_binaries 33 | -------------------------------------------------------------------------------- /scripts/load_env_variables.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # When Running the k8s Controller by GoLand IDE locally, it is required to set the environment variables, this script will generate a file called `envFile` 4 | # that contains all needed environment variables, this `envFile` file can be use by GoLand "EnvFile" plugin 5 | # When config the "green arrow"(how we `run` or `debug` this project) in GoLand, we could set a "before launch" task to run this script `load_env_variables.sh`, and set the "EnvFile" plugin to load the `envFile` file 6 | # "EnvFile" is a plugin, you can install it from GoLand plugin marketplace 7 | 8 | echo "Setting environment variables" 9 | 10 | > envFile 11 | 12 | # Set KUBEBUILDER_ASSETS if not set 13 | if [ -z "$KUBEBUILDER_ASSETS" ]; then 14 | KUBEBUILDER_ASSETS=${HOME}/.kubebuilder/bin 15 | fi 16 | echo "KUBEBUILDER_ASSETS=$KUBEBUILDER_ASSETS" >> envFile 17 | 18 | # Set CLUSTER_NAME if not set 19 | if [ -z "$CLUSTER_NAME" ]; then 20 | CLUSTER_NAME=$(kubectl config view --minify -o jsonpath='{.clusters[].name}' | rev | cut -d"/" -f1 | rev | cut -d"." -f1) 21 | fi 22 | echo "CLUSTER_NAME=$CLUSTER_NAME" >> envFile 23 | 24 | # Set CLUSTER_VPC_ID if not set 25 | if [ -z "$CLUSTER_VPC_ID" ]; then 26 | CLUSTER_VPC_ID=$(aws eks describe-cluster --name ${CLUSTER_NAME} | jq -r ".cluster.resourcesVpcConfig.vpcId") 27 | fi 28 | echo "CLUSTER_VPC_ID=$CLUSTER_VPC_ID" >> envFile 29 | 30 | # Set AWS_ACCOUNT_ID if not set 31 | if [ -z "$AWS_ACCOUNT_ID" ]; then 32 | AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) 33 | fi 34 | echo "AWS_ACCOUNT_ID=$AWS_ACCOUNT_ID" >> envFile 35 | 36 | if [ -z "$REGION" ]; then 37 | REGION=us-west-2 38 | fi 39 | echo "REGION=$REGION" >> envFile 40 | 41 | echo "LOG_LEVEL=debug" >> envFile 42 | -------------------------------------------------------------------------------- /test/pkg/test/context.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "go.uber.org/zap" 8 | "go.uber.org/zap/zaptest" 9 | ) 10 | 11 | type loggerKey struct{} 12 | 13 | func NewContext(t *testing.T) context.Context { 14 | ctx := context.Background() 15 | ctx = context.WithValue(ctx, loggerKey{}, zaptest.NewLogger(t, zaptest.WrapOptions( 16 | zap.AddCaller(), 17 | zap.Development(), 18 | )).Sugar()) 19 | return ctx 20 | } 21 | 22 | func Logger(ctx context.Context) *zap.SugaredLogger { 23 | return ctx.Value(loggerKey{}).(*zap.SugaredLogger) 24 | } 25 | -------------------------------------------------------------------------------- /test/pkg/test/gateway.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "github.com/samber/lo" 5 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 6 | gwv1 "sigs.k8s.io/gateway-api/apis/v1" 7 | ) 8 | 9 | func (env *Framework) NewGateway(name string, namespace string) *gwv1.Gateway { 10 | gateway := New( 11 | &gwv1.Gateway{ 12 | ObjectMeta: metav1.ObjectMeta{ 13 | Name: name, 14 | Namespace: namespace, 15 | Annotations: map[string]string{ 16 | "application-networking.k8s.aws/lattice-vpc-association": "true", 17 | }, 18 | }, 19 | Spec: gwv1.GatewaySpec{ 20 | GatewayClassName: "amazon-vpc-lattice", 21 | Listeners: []gwv1.Listener{ 22 | { 23 | Name: "http", 24 | Protocol: gwv1.HTTPProtocolType, 25 | Port: 80, 26 | }, 27 | { 28 | Name: "https", 29 | Protocol: gwv1.HTTPSProtocolType, 30 | Port: 443, 31 | TLS: &gwv1.GatewayTLSConfig{ 32 | Mode: lo.ToPtr(gwv1.TLSModeTerminate), 33 | CertificateRefs: []gwv1.SecretObjectReference{ 34 | { 35 | Name: "dummy", 36 | }, 37 | }, 38 | }, 39 | }, 40 | { 41 | Name: "tls", 42 | Protocol: gwv1.TLSProtocolType, 43 | Port: 444, 44 | TLS: &gwv1.GatewayTLSConfig{ 45 | Mode: lo.ToPtr(gwv1.TLSModePassthrough), 46 | }, 47 | }, 48 | }, 49 | }, 50 | Status: gwv1.GatewayStatus{}, 51 | }, 52 | ) 53 | return gateway 54 | } 55 | -------------------------------------------------------------------------------- /test/pkg/test/grpc_helloworld.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "github.com/samber/lo" 5 | appsv1 "k8s.io/api/apps/v1" 6 | v1 "k8s.io/api/core/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | "k8s.io/apimachinery/pkg/util/intstr" 9 | ) 10 | 11 | // https://github.com/grpc/grpc-go/tree/master/examples/helloworld 12 | func (env *Framework) NewGrpcHelloWorld(options GrpcAppOptions) (*appsv1.Deployment, *v1.Service) { 13 | 14 | deployment := New(&appsv1.Deployment{ 15 | ObjectMeta: metav1.ObjectMeta{ 16 | Namespace: options.Namespace, 17 | }, 18 | Spec: appsv1.DeploymentSpec{ 19 | Replicas: lo.ToPtr(int32(1)), 20 | Selector: &metav1.LabelSelector{ 21 | MatchLabels: map[string]string{ 22 | "app": options.AppName, 23 | }, 24 | }, 25 | Template: v1.PodTemplateSpec{ 26 | ObjectMeta: metav1.ObjectMeta{ 27 | Namespace: options.Namespace, 28 | Labels: map[string]string{ 29 | "app": options.AppName, 30 | DiscoveryLabel: "true", 31 | }, 32 | }, 33 | Spec: v1.PodSpec{ 34 | Containers: []v1.Container{{ 35 | Name: options.AppName, 36 | Image: "aguilbau/hello-world-grpc:latest", 37 | }}, 38 | }, 39 | }, 40 | }, 41 | }) 42 | 43 | service := New(&v1.Service{ 44 | TypeMeta: metav1.TypeMeta{ 45 | Kind: "Service", 46 | }, 47 | ObjectMeta: metav1.ObjectMeta{ 48 | Namespace: options.Namespace, 49 | }, 50 | Spec: v1.ServiceSpec{ 51 | Selector: map[string]string{ 52 | "app": options.AppName, 53 | }, 54 | Ports: []v1.ServicePort{ 55 | { 56 | Name: "hello-world-grpc", 57 | Protocol: v1.ProtocolTCP, 58 | Port: int32(10051), 59 | TargetPort: intstr.FromInt(50051), 60 | }, 61 | }, 62 | }, 63 | }) 64 | return deployment, service 65 | 66 | } 67 | -------------------------------------------------------------------------------- /test/pkg/test/grpcbin.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "github.com/samber/lo" 5 | appsv1 "k8s.io/api/apps/v1" 6 | v1 "k8s.io/api/core/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | "k8s.io/apimachinery/pkg/util/intstr" 9 | ) 10 | 11 | type GrpcAppOptions struct { 12 | AppName string 13 | Namespace string 14 | } 15 | 16 | // https://github.com/moul/grpcbin 17 | func (env *Framework) NewGrpcBin(options GrpcAppOptions) (*appsv1.Deployment, *v1.Service) { 18 | 19 | deployment := New(&appsv1.Deployment{ 20 | ObjectMeta: metav1.ObjectMeta{ 21 | Namespace: options.Namespace, 22 | }, 23 | Spec: appsv1.DeploymentSpec{ 24 | Replicas: lo.ToPtr(int32(1)), 25 | Selector: &metav1.LabelSelector{ 26 | MatchLabels: map[string]string{ 27 | "app": options.AppName, 28 | }, 29 | }, 30 | Template: v1.PodTemplateSpec{ 31 | ObjectMeta: metav1.ObjectMeta{ 32 | Namespace: options.Namespace, 33 | Labels: map[string]string{ 34 | "app": options.AppName, 35 | DiscoveryLabel: "true", 36 | }, 37 | }, 38 | Spec: v1.PodSpec{ 39 | Containers: []v1.Container{{ 40 | Name: options.AppName, 41 | Image: "moul/grpcbin:latest", 42 | }}, 43 | }, 44 | }, 45 | }, 46 | }) 47 | 48 | service := New(&v1.Service{ 49 | TypeMeta: metav1.TypeMeta{ 50 | Kind: "Service", 51 | }, 52 | ObjectMeta: metav1.ObjectMeta{ 53 | Namespace: options.Namespace, 54 | }, 55 | Spec: v1.ServiceSpec{ 56 | Selector: map[string]string{ 57 | "app": options.AppName, 58 | }, 59 | Ports: []v1.ServicePort{ 60 | { 61 | Name: "grpcbin-over-http", 62 | Protocol: v1.ProtocolTCP, 63 | Port: int32(19000), 64 | TargetPort: intstr.FromInt(9000), 65 | }, 66 | { 67 | Name: "grpcbin-over-https", 68 | Protocol: v1.ProtocolTCP, 69 | Port: int32(19001), 70 | TargetPort: intstr.FromInt(9001), 71 | }, 72 | }, 73 | }, 74 | }) 75 | return deployment, service 76 | } 77 | -------------------------------------------------------------------------------- /test/pkg/test/grpcroute.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "github.com/samber/lo" 5 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 6 | gwv1 "sigs.k8s.io/gateway-api/apis/v1" 7 | ) 8 | 9 | func (env *Framework) NewGRPCRoute(namespace string, parentRefsGateway *gwv1.Gateway, rules []gwv1.GRPCRouteRule) *gwv1.GRPCRoute { 10 | grpcRoute := New(&gwv1.GRPCRoute{ 11 | TypeMeta: metav1.TypeMeta{}, 12 | ObjectMeta: metav1.ObjectMeta{ 13 | Namespace: namespace, 14 | }, 15 | Spec: gwv1.GRPCRouteSpec{ 16 | CommonRouteSpec: gwv1.CommonRouteSpec{ 17 | ParentRefs: []gwv1.ParentReference{{ 18 | Name: gwv1.ObjectName(parentRefsGateway.Name), 19 | Namespace: (*gwv1.Namespace)(&parentRefsGateway.Namespace), 20 | SectionName: lo.ToPtr(gwv1.SectionName("https")), 21 | }}, 22 | }, 23 | Rules: rules, 24 | }, 25 | }) 26 | 27 | return grpcRoute 28 | } 29 | -------------------------------------------------------------------------------- /test/pkg/test/grpcurl_runner.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | v1 "k8s.io/api/core/v1" 5 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 6 | ) 7 | 8 | // https://github.com/fullstorydev/grpcurl 9 | func NewGrpcurlRunnerPod(name, namespace string) *v1.Pod { 10 | grpcurlPod := &v1.Pod{ 11 | ObjectMeta: metav1.ObjectMeta{ 12 | Namespace: namespace, 13 | Name: name, 14 | Labels: map[string]string{ 15 | "app": "grpcurl-runner", 16 | }, 17 | }, 18 | Spec: v1.PodSpec{ 19 | Containers: []v1.Container{ 20 | { 21 | Name: "grpcurl-runner-container", 22 | // https://gallery.ecr.aws/a0j4q9e4/grpcurl-runner 23 | Image: "public.ecr.aws/a0j4q9e4/grpcurl-runner:latest", 24 | Command: []string{ 25 | "/bin/sh", 26 | "-c", 27 | "while true; do sleep 5; done;", 28 | }, 29 | }, 30 | }, 31 | }, 32 | } 33 | return grpcurlPod 34 | } 35 | -------------------------------------------------------------------------------- /test/pkg/test/header_match_httproute.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "github.com/samber/lo" 5 | v1 "k8s.io/api/core/v1" 6 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 7 | gwv1 "sigs.k8s.io/gateway-api/apis/v1" 8 | ) 9 | 10 | func (env *Framework) NewHeaderMatchHttpRoute(parentRefsGateway *gwv1.Gateway, services []*v1.Service) *gwv1.HTTPRoute { 11 | var rules []gwv1.HTTPRouteRule 12 | for _, service := range services { 13 | rule := gwv1.HTTPRouteRule{ 14 | BackendRefs: []gwv1.HTTPBackendRef{{ 15 | BackendRef: gwv1.BackendRef{ 16 | BackendObjectReference: gwv1.BackendObjectReference{ 17 | Name: gwv1.ObjectName(service.Name), 18 | Kind: lo.ToPtr(gwv1.Kind("Service")), 19 | Port: (*gwv1.PortNumber)(&service.Spec.Ports[0].Port), 20 | }, 21 | }, 22 | }}, 23 | Matches: []gwv1.HTTPRouteMatch{ 24 | { 25 | Headers: []gwv1.HTTPHeaderMatch{ 26 | { 27 | Type: lo.ToPtr(gwv1.HeaderMatchExact), 28 | Name: "my-header-name1", 29 | Value: "my-header-value1", 30 | }, 31 | { 32 | Type: lo.ToPtr(gwv1.HeaderMatchExact), 33 | Name: "my-header-name2", 34 | Value: "my-header-value2", 35 | }, 36 | }, 37 | }, 38 | }, 39 | } 40 | rules = append(rules, rule) 41 | } 42 | httpRoute := New(&gwv1.HTTPRoute{ 43 | ObjectMeta: metav1.ObjectMeta{ 44 | Namespace: env.namespace, 45 | }, 46 | Spec: gwv1.HTTPRouteSpec{ 47 | CommonRouteSpec: gwv1.CommonRouteSpec{ 48 | ParentRefs: []gwv1.ParentReference{{ 49 | Name: gwv1.ObjectName(parentRefsGateway.Name), 50 | SectionName: lo.ToPtr(gwv1.SectionName("http")), 51 | }}, 52 | }, 53 | Rules: rules, 54 | }, 55 | }) 56 | 57 | return httpRoute 58 | } 59 | -------------------------------------------------------------------------------- /test/pkg/test/httpapp.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "github.com/samber/lo" 5 | appsv1 "k8s.io/api/apps/v1" 6 | v1 "k8s.io/api/core/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | "k8s.io/apimachinery/pkg/util/intstr" 9 | ) 10 | 11 | type HTTPAppOptions struct { 12 | Name string 13 | Namespace string // the object will be created in this namespace 14 | Port int 15 | TargetPort int 16 | MergeFromDeployment []*appsv1.Deployment 17 | MergeFromService []*v1.Service 18 | } 19 | 20 | func (env *Framework) NewHttpApp(options HTTPAppOptions) (*appsv1.Deployment, *v1.Service) { 21 | if options.Port == 0 { 22 | options.Port = 80 23 | } 24 | if options.TargetPort == 0 { 25 | options.TargetPort = 8090 26 | } 27 | deployment := New(&appsv1.Deployment{ 28 | ObjectMeta: metav1.ObjectMeta{ 29 | Namespace: options.Namespace, 30 | }, 31 | Spec: appsv1.DeploymentSpec{ 32 | Replicas: lo.ToPtr(int32(1)), 33 | Selector: &metav1.LabelSelector{ 34 | MatchLabels: map[string]string{ 35 | "app": options.Name, 36 | }, 37 | }, 38 | Template: v1.PodTemplateSpec{ 39 | ObjectMeta: metav1.ObjectMeta{ 40 | Namespace: options.Namespace, 41 | Labels: map[string]string{ 42 | "app": options.Name, 43 | DiscoveryLabel: "true", 44 | }, 45 | }, 46 | Spec: v1.PodSpec{ 47 | Containers: []v1.Container{{ 48 | Name: options.Name, 49 | Image: "public.ecr.aws/x2j8p8w7/http-server:latest", 50 | Env: []v1.EnvVar{{ 51 | Name: "PodName", 52 | Value: options.Name + " handler pod", 53 | }}, 54 | }}, 55 | }, 56 | }, 57 | }, 58 | }, options.MergeFromDeployment...) 59 | 60 | service := New(&v1.Service{ 61 | TypeMeta: metav1.TypeMeta{ 62 | Kind: "Service", 63 | }, 64 | ObjectMeta: metav1.ObjectMeta{ 65 | Namespace: options.Namespace, 66 | Name: options.Name, 67 | }, 68 | Spec: v1.ServiceSpec{ 69 | Selector: map[string]string{ 70 | "app": options.Name, 71 | }, 72 | Ports: []v1.ServicePort{{ 73 | Protocol: v1.ProtocolTCP, 74 | Port: int32(options.Port), 75 | TargetPort: intstr.FromInt(options.TargetPort), 76 | }}, 77 | }, 78 | }, options.MergeFromService...) 79 | return deployment, service 80 | 81 | } 82 | -------------------------------------------------------------------------------- /test/pkg/test/httproute.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "github.com/samber/lo" 5 | corev1 "k8s.io/api/core/v1" 6 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 7 | gwv1 "sigs.k8s.io/gateway-api/apis/v1" 8 | ) 9 | 10 | func (env *Framework) NewHttpRoute(parentRefsGateway *gwv1.Gateway, service *corev1.Service, kind string) *gwv1.HTTPRoute { 11 | var rules []gwv1.HTTPRouteRule 12 | rule := gwv1.HTTPRouteRule{ 13 | BackendRefs: []gwv1.HTTPBackendRef{{ 14 | BackendRef: gwv1.BackendRef{ 15 | BackendObjectReference: gwv1.BackendObjectReference{ 16 | Name: gwv1.ObjectName(service.Name), 17 | Namespace: (*gwv1.Namespace)(&service.Namespace), 18 | Kind: lo.ToPtr(gwv1.Kind(kind)), 19 | Port: (*gwv1.PortNumber)(&service.Spec.Ports[0].Port), 20 | }, 21 | }, 22 | }}, 23 | } 24 | rules = append(rules, rule) 25 | parentNS := gwv1.Namespace(parentRefsGateway.Namespace) 26 | httpRoute := New(&gwv1.HTTPRoute{ 27 | ObjectMeta: metav1.ObjectMeta{ 28 | Namespace: service.Namespace, 29 | Name: service.Name, 30 | }, 31 | Spec: gwv1.HTTPRouteSpec{ 32 | CommonRouteSpec: gwv1.CommonRouteSpec{ 33 | ParentRefs: []gwv1.ParentReference{{ 34 | Name: gwv1.ObjectName(parentRefsGateway.Name), 35 | Namespace: &parentNS, 36 | }}, 37 | }, 38 | Rules: rules, 39 | }, 40 | }) 41 | return httpRoute 42 | } 43 | -------------------------------------------------------------------------------- /test/pkg/test/httpsapp.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "github.com/samber/lo" 5 | appsv1 "k8s.io/api/apps/v1" 6 | v1 "k8s.io/api/core/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | "k8s.io/apimachinery/pkg/util/intstr" 9 | ) 10 | 11 | type HTTPsAppOptions struct { 12 | Name string 13 | Namespace string // the object will be created in this namespace 14 | Port int 15 | TargetPort int 16 | MergeFromDeployment []*appsv1.Deployment 17 | MergeFromService []*v1.Service 18 | } 19 | 20 | func (env *Framework) NewHttpsApp(options HTTPsAppOptions) (*appsv1.Deployment, *v1.Service) { 21 | if options.Port == 0 { 22 | options.Port = 443 23 | } 24 | if options.TargetPort == 0 { 25 | options.TargetPort = 443 26 | } 27 | deployment := New(&appsv1.Deployment{ 28 | ObjectMeta: metav1.ObjectMeta{ 29 | Namespace: options.Namespace, 30 | }, 31 | Spec: appsv1.DeploymentSpec{ 32 | Replicas: lo.ToPtr(int32(1)), 33 | Selector: &metav1.LabelSelector{ 34 | MatchLabels: map[string]string{ 35 | "app": options.Name, 36 | }, 37 | }, 38 | Template: v1.PodTemplateSpec{ 39 | ObjectMeta: metav1.ObjectMeta{ 40 | Namespace: options.Namespace, 41 | Labels: map[string]string{ 42 | "app": options.Name, 43 | DiscoveryLabel: "true", 44 | }, 45 | }, 46 | Spec: v1.PodSpec{ 47 | Containers: []v1.Container{{ 48 | Name: options.Name, 49 | Image: "public.ecr.aws/x2j8p8w7/https-server:latest", 50 | Env: []v1.EnvVar{{ 51 | Name: "PodName", 52 | Value: options.Name + " handler pod", 53 | }}, 54 | }}, 55 | }, 56 | }, 57 | }, 58 | }, options.MergeFromDeployment...) 59 | 60 | service := New(&v1.Service{ 61 | TypeMeta: metav1.TypeMeta{ 62 | Kind: "Service", 63 | }, 64 | ObjectMeta: metav1.ObjectMeta{ 65 | Namespace: options.Namespace, 66 | Name: options.Name, 67 | }, 68 | Spec: v1.ServiceSpec{ 69 | Selector: map[string]string{ 70 | "app": options.Name, 71 | }, 72 | Ports: []v1.ServicePort{{ 73 | Protocol: v1.ProtocolTCP, 74 | Port: int32(options.Port), 75 | TargetPort: intstr.FromInt(options.TargetPort), 76 | }}, 77 | }, 78 | }, options.MergeFromService...) 79 | return deployment, service 80 | 81 | } 82 | -------------------------------------------------------------------------------- /test/pkg/test/metadata.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "strings" 7 | "sync" 8 | "time" 9 | 10 | randomdata "github.com/Pallinder/go-randomdata" 11 | "github.com/imdario/mergo" 12 | "sigs.k8s.io/controller-runtime/pkg/client" 13 | ) 14 | 15 | var ( 16 | sequentialNumber = 0 17 | randomizer = rand.New(rand.NewSource(time.Now().UnixNano())) //nolint 18 | sequentialNumberLock = new(sync.Mutex) 19 | DiscoveryLabel = "testing.kubernetes.io" 20 | ) 21 | 22 | func New[T client.Object](t T, mergeFrom ...T) T { 23 | if t.GetName() == "" { 24 | t.SetName(RandomName()) 25 | } 26 | if t.GetNamespace() == "" { 27 | t.SetNamespace("e2e-test") 28 | } 29 | t.SetLabels(map[string]string{DiscoveryLabel: "true"}) 30 | return MustMerge(t, mergeFrom...) 31 | } 32 | 33 | func MustMerge[T interface{}](dest T, srcs ...T) T { 34 | for _, src := range srcs { 35 | if err := mergo.Merge(&dest, src, mergo.WithOverride, mergo.WithAppendSlice); err != nil { 36 | panic(fmt.Sprintf("Failed to merge object: %s", err)) 37 | } 38 | } 39 | return dest 40 | } 41 | 42 | func RandomName() string { 43 | sequentialNumberLock.Lock() 44 | defer sequentialNumberLock.Unlock() 45 | sequentialNumber++ 46 | return strings.ToLower(fmt.Sprintf("%s-%d-%s", randomdata.SillyName()[:5], sequentialNumber, randomdata.Alphanumeric(10))) 47 | } 48 | -------------------------------------------------------------------------------- /test/pkg/test/method_match_httproute.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "github.com/samber/lo" 5 | v1 "k8s.io/api/core/v1" 6 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 7 | gwv1 "sigs.k8s.io/gateway-api/apis/v1" 8 | ) 9 | 10 | // creates a route sending GET to getService and POST to postService 11 | func (env *Framework) NewMethodMatchHttpRoute(parentRefsGateway *gwv1.Gateway, getService *v1.Service, postService *v1.Service, 12 | httpRouteName string, namespace string) *gwv1.HTTPRoute { 13 | getRule := gwv1.HTTPRouteRule{ 14 | BackendRefs: []gwv1.HTTPBackendRef{{ 15 | BackendRef: gwv1.BackendRef{ 16 | BackendObjectReference: gwv1.BackendObjectReference{ 17 | Name: gwv1.ObjectName(getService.Name), 18 | Kind: lo.ToPtr(gwv1.Kind("Service")), 19 | Port: (*gwv1.PortNumber)(&postService.Spec.Ports[0].Port), 20 | }, 21 | }, 22 | }}, 23 | Matches: []gwv1.HTTPRouteMatch{ 24 | { 25 | Method: lo.ToPtr(gwv1.HTTPMethodGet), 26 | }, 27 | }, 28 | } 29 | 30 | postRule := gwv1.HTTPRouteRule{ 31 | BackendRefs: []gwv1.HTTPBackendRef{{ 32 | BackendRef: gwv1.BackendRef{ 33 | BackendObjectReference: gwv1.BackendObjectReference{ 34 | Name: gwv1.ObjectName(postService.Name), 35 | Kind: lo.ToPtr(gwv1.Kind("Service")), 36 | Port: (*gwv1.PortNumber)(&postService.Spec.Ports[0].Port), 37 | }, 38 | }, 39 | }}, 40 | Matches: []gwv1.HTTPRouteMatch{ 41 | { 42 | Method: lo.ToPtr(gwv1.HTTPMethodPost), 43 | }, 44 | }, 45 | } 46 | 47 | httpRoute := New(&gwv1.HTTPRoute{ 48 | ObjectMeta: metav1.ObjectMeta{ 49 | Name: httpRouteName, 50 | Namespace: namespace, 51 | }, 52 | Spec: gwv1.HTTPRouteSpec{ 53 | CommonRouteSpec: gwv1.CommonRouteSpec{ 54 | ParentRefs: []gwv1.ParentReference{{ 55 | Name: gwv1.ObjectName(parentRefsGateway.Name), 56 | SectionName: lo.ToPtr(gwv1.SectionName("http")), 57 | }}, 58 | }, 59 | Rules: []gwv1.HTTPRouteRule{getRule, postRule}, 60 | }, 61 | }) 62 | 63 | return httpRoute 64 | } 65 | -------------------------------------------------------------------------------- /test/pkg/test/path_match_httproute.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/samber/lo" 7 | corev1 "k8s.io/api/core/v1" 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | "sigs.k8s.io/controller-runtime/pkg/client" 10 | gwv1 "sigs.k8s.io/gateway-api/apis/v1" 11 | ) 12 | 13 | func (env *Framework) NewPathMatchHttpRoute(parentRefsGateway *gwv1.Gateway, backendRefObjects []client.Object, 14 | gwListenerSectionName string, name string, namespace string) *gwv1.HTTPRoute { 15 | var rules []gwv1.HTTPRouteRule 16 | var httpns *string 17 | if namespace == "" { 18 | httpns = nil 19 | 20 | } else { 21 | httpns = &namespace 22 | } 23 | for i, object := range backendRefObjects { 24 | var port *gwv1.PortNumber 25 | if svc, ok := object.(*corev1.Service); ok { 26 | pv := gwv1.PortNumber(svc.Spec.Ports[0].Port) 27 | port = &pv 28 | } 29 | rule := gwv1.HTTPRouteRule{ 30 | BackendRefs: []gwv1.HTTPBackendRef{{ 31 | BackendRef: gwv1.BackendRef{ 32 | BackendObjectReference: gwv1.BackendObjectReference{ 33 | Name: gwv1.ObjectName(object.GetName()), 34 | Namespace: (*gwv1.Namespace)(httpns), 35 | Kind: lo.ToPtr(gwv1.Kind(object.GetObjectKind().GroupVersionKind().Kind)), 36 | Port: port, 37 | }, 38 | }, 39 | }}, 40 | Matches: []gwv1.HTTPRouteMatch{ 41 | { 42 | Path: &gwv1.HTTPPathMatch{ 43 | Type: lo.ToPtr(gwv1.PathMatchPathPrefix), 44 | Value: lo.ToPtr("/pathmatch" + strconv.Itoa(i)), 45 | }, 46 | }, 47 | }, 48 | } 49 | rules = append(rules, rule) 50 | } 51 | 52 | httpRoute := New(&gwv1.HTTPRoute{ 53 | ObjectMeta: metav1.ObjectMeta{ 54 | Name: name, 55 | Namespace: namespace, 56 | }, 57 | Spec: gwv1.HTTPRouteSpec{ 58 | CommonRouteSpec: gwv1.CommonRouteSpec{ 59 | ParentRefs: []gwv1.ParentReference{{ 60 | Name: gwv1.ObjectName(parentRefsGateway.Name), 61 | Namespace: (*gwv1.Namespace)(httpns), 62 | SectionName: lo.ToPtr(gwv1.SectionName(gwListenerSectionName)), 63 | }}, 64 | }, 65 | Rules: rules, 66 | }, 67 | }) 68 | return httpRoute 69 | } 70 | -------------------------------------------------------------------------------- /test/pkg/test/target_group_policy.go: -------------------------------------------------------------------------------- 1 | package test 2 | -------------------------------------------------------------------------------- /test/pkg/test/tlsroute.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "github.com/samber/lo" 5 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 6 | gwv1 "sigs.k8s.io/gateway-api/apis/v1" 7 | "sigs.k8s.io/gateway-api/apis/v1alpha2" 8 | ) 9 | 10 | func (env *Framework) NewTLSRoute(namespace string, parentRefsGateway *gwv1.Gateway, rules []v1alpha2.TLSRouteRule) *v1alpha2.TLSRoute { 11 | tlsRoute := New(&v1alpha2.TLSRoute{ 12 | TypeMeta: metav1.TypeMeta{}, 13 | ObjectMeta: metav1.ObjectMeta{ 14 | Namespace: namespace, 15 | }, 16 | Spec: v1alpha2.TLSRouteSpec{ 17 | CommonRouteSpec: v1alpha2.CommonRouteSpec{ 18 | ParentRefs: []gwv1.ParentReference{{ 19 | Name: gwv1.ObjectName(parentRefsGateway.Name), 20 | Namespace: (*gwv1.Namespace)(&parentRefsGateway.Namespace), 21 | SectionName: lo.ToPtr(gwv1.SectionName("tls")), 22 | }}, 23 | }, 24 | Hostnames: []gwv1.Hostname{"lattice-k8s-tls-passthrough-test.com"}, 25 | Rules: rules, 26 | }, 27 | }) 28 | 29 | return tlsRoute 30 | } 31 | -------------------------------------------------------------------------------- /test/pkg/test/weighted_routing_httproute.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "github.com/samber/lo" 5 | corev1 "k8s.io/api/core/v1" 6 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 7 | "sigs.k8s.io/controller-runtime/pkg/client" 8 | gwv1 "sigs.k8s.io/gateway-api/apis/v1" 9 | ) 10 | 11 | type ObjectAndWeight struct { 12 | Object client.Object 13 | Weight int32 14 | } 15 | 16 | func (env *Framework) NewWeightedRoutingHttpRoute(parentRefsGateway *gwv1.Gateway, backendRefObjectAndWeights []*ObjectAndWeight, 17 | gwListenerSectionNames []string) *gwv1.HTTPRoute { 18 | 19 | var backendRefs []gwv1.HTTPBackendRef 20 | for _, objectAndWeight := range backendRefObjectAndWeights { 21 | var port *gwv1.PortNumber 22 | if svc, ok := objectAndWeight.Object.(*corev1.Service); ok { 23 | pv := gwv1.PortNumber(svc.Spec.Ports[0].Port) 24 | port = &pv 25 | } 26 | backendRefs = append(backendRefs, gwv1.HTTPBackendRef{ 27 | BackendRef: gwv1.BackendRef{ 28 | BackendObjectReference: gwv1.BackendObjectReference{ 29 | Name: gwv1.ObjectName(objectAndWeight.Object.GetName()), 30 | Kind: lo.ToPtr(gwv1.Kind(objectAndWeight.Object.GetObjectKind().GroupVersionKind().Kind)), 31 | Port: port, 32 | }, 33 | Weight: lo.ToPtr(objectAndWeight.Weight), 34 | }, 35 | }) 36 | } 37 | var parentRefs []gwv1.ParentReference 38 | for _, gwListenerSectionName := range gwListenerSectionNames { 39 | parentRefs = append(parentRefs, gwv1.ParentReference{ 40 | Name: gwv1.ObjectName(parentRefsGateway.Name), 41 | SectionName: lo.ToPtr(gwv1.SectionName(gwListenerSectionName)), 42 | }) 43 | } 44 | httpRoute := New(&gwv1.HTTPRoute{ 45 | ObjectMeta: metav1.ObjectMeta{ 46 | Namespace: parentRefsGateway.Namespace, 47 | }, 48 | Spec: gwv1.HTTPRouteSpec{ 49 | CommonRouteSpec: gwv1.CommonRouteSpec{ 50 | ParentRefs: parentRefs, 51 | }, 52 | Rules: []gwv1.HTTPRouteRule{ 53 | { 54 | BackendRefs: backendRefs, 55 | }, 56 | }, 57 | }, 58 | }) 59 | return httpRoute 60 | } 61 | -------------------------------------------------------------------------------- /test/suites/integration/readiness_gate_test.go: -------------------------------------------------------------------------------- 1 | package integration 2 | 3 | import ( 4 | "github.com/aws/aws-application-networking-k8s/pkg/deploy/lattice" 5 | "github.com/aws/aws-application-networking-k8s/pkg/k8s" 6 | "github.com/aws/aws-application-networking-k8s/pkg/utils" 7 | "github.com/aws/aws-application-networking-k8s/test/pkg/test" 8 | . "github.com/onsi/ginkgo/v2" 9 | . "github.com/onsi/gomega" 10 | "github.com/samber/lo" 11 | appsv1 "k8s.io/api/apps/v1" 12 | v1 "k8s.io/api/core/v1" 13 | gwv1 "sigs.k8s.io/gateway-api/apis/v1" 14 | ) 15 | 16 | var _ = Describe("Pod Readiness Gate", Ordered, func() { 17 | var ( 18 | deployment *appsv1.Deployment 19 | service *v1.Service 20 | route *gwv1.HTTPRoute 21 | ) 22 | 23 | BeforeAll(func() { 24 | deployment, service = testFramework.NewNginxApp(test.ElasticSearchOptions{ 25 | Name: "pod-test", 26 | Namespace: k8snamespace, 27 | ReadinessGate: true, 28 | }) 29 | route = testFramework.NewHttpRoute(testGateway, service, service.Kind) 30 | testFramework.ExpectCreated(ctx, deployment, service, route) 31 | }) 32 | 33 | It("updates condition when injected", func() { 34 | Eventually(func(g Gomega) { 35 | pods := testFramework.GetPodsByDeploymentName(deployment.Name, deployment.Namespace) 36 | for _, pod := range pods { 37 | cond := utils.FindPodStatusCondition(pod.Status.Conditions, lattice.LatticeReadinessGateConditionType) 38 | g.Expect(cond).To(Not(BeNil())) 39 | g.Expect(cond.Status).To(Equal(v1.ConditionTrue)) 40 | } 41 | }).Should(Succeed()) 42 | }) 43 | 44 | It("updates condition when a new pod is added", func() { 45 | testFramework.Get(ctx, k8s.NamespacedName(deployment), deployment) 46 | deployment.Spec.Replicas = lo.ToPtr(int32(3)) 47 | testFramework.ExpectUpdated(ctx, deployment) 48 | 49 | Eventually(func(g Gomega) { 50 | pods := testFramework.GetPodsByDeploymentName(deployment.Name, deployment.Namespace) 51 | for _, pod := range pods { 52 | cond := utils.FindPodStatusCondition(pod.Status.Conditions, lattice.LatticeReadinessGateConditionType) 53 | g.Expect(cond).To(Not(BeNil())) 54 | g.Expect(cond.Status).To(Equal(v1.ConditionTrue)) 55 | } 56 | }).Should(Succeed()) 57 | }) 58 | 59 | AfterAll(func() { 60 | testFramework.ExpectDeletedThenNotFound(ctx, 61 | deployment, 62 | service, 63 | route, 64 | ) 65 | }) 66 | }) 67 | -------------------------------------------------------------------------------- /test/suites/webhook/suite_test.go: -------------------------------------------------------------------------------- 1 | package webhook 2 | 3 | import ( 4 | "context" 5 | "github.com/aws/aws-application-networking-k8s/pkg/utils/gwlog" 6 | "github.com/aws/aws-application-networking-k8s/test/pkg/test" 7 | "github.com/aws/aws-sdk-go/service/vpclattice" 8 | . "github.com/onsi/ginkgo/v2" 9 | . "github.com/onsi/gomega" 10 | "go.uber.org/zap" 11 | "os" 12 | gwv1 "sigs.k8s.io/gateway-api/apis/v1" 13 | "testing" 14 | ) 15 | 16 | const ( 17 | k8snamespace = "webhook-" + test.K8sNamespace 18 | ) 19 | 20 | var testFramework *test.Framework 21 | var ctx context.Context 22 | var testGateway *gwv1.Gateway 23 | var testServiceNetwork *vpclattice.ServiceNetworkSummary 24 | 25 | var _ = SynchronizedBeforeSuite(func() { 26 | vpcId := os.Getenv("CLUSTER_VPC_ID") 27 | if vpcId == "" { 28 | Fail("CLUSTER_VPC_ID environment variable must be set to run integration tests") 29 | } 30 | 31 | // provision gateway, wait for service network association 32 | testGateway = testFramework.NewGateway("test-gateway", k8snamespace) 33 | testFramework.ExpectCreated(ctx, testGateway) 34 | 35 | testServiceNetwork = testFramework.GetServiceNetwork(ctx, testGateway) 36 | 37 | testFramework.Log.Infof(ctx, "Expecting VPC %s and service network %s association", vpcId, *testServiceNetwork.Id) 38 | Eventually(func(g Gomega) { 39 | associated, _, _ := testFramework.IsVpcAssociatedWithServiceNetwork(ctx, vpcId, testServiceNetwork) 40 | g.Expect(associated).To(BeTrue()) 41 | }).Should(Succeed()) 42 | }, func() { 43 | testGateway = testFramework.NewGateway("test-gateway", k8snamespace) 44 | testServiceNetwork = testFramework.GetServiceNetwork(ctx, testGateway) 45 | }) 46 | 47 | func TestIntegration(t *testing.T) { 48 | ctx = test.NewContext(t) 49 | logger := gwlog.NewLogger(zap.DebugLevel) 50 | testFramework = test.NewFramework(ctx, logger, k8snamespace) 51 | RegisterFailHandler(Fail) 52 | RunSpecs(t, "WebhookIntegration") 53 | } 54 | 55 | var _ = SynchronizedAfterSuite(func() {}, func() { 56 | testFramework.ExpectDeletedThenNotFound(ctx, testGateway) 57 | }) 58 | --------------------------------------------------------------------------------