├── .devcontainer ├── dev │ ├── devcontainer.json │ └── post-create.sh └── website │ ├── devcontainer.json │ └── post-create.sh ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ ├── feature_request.md │ └── something-else.md ├── dependabot.yml ├── release.yml └── workflows │ ├── codeql.yml │ ├── linkcheck.yml │ ├── pr-check.yml │ ├── pr-cli.yml │ ├── pr-e2e-test.yml │ └── release.yml ├── .gitignore ├── .golangci.yaml ├── .licenserc.yaml ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Containerfile.dev ├── GOVERNANCE.md ├── LICENSE ├── Makefile ├── README.md ├── SECURITY.md ├── cmd ├── cl-controlplane │ ├── Dockerfile │ ├── app │ │ └── server.go │ └── cl-controlplane.go ├── cl-dataplane │ ├── Dockerfile │ ├── app │ │ ├── envoy.go │ │ ├── envoyconf.go │ │ └── server.go │ └── cl-dataplane.go ├── cl-go-dataplane │ ├── Dockerfile │ ├── app │ │ └── server.go │ └── cl-go-dataplane.go ├── cl-operator │ ├── Dockerfile │ └── main.go └── clusterlink │ ├── cl-adm.go │ ├── cmd │ ├── cmd.go │ ├── create │ │ ├── create.go │ │ ├── create_fabric.go │ │ └── create_peer.go │ ├── delete │ │ ├── delete.go │ │ └── delete_peer.go │ └── deploy │ │ ├── deploy.go │ │ └── deploy_peer.go │ └── config │ └── config.go ├── config ├── config.go ├── crds │ ├── clusterlink.net_accesspolicies.yaml │ ├── clusterlink.net_exports.yaml │ ├── clusterlink.net_imports.yaml │ ├── clusterlink.net_instances.yaml │ ├── clusterlink.net_peers.yaml │ └── clusterlink.net_privilegedaccesspolicies.yaml └── operator │ ├── manager │ └── manager.yaml │ └── rbac │ ├── leader_election_role.yaml │ ├── leader_election_role_binding.yaml │ ├── operator_editor_role.yaml │ ├── operator_viewer_role.yaml │ ├── role.yaml │ ├── role_binding.yaml │ └── service_account.yaml ├── demos ├── bookinfo │ ├── cloud │ │ ├── apply_lb.py │ │ ├── gw_failover.py │ │ └── test.py │ ├── kind │ │ ├── README.md │ │ ├── apply_lb.py │ │ ├── gw_failover.py │ │ └── test.py │ ├── manifests │ │ ├── clusterlink │ │ │ ├── allow-policy.yaml │ │ │ ├── deny-server1-policy.yaml │ │ │ ├── export-reviews.yaml │ │ │ ├── import-reviews-lb-random.yaml │ │ │ ├── import-reviews-lb-static.yaml │ │ │ ├── import-reviews.yaml │ │ │ ├── peer-client.yaml │ │ │ ├── peer-server1.yaml │ │ │ └── peer-server2.yaml │ │ ├── product │ │ │ ├── details.yaml │ │ │ ├── product.yaml │ │ │ └── product2.yaml │ │ └── review │ │ │ ├── rating.yaml │ │ │ ├── review-v2.yaml │ │ │ └── review-v3.yaml │ └── test.py ├── frp │ ├── kind │ │ └── test.py │ ├── test.py │ └── testdata │ │ └── manifests │ │ ├── client │ │ ├── frpc.yaml │ │ ├── peer1 │ │ │ ├── frpc-configmap.yaml │ │ │ └── peer.yaml │ │ ├── peer2 │ │ │ ├── frpc-configmap.yaml │ │ │ └── peer.yaml │ │ └── peer3 │ │ │ ├── frpc-configmap.yaml │ │ │ └── peer.yaml │ │ ├── frp-ns.yaml │ │ └── server │ │ ├── frps-configmap.yaml │ │ └── frps.yaml ├── iperf3 │ ├── cloud │ │ └── test.py │ ├── kind │ │ ├── iperf3_client_start.py │ │ └── test.py │ ├── test.py │ └── testdata │ │ └── manifests │ │ ├── clusterlink │ │ ├── allow-policy.yaml │ │ ├── export-iperf3.yaml │ │ ├── import-iperf3.yaml │ │ ├── peer-client.yaml │ │ └── peer-server.yaml │ │ ├── iperf3-client │ │ ├── iperf3-client.yaml │ │ ├── iperf3-client2.yaml │ │ └── kind-config.yaml │ │ └── iperf3-server │ │ ├── iperf3-svc.yaml │ │ ├── iperf3.yaml │ │ └── kind-config.yaml ├── nginx │ └── testdata │ │ ├── clusterlink │ │ ├── allow-policy.yaml │ │ ├── export-nginx.yaml │ │ ├── import-nginx-relay.yaml │ │ ├── import-nginx.yaml │ │ ├── peer-client.yaml │ │ ├── peer-relay.yaml │ │ └── peer-server.yaml │ │ ├── nginx-job.yaml │ │ ├── nginx-relay-job.yaml │ │ └── nginx-server.yaml ├── qotd │ ├── kind │ │ └── test.py │ └── manifests │ │ ├── qotd_author.yaml │ │ ├── qotd_db.yaml │ │ ├── qotd_engraving.yaml │ │ ├── qotd_image.yaml │ │ ├── qotd_pdf.yaml │ │ ├── qotd_quote.yaml │ │ ├── qotd_rating.yaml │ │ └── qotd_web.yaml ├── speedtest │ ├── kind │ │ ├── README.md │ │ ├── apply_policy.py │ │ ├── service_import.py │ │ └── test.py │ └── testdata │ │ ├── manifests │ │ ├── firefox.yaml │ │ ├── firefox2.yaml │ │ └── speedtest.yaml │ │ └── policy │ │ ├── denyFromGw.yaml │ │ └── denyToSpeedtest.yaml └── utils │ ├── __init__.py │ ├── cloud.py │ ├── clusterlink.py │ ├── common.py │ ├── k8s.py │ ├── kind.py │ └── manifests │ └── kind │ ├── calico │ └── calico-config.yaml │ ├── cl-svc.yaml │ └── flannel │ ├── create_cni_bridge.py │ └── flannel-config.yaml ├── design-proposals ├── DESIGN-PROPOSALS-template.md ├── crd-based-management.md ├── deployment.png ├── policy-attributes.md └── project-deployment.md ├── docs ├── ClusteLink.pdf ├── Policy.md ├── controlplane-dataplane.md ├── figures │ ├── BookInfo_demo.png │ └── clusterlink.png ├── installation.md ├── mbg-proto.png ├── openspeedtest.png └── policy-arch.png ├── examples └── policies │ ├── allowAll.json │ ├── allowToReviews.json │ └── deny_from_gw1.json ├── go.mod ├── go.sum ├── hack ├── boilerplate.go.txt ├── gen-doc-version.sh ├── install-devtools.sh └── install_clusterlink.sh ├── netlify.toml ├── pkg ├── apis │ └── clusterlink.net │ │ └── v1alpha1 │ │ ├── accesspolicy.go │ │ ├── accesspolicy_test.go │ │ ├── export.go │ │ ├── groupversion_info.go │ │ ├── import.go │ │ ├── instance_types.go │ │ ├── peer.go │ │ └── zz_generated.deepcopy.go ├── bootstrap │ ├── cert.go │ ├── crypt.go │ └── platform │ │ ├── config.go │ │ └── k8s.go ├── controlplane │ ├── api │ │ ├── authz.go │ │ ├── const.go │ │ ├── heartbeat.go │ │ └── xds.go │ ├── authz │ │ ├── connectivitypdp │ │ │ ├── accesspolicy.go │ │ │ ├── connectivity_pdp.go │ │ │ ├── connectivity_pdp_test.go │ │ │ └── test_data │ │ │ │ ├── all_layers.yaml │ │ │ │ ├── mixed_policies_and_other_resources.yaml │ │ │ │ ├── not_a_yaml │ │ │ │ ├── privileged_and_regular.yaml │ │ │ │ └── simple_privileged.yaml │ │ ├── controllers.go │ │ ├── loadbalancer.go │ │ ├── manager.go │ │ └── server.go │ ├── control │ │ ├── controllers.go │ │ ├── manager.go │ │ ├── peer.go │ │ └── port.go │ ├── peer │ │ ├── certs.go │ │ └── client.go │ └── xds │ │ ├── controllers.go │ │ ├── manager.go │ │ └── server.go ├── dataplane │ ├── api │ │ └── const.go │ ├── client │ │ ├── fetcher.go │ │ └── xds.go │ └── server │ │ ├── dataplane.go │ │ ├── forwarder.go │ │ ├── listener.go │ │ └── server.go ├── operator │ └── controller │ │ ├── instance_controller.go │ │ └── instance_controller_test.go ├── util │ ├── controller │ │ ├── controller.go │ │ └── manager.go │ ├── grpc │ │ └── server.go │ ├── http │ │ └── server.go │ ├── jsonapi │ │ └── client.go │ ├── log │ │ └── util.go │ ├── runnable │ │ └── manager.go │ ├── tcp │ │ └── listener.go │ └── tls │ │ └── util.go └── versioninfo │ ├── variables.go │ └── version.go ├── tests ├── cli │ └── basic_test.sh └── e2e │ └── k8s │ ├── k8s_test.go │ ├── services │ ├── errors.go │ ├── httpecho │ │ ├── client.go │ │ └── server.go │ └── iperf3 │ │ ├── client.go │ │ └── server.go │ ├── suite.go │ ├── test_basic.go │ ├── test_dynamic.go │ ├── test_export.go │ ├── test_import.go │ ├── test_loadbalancing.go │ ├── test_operator.go │ ├── test_peer.go │ ├── test_performance.go │ ├── test_policy.go │ ├── test_redundancy.go │ └── util │ ├── async.go │ ├── clusterlink.go │ ├── fabric.go │ ├── k8s_yaml.go │ ├── kind.go │ └── policies.go └── website ├── .gitignore ├── Makefile ├── assets ├── icons │ ├── logo.png │ └── logo.svg └── scss │ ├── _search.scss │ └── _variables_project.scss ├── config.toml ├── content └── en │ ├── _index.md │ ├── about │ └── _index.md │ ├── blog │ ├── _index.md │ ├── clusterlink-intro │ │ ├── field.jpg │ │ └── index.md │ └── hello-world │ │ ├── index.md │ │ └── sunflower.jpg │ ├── docs │ ├── _index.md │ ├── main │ │ ├── _index.md │ │ ├── concepts │ │ │ ├── _index.md │ │ │ ├── fabric.md │ │ │ ├── peers.md │ │ │ ├── policies.md │ │ │ └── services.md │ │ ├── doc-contribution │ │ │ └── _index.md │ │ ├── getting-started │ │ │ ├── _index.md │ │ │ ├── developers.md │ │ │ └── users.md │ │ ├── overview │ │ │ └── _index.md │ │ ├── tasks │ │ │ ├── _index.md │ │ │ ├── operator.md │ │ │ ├── private-networks │ │ │ │ ├── frp-system.png │ │ │ │ └── index.md │ │ │ └── relay │ │ │ │ ├── index.md │ │ │ │ └── nginx-relay.png │ │ └── tutorials │ │ │ ├── _index.md │ │ │ ├── bookinfo │ │ │ ├── bookinfo.png │ │ │ └── index.md │ │ │ ├── iperf │ │ │ └── index.md │ │ │ └── nginx │ │ │ └── index.md │ ├── v0.1 │ │ ├── _index.md │ │ ├── concepts │ │ │ ├── _index.md │ │ │ ├── fabric.md │ │ │ ├── peers.md │ │ │ ├── policies.md │ │ │ └── services.md │ │ ├── doc-contribution │ │ │ └── _index.md │ │ ├── getting-started │ │ │ ├── _index.md │ │ │ ├── developers.md │ │ │ └── users.md │ │ ├── overview │ │ │ └── _index.md │ │ └── tutorials │ │ │ ├── _index.md │ │ │ └── iperf.md │ ├── v0.2 │ │ ├── _index.md │ │ ├── concepts │ │ │ ├── _index.md │ │ │ ├── fabric.md │ │ │ ├── peers.md │ │ │ ├── policies.md │ │ │ └── services.md │ │ ├── doc-contribution │ │ │ └── _index.md │ │ ├── getting-started │ │ │ ├── _index.md │ │ │ ├── developers.md │ │ │ └── users.md │ │ ├── overview │ │ │ └── _index.md │ │ ├── tasks │ │ │ ├── _index.md │ │ │ └── operator.md │ │ └── tutorials │ │ │ ├── _index.md │ │ │ ├── bookinfo │ │ │ ├── bookinfo.png │ │ │ └── index.md │ │ │ └── iperf.md │ ├── v0.3 │ │ ├── _index.md │ │ ├── concepts │ │ │ ├── _index.md │ │ │ ├── fabric.md │ │ │ ├── peers.md │ │ │ ├── policies.md │ │ │ └── services.md │ │ ├── doc-contribution │ │ │ └── _index.md │ │ ├── getting-started │ │ │ ├── _index.md │ │ │ ├── developers.md │ │ │ └── users.md │ │ ├── overview │ │ │ └── _index.md │ │ ├── tasks │ │ │ ├── _index.md │ │ │ ├── operator.md │ │ │ └── relay │ │ │ │ ├── index.md │ │ │ │ └── nginx-relay.png │ │ └── tutorials │ │ │ ├── _index.md │ │ │ ├── bookinfo │ │ │ ├── bookinfo.png │ │ │ └── index.md │ │ │ ├── iperf │ │ │ └── index.md │ │ │ └── nginx │ │ │ └── index.md │ └── v0.4 │ │ ├── _index.md │ │ ├── concepts │ │ ├── _index.md │ │ ├── fabric.md │ │ ├── peers.md │ │ ├── policies.md │ │ └── services.md │ │ ├── doc-contribution │ │ └── _index.md │ │ ├── getting-started │ │ ├── _index.md │ │ ├── developers.md │ │ └── users.md │ │ ├── overview │ │ └── _index.md │ │ ├── tasks │ │ ├── _index.md │ │ ├── operator.md │ │ ├── private-networks │ │ │ ├── frp-system.png │ │ │ └── index.md │ │ └── relay │ │ │ ├── index.md │ │ │ └── nginx-relay.png │ │ └── tutorials │ │ ├── _index.md │ │ ├── bookinfo │ │ ├── bookinfo.png │ │ └── index.md │ │ ├── iperf │ │ └── index.md │ │ └── nginx │ │ └── index.md │ ├── featured-background.png │ └── search.md ├── go.mod ├── go.sum ├── i18n └── en.toml ├── layouts ├── index.redirects ├── partials │ └── favicons.html └── shortcodes │ ├── abbr.html │ ├── anchor.html │ ├── clusterlink │ ├── card.html │ ├── feature.html │ ├── intro.html │ └── main.html │ └── expand.html ├── netlify.toml ├── package.json └── static ├── backgrounds ├── section2.jpg └── section4.jpg ├── clusterlink_figure.png ├── clusterlink_logo.png ├── favicons ├── apple-touch-icon-180x180.png ├── favicon-32x32.png ├── favicon.ico ├── favicon.png └── favicon.svg ├── files ├── peer_crd_sample.yaml └── tutorials │ ├── allow-all-policy.md │ ├── cli-installation.md │ ├── deploy-clusterlink.md │ ├── envsubst.md │ ├── nginx │ └── nginx-output.md │ └── peer.md ├── icons ├── icon1.png ├── icon2.png ├── icon3.png ├── icon4.png ├── icon5.png └── icon6.png ├── logo-darkblue-yellow.png ├── logo-lightblue-yellow.png └── logo-white-yellow.png /.devcontainer/dev/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ClusterLink Dev Container", 3 | "image": "mcr.microsoft.com/devcontainers/base:ubuntu", 4 | "features": { 5 | "ghcr.io/devcontainers/features/git:1": {}, 6 | "ghcr.io/devcontainers/features/go:1": {}, 7 | "ghcr.io/mpriscella/features/kind:1": { 8 | "version": "0.22.0" 9 | }, 10 | "ghcr.io/devcontainers-contrib/features/kubectl-asdf:2": { 11 | "version": "1.23.5" 12 | }, 13 | "ghcr.io/devcontainers/features/docker-in-docker:2": {}, 14 | "ghcr.io/devcontainers/features/python:1": {} 15 | }, 16 | "remoteUser": "vscode", 17 | "postCreateCommand": "bash .devcontainer/dev/post-create.sh", 18 | "customizations": { 19 | "vscode":{ 20 | "settings": { 21 | "terminal.integrated.defaultProfile.linux": "bash" 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.devcontainer/dev/post-create.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) The ClusterLink Authors. 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | # Initialize Go tools 17 | go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.54.2 18 | go install golang.org/x/tools/cmd/goimports@latest 19 | go install github.com/mfridman/tparse@latest 20 | go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.14.0 21 | go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest 22 | go install mvdan.cc/gofumpt@v0.6.0 23 | go install github.com/raviqqe/muffet/v2@v2.9.3 24 | -------------------------------------------------------------------------------- /.devcontainer/website/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ClusterLink Website/Docs Dev Container", 3 | "image": "mcr.microsoft.com/devcontainers/base:ubuntu", 4 | "features": { 5 | "ghcr.io/devcontainers/features/git:1": {}, 6 | "ghcr.io/devcontainers/features/go:1": {}, 7 | "ghcr.io/devcontainers/features/hugo:1": { 8 | "version": "0.117.0", 9 | "extended": true 10 | }, 11 | "ghcr.io/devcontainers/features/node:1": {} 12 | }, 13 | "remoteUser": "vscode", 14 | "postCreateCommand": "bash .devcontainer/website/post-create.sh", 15 | "customizations": { 16 | "vscode":{ 17 | "settings": { 18 | "terminal.integrated.defaultProfile.linux": "bash" 19 | } 20 | } 21 | }, 22 | "portsAttributes": { 23 | "1313": { 24 | "label": "hugo server", 25 | "onAutoForward": "openBrowser" 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.devcontainer/website/post-create.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) The ClusterLink Authors. 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | # Initialize packages for website 17 | pushd website 18 | npm install 19 | 20 | go install "github.com/raviqqe/muffet/v2@v2.9.3" 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]:" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of the current behavior why this is a bug. 12 | 13 | **To Reproduce** 14 | Is the issue is reproducible? Always or only under certain conditions? 15 | Steps to reproduce the behavior: 16 | 17 | 1. ... 18 | 2. ... 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Additional context** 24 | Add any other context about the problem here (e.g., ClusterLink version, related issues, suggestions how to fix) 25 | 26 | If possible, please confirm: 27 | 28 | - [ ] This is something I can **debug and identify the location where the problem exists** 29 | - [ ] If the bug is confirmed, **I would be willing to submit a PR**. Bug fixes and documentation fixes are welcome. 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for ClusterLink 4 | title: "[FEATURE]:" 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. 12 | What are you trying to do and how would you want to do it differently? 13 | Is it something you currently you cannot do? Is this related to an issue/problem? 14 | 15 | **Describe the solution you'd like** 16 | A clear and concise description of what you want to happen. 17 | What is the motivation / use case for changing the behavior? 18 | 19 | **Describe alternatives you've considered** 20 | A clear and concise description of any alternative solutions or features you've considered. 21 | 22 | **Additional context** 23 | Add any other context or screenshots about the feature request here. 24 | 25 | If possible, please confirm: 26 | 27 | - [ ] **If the feature request is approved, I would be willing to submit a PR** 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/something-else.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Something else 3 | about: Issues other than bug reports and enhancements (e.g., questions, documentation, 4 | etc.) 5 | title: '' 6 | labels: '' 7 | assignees: '' 8 | 9 | --- 10 | 11 | 12 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "gomod" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | groups: 13 | k8s: 14 | patterns: 15 | - "k8s.io/*" 16 | - "sigs.k8s.io/*" 17 | cli: 18 | patterns: 19 | - "github.com/spf13/*" 20 | grpc-go: 21 | patterns: 22 | - "google.golang.org/*" 23 | - package-ecosystem: "github-actions" 24 | directory: "/" 25 | schedule: 26 | interval: "monthly" 27 | - package-ecosystem: "docker" 28 | directory: "cmd/cl-controlplane" 29 | schedule: 30 | interval: "monthly" 31 | - package-ecosystem: "docker" 32 | directory: "cmd/cl-go-dataplane" 33 | schedule: 34 | interval: "monthly" 35 | - package-ecosystem: "docker" 36 | directory: "cmd/cl-dataplane" 37 | schedule: 38 | interval: "monthly" 39 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | # .github/release.yml 2 | 3 | changelog: 4 | exclude: 5 | labels: 6 | - ignore-for-release 7 | - dependencies 8 | - documentation 9 | categories: 10 | - title: Breaking Changes 🛠 11 | labels: 12 | - breaking-change 13 | - title: New Features 🎉 14 | labels: 15 | - enhancement 16 | - title: Bug Fixes 🐞 17 | labels: 18 | - bugfix 19 | - title: Other Changes 20 | labels: 21 | - "*" 22 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [ "main" ] 9 | schedule: 10 | - cron: '16 6 * * 3' 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | analyze: 17 | name: Analyze 18 | runs-on: 'ubuntu-latest' 19 | timeout-minutes: 360 20 | permissions: 21 | actions: read 22 | contents: read 23 | security-events: write 24 | 25 | strategy: 26 | fail-fast: false 27 | matrix: 28 | language: [ 'go' ] 29 | 30 | steps: 31 | - name: Checkout repository 32 | uses: actions/checkout@v4 33 | with: 34 | fetch-tags: true 35 | - name: Initialize CodeQL 36 | uses: github/codeql-action/init@v3 37 | with: 38 | languages: ${{ matrix.language }} 39 | 40 | - name: Autobuild 41 | uses: github/codeql-action/autobuild@v3 42 | 43 | - name: Perform CodeQL Analysis 44 | uses: github/codeql-action/analyze@v3 45 | with: 46 | category: "/language:${{matrix.language}}" 47 | -------------------------------------------------------------------------------- /.github/workflows/linkcheck.yml: -------------------------------------------------------------------------------- 1 | name: Periodic Website Link Check 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: "0 0 * * 6" # weekly on Sat midnight 7 | 8 | jobs: 9 | broken-link-checker: 10 | if: github.repository_owner == 'clusterlink-net' # do not run on forks 11 | name: Check broken links 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Check 15 | uses: ruzickap/action-my-broken-link-checker@v2 16 | with: 17 | url: https://clusterlink.net 18 | cmd_params: '--buffer-size=65536 --max-connections=2 --rate-limit=4 --timeout=20' # muffet parameters 19 | -------------------------------------------------------------------------------- /.github/workflows/pr-check.yml: -------------------------------------------------------------------------------- 1 | name: PR check 2 | run-name: "PR #${{ github.event.number }} check (${{ github.sha }})" 3 | 4 | on: 5 | push: 6 | branches: [ main ] 7 | pull_request: 8 | 9 | jobs: 10 | static-checks: 11 | runs-on: ubuntu-22.04 12 | steps: 13 | - name: Check out repository code 14 | uses: actions/checkout@v4 15 | with: 16 | fetch-depth: '0' 17 | - name: Check License Header 18 | uses: apache/skywalking-eyes/header@main 19 | - name: Setup Go 20 | uses: actions/setup-go@v5 21 | with: 22 | go-version-file: ./go.mod 23 | - name: Setup goimports 24 | run: go install golang.org/x/tools/cmd/goimports@v0.13.0 25 | - name: Check go.mod and go.sum 26 | run: go mod tidy && git diff --exit-code 27 | - name: Check generated code 28 | run: GOPATH=$(go env GOPATH) make codegen && git diff --exit-code 29 | - name: Check format 30 | run: goimports -l -w . && git diff --exit-code 31 | - name: Run vet check 32 | run: go vet ./... 33 | - name: Run linters 34 | uses: golangci/golangci-lint-action@v6 35 | with: 36 | version: v1.54.2 37 | skip-pkg-cache: true 38 | 39 | unit-tests: 40 | runs-on: ubuntu-latest 41 | steps: 42 | - name: checkout 43 | uses: actions/checkout@v4 44 | with: 45 | fetch-tags: true 46 | - name: set up go 47 | uses: actions/setup-go@v5 48 | with: 49 | go-version-file: ./go.mod 50 | - name: setup tparse 51 | run: go install github.com/mfridman/tparse@latest 52 | - name: run build 53 | run: make build 54 | - name: run unit tests 55 | run: make unit-tests 56 | -------------------------------------------------------------------------------- /.github/workflows/pr-cli.yml: -------------------------------------------------------------------------------- 1 | name: PR check - CLI Installation test 2 | on: 3 | push: 4 | branches: [ main ] 5 | pull_request: 6 | branches: [ main ] 7 | 8 | jobs: 9 | cli-test: 10 | runs-on: ${{ matrix.os }} 11 | strategy: 12 | matrix: 13 | os: [ubuntu-latest] 14 | 15 | steps: 16 | - name: checkout 17 | uses: actions/checkout@v4 18 | with: 19 | fetch-tags: true 20 | - name: Set up Go 21 | uses: actions/setup-go@v5 22 | with: 23 | go-version-file: ./go.mod 24 | - name: Install kind 25 | uses: helm/kind-action@v1.12.0 26 | with: 27 | install_only: true 28 | - name: Run build 29 | run: make build 30 | - name: Build docker images 31 | run: make docker-build 32 | - name: Run installation test using CLI 33 | run: ./tests/cli/basic_test.sh 34 | - name: upload e2e k8s test logs 35 | uses: actions/upload-artifact@v4 36 | if: failure() 37 | with: 38 | name: tests-cli 39 | path: /tmp/clusterlink-cli 40 | -------------------------------------------------------------------------------- /.github/workflows/pr-e2e-test.yml: -------------------------------------------------------------------------------- 1 | name: PR check - e2e tests 2 | on: 3 | push: 4 | branches: [ main ] 5 | pull_request: 6 | branches: [ main ] 7 | 8 | jobs: 9 | e2e-connectivity-test: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: checkout 14 | uses: actions/checkout@v4 15 | with: 16 | fetch-tags: true 17 | - name: Set up Go 18 | uses: actions/setup-go@v5 19 | with: 20 | go-version-file: ./go.mod 21 | - name: Install kind 22 | uses: helm/kind-action@v1.12.0 23 | with: 24 | install_only: true 25 | - name: Run build 26 | run: make build 27 | - name: Build docker images 28 | run: make docker-build 29 | - name: Run e2e k8s test on a kind cluster 30 | run: CICD=1 ${{ runner.debug == '1' && 'DEBUG=1' || '' }} make tests-e2e-k8s 31 | - name: upload e2e k8s test logs 32 | uses: actions/upload-artifact@v4 33 | if: failure() 34 | with: 35 | name: tests-e2e-k8s 36 | path: /tmp/clusterlink-k8s-tests 37 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release Images and Binaries 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v**' 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | contents: write 13 | packages: write 14 | steps: 15 | - name: checkout 16 | uses: actions/checkout@v4 17 | - name: Set up Go 18 | uses: actions/setup-go@v5 19 | with: 20 | go-version-file: ./go.mod 21 | - name: Login to GitHub Container Registry 22 | uses: docker/login-action@v3 23 | with: 24 | registry: ghcr.io 25 | username: ${{ github.actor }} 26 | password: ${{ secrets.GITHUB_TOKEN }} 27 | - name: Build binaries for amd64 28 | run: GOARCH=amd64 make build 29 | - name: Build binaries for arm64 30 | run: GOARCH=arm64 make build 31 | - name: Tag and push ClusterLink images with tag 'latest' and ${{ github.ref_name }} 32 | run: | 33 | docker buildx create --use --driver docker-container 34 | PLATFORMS=linux/amd64,linux/arm64 make push-image 35 | PLATFORMS=linux/amd64,linux/arm64 make push-image IMAGE_VERSION=${{ github.ref_name }} 36 | - name: Build and compress binaries 37 | run: | 38 | for pair in "linux:amd64" "linux:arm64" "darwin:amd64" "darwin:arm64"; do 39 | IFS=':' read -r os arch <<< "$pair" 40 | GOOS="$os" GOARCH="$arch" CGO_ENABLED=0 make cli-build BIN_DIR="$os" 41 | tar -czvf "clusterlink-$os-$arch.tar.gz" --transform "s/$os/clusterlink/" ./"$os" 42 | done 43 | - name: Upload binaries to release 44 | uses: svenstaro/upload-release-action@v2 45 | with: 46 | repo_token: ${{ secrets.GITHUB_TOKEN }} 47 | file: ./clusterlink* 48 | tag: ${{ github.ref }} 49 | overwrite: true 50 | file_glob: true 51 | - name: Update script installation version 52 | run: sed -i "s/VERSION=\"latest\"/VERSION=\"${{ github.ref_name }}\"/" hack/install_clusterlink.sh 53 | - name: Upload script installation 54 | uses: svenstaro/upload-release-action@v2 55 | with: 56 | repo_token: ${{ secrets.GITHUB_TOKEN }} 57 | file: ./hack/install_clusterlink.sh 58 | asset_name: clusterlink.sh 59 | tag: ${{ github.ref }} 60 | overwrite: true 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | *.pyc 8 | *.pyc 9 | .vscode/ 10 | # Test binary, built with `go test -c` 11 | *.test 12 | 13 | # Output of the go coverage tool, specifically when used with LiteIDE 14 | *.out 15 | 16 | # Dependency directories (remove the comment below to include it) 17 | # vendor/ 18 | 19 | # Binaries folder 20 | bin/ 21 | graphs/ 22 | 23 | # empty file targets 24 | dist/ 25 | -------------------------------------------------------------------------------- /.licenserc.yaml: -------------------------------------------------------------------------------- 1 | header: 2 | license: 3 | spdx-id: Apache-2.0 4 | copyright-owner: Apache Software Foundation 5 | software-name: ClusterLink 6 | content: | 7 | Copyright (c) The ClusterLink Authors. 8 | Licensed under the Apache License, Version 2.0 (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an "AS IS" BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | paths: # <7> 20 | - "**" 21 | 22 | paths-ignore: # <8> 23 | - "dist" 24 | - "licenses" 25 | - "**/*.md" 26 | - "**/*.yaml" 27 | - "**/*.toml" 28 | - "**/*.yml" 29 | - "**/*.html" 30 | - "**/*.txt" 31 | - "**/*.json" 32 | - "**/*.cnf" 33 | - "**/mtls/*" 34 | - "**/not_a_yaml" 35 | - "**/go.mod" 36 | - "**/go.sum" 37 | - ".gitignore" 38 | - "LICENSE" 39 | - "NOTICE" 40 | - "CODEOWNERS" 41 | - "**/Dockerfile" 42 | - "**/dockerfile" 43 | - "**/Containerfile.*" 44 | - "hack/*.sh" 45 | - "**/Makefile" 46 | - "**/assets/languages.yaml" 47 | - "**/assets/assets.gen.go" 48 | - "**/*.scss" 49 | - "**/*.svg" 50 | - "**/*.lock" 51 | - "website/layouts/**" 52 | 53 | comment: on-failure 54 | 55 | # If you don't want to check dependencies' license compatibility, remove the following part 56 | dependency: 57 | files: 58 | - package.json # If this is a npm project. 59 | - go.mod # If this is a Go project. 60 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # These owners will be the default owners for everything in 2 | # the repo. Unless a later match takes precedence, 3 | # Order is important; the last matching pattern takes the most 4 | # precedence. 5 | * @elevran 6 | cmd/ @kfirtoledo 7 | demos/ @kfirtoledo 8 | pkg/ @elevran @kfirtoledo 9 | pkg/dataplane/go @praveingk 10 | pkg/controlplane/authz/ @zivnevo 11 | website/ @elevran @michalmalka 12 | 13 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | We follow the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md). 4 | 5 | Please contact the [CNCF Code of Conduct Committee](mailto:conduct@cncf.io) 6 | in order to report violations of the Code of Conduct. 7 | -------------------------------------------------------------------------------- /Containerfile.dev: -------------------------------------------------------------------------------- 1 | FROM docker.io/library/golang:1.22-bullseye 2 | 3 | # To allow installing kubectl 4 | RUN mkdir /etc/apt/keyrings &&\ 5 | apt-get install -y apt-transport-https ca-certificates curl &&\ 6 | curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.28/deb/Release.key | gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg &&\ 7 | echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.28/deb/ /' | tee /etc/apt/sources.list.d/kubernetes.list 8 | 9 | RUN apt-get update -qy && \ 10 | apt-get install --no-install-recommends -qy \ 11 | ca-certificates \ 12 | curl \ 13 | python3 \ 14 | make \ 15 | git \ 16 | docker.io \ 17 | kubectl \ 18 | && \ 19 | apt-get clean 20 | 21 | # 22 | # Install go build tools, and cache go modules. 23 | # 24 | COPY ./hack/install-devtools.sh /src/ 25 | COPY go.mod /src/ 26 | COPY go.sum /src/ 27 | RUN cd /src &&\ 28 | sh -x ./install-devtools.sh &&\ 29 | go mod download &&\ 30 | cd / &&\ 31 | rm -rf /src 32 | -------------------------------------------------------------------------------- /cmd/cl-controlplane/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.21 2 | 3 | # Populated during the build process, for example, with 'arm64' or 'amd64'. 4 | ARG TARGETARCH 5 | 6 | # Copy binary 7 | RUN mkdir -p /usr/local/bin 8 | COPY ./bin/$TARGETARCH/cl-controlplane /usr/local/bin/cl-controlplane 9 | 10 | # Create directory for private keys 11 | RUN mkdir -p /etc/ssl/private 12 | 13 | # Create directory for certificates 14 | RUN mkdir -p /etc/ssl/certs 15 | 16 | # Create directory for store file 17 | RUN mkdir -p /var/lib/clink 18 | 19 | ENTRYPOINT ["/usr/local/bin/cl-controlplane"] 20 | -------------------------------------------------------------------------------- /cmd/cl-controlplane/cl-controlplane.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // The cl-controlplane binary runs a gRPC server which configure the clink dataplane. 15 | // In addition, it runs an HTTPS server for administrative management (API), 16 | // authorization of remote peers and dataplane connections. 17 | package main 18 | 19 | import ( 20 | "os" 21 | 22 | "github.com/clusterlink-net/clusterlink/cmd/cl-controlplane/app" 23 | "github.com/clusterlink-net/clusterlink/pkg/versioninfo" 24 | ) 25 | 26 | func main() { 27 | command := app.NewCLControlplaneCommand() 28 | command.Version = versioninfo.Short() 29 | if err := command.Execute(); err != nil { 30 | os.Exit(1) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /cmd/cl-dataplane/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM envoyproxy/envoy:v1.34.1 2 | 3 | # Populated during the build process, for example, with 'arm64' or 'amd64'. 4 | ARG TARGETARCH 5 | 6 | # Copy binary 7 | RUN mkdir -p /usr/local/bin 8 | COPY ./bin/$TARGETARCH/cl-dataplane /usr/local/bin/cl-dataplane 9 | 10 | # Create directory for private keys 11 | RUN mkdir -p /etc/ssl/private 12 | 13 | # Create directory for certificates 14 | RUN mkdir -p /etc/ssl/certs 15 | 16 | ENTRYPOINT ["/usr/local/bin/cl-dataplane"] 17 | -------------------------------------------------------------------------------- /cmd/cl-dataplane/app/envoy.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package app 15 | 16 | import ( 17 | "bytes" 18 | "fmt" 19 | "os" 20 | "os/exec" 21 | "text/template" 22 | 23 | cpapi "github.com/clusterlink-net/clusterlink/pkg/controlplane/api" 24 | "github.com/clusterlink-net/clusterlink/pkg/dataplane/api" 25 | ) 26 | 27 | const ( 28 | envoyPath = "/usr/local/bin/envoy" 29 | 30 | adminPort = 1500 31 | ) 32 | 33 | func (o *Options) runEnvoy(dataplaneID string) error { 34 | envoyConfArgs := map[string]interface{}{ 35 | "dataplaneID": dataplaneID, 36 | 37 | "adminPort": adminPort, 38 | 39 | "controlplaneHost": o.ControlplaneHost, 40 | "controlplanePort": cpapi.ListenPort, 41 | 42 | "dataplaneListenPort": api.ListenPort, 43 | 44 | "certificateFile": CertificateFile, 45 | "keyFile": KeyFile, 46 | "caFile": CAFile, 47 | 48 | "controlplaneCluster": cpapi.ControlplaneCluster, 49 | "egressRouterCluster": cpapi.EgressRouterCluster, 50 | 51 | "egressRouterListener": cpapi.EgressRouterListener, 52 | "ingressRouterListener": cpapi.IngressRouterListener, 53 | 54 | "certificateSecret": cpapi.CertificateSecret, 55 | "validationSecret": cpapi.ValidationSecret, 56 | 57 | "authorizationHeader": cpapi.AuthorizationHeader, 58 | "targetClusterHeader": cpapi.TargetClusterHeader, 59 | } 60 | 61 | var envoyConf bytes.Buffer 62 | t := template.Must(template.New("").Parse(envoyConfigurationTemplate)) 63 | if err := t.Execute(&envoyConf, envoyConfArgs); err != nil { 64 | return fmt.Errorf("cannot create Envoy configuration off template: %w", err) 65 | } 66 | 67 | args := []string{ 68 | "--log-level", o.LogLevel, 69 | "--config-yaml", envoyConf.String(), 70 | } 71 | if o.LogFile != "" { 72 | args = append(args, "--log-path", o.LogFile) 73 | } 74 | 75 | cmd := exec.Command(envoyPath, args...) 76 | cmd.Stdout = os.Stdout 77 | cmd.Stderr = os.Stderr 78 | return cmd.Run() 79 | } 80 | -------------------------------------------------------------------------------- /cmd/cl-dataplane/cl-dataplane.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // The cl-dataplane binary runs an instance of a clink dataplane. 15 | package main 16 | 17 | import ( 18 | "os" 19 | 20 | "github.com/clusterlink-net/clusterlink/cmd/cl-dataplane/app" 21 | "github.com/clusterlink-net/clusterlink/pkg/versioninfo" 22 | ) 23 | 24 | func main() { 25 | command := app.NewCLDataplaneCommand() 26 | command.Version = versioninfo.Short() 27 | if err := command.Execute(); err != nil { 28 | os.Exit(1) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /cmd/cl-go-dataplane/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.21 2 | 3 | # Populated during the build process, for example, with 'arm64' or 'amd64'. 4 | ARG TARGETARCH 5 | 6 | # Copy binary 7 | RUN mkdir -p /usr/local/bin 8 | COPY ./bin/$TARGETARCH/cl-go-dataplane /usr/local/bin/cl-go-dataplane 9 | 10 | # Create directory for private keys 11 | RUN mkdir -p /etc/ssl/private 12 | 13 | # Create directory for certificates 14 | RUN mkdir -p /etc/ssl/certs 15 | 16 | ENTRYPOINT ["/usr/local/bin/cl-go-dataplane"] 17 | -------------------------------------------------------------------------------- /cmd/cl-go-dataplane/cl-go-dataplane.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // The cl-dataplane binary runs an instance of a clink dataplane. 15 | package main 16 | 17 | import ( 18 | "os" 19 | 20 | "github.com/clusterlink-net/clusterlink/cmd/cl-go-dataplane/app" 21 | "github.com/clusterlink-net/clusterlink/pkg/versioninfo" 22 | ) 23 | 24 | func main() { 25 | command := app.NewCLGoDataplaneCommand() 26 | command.Version = versioninfo.Short() 27 | if err := command.Execute(); err != nil { 28 | os.Exit(1) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /cmd/cl-operator/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.14 2 | 3 | 4 | 5 | FROM gcr.io/distroless/static:nonroot 6 | WORKDIR / 7 | ARG TARGETARCH 8 | COPY ./bin/$TARGETARCH/cl-operator cl-operator 9 | USER 65532:65532 10 | 11 | ENTRYPOINT ["/cl-operator"] 12 | -------------------------------------------------------------------------------- /cmd/clusterlink/cl-adm.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // The clusterlink binary is used for preparing a clusterlink deployment. 15 | // The deployment includes certificate files for establishing secure TLS connections 16 | // with other cluster components, and configuration for spawning up the various clusterlink 17 | // components in different environments. 18 | package main 19 | 20 | import ( 21 | "os" 22 | 23 | "github.com/clusterlink-net/clusterlink/cmd/clusterlink/cmd" 24 | "github.com/clusterlink-net/clusterlink/pkg/versioninfo" 25 | ) 26 | 27 | func main() { 28 | command := cmd.NewCLADMCommand() 29 | command.Version = versioninfo.Short() 30 | if err := command.Execute(); err != nil { 31 | os.Exit(1) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /cmd/clusterlink/cmd/cmd.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package cmd 15 | 16 | import ( 17 | "github.com/spf13/cobra" 18 | 19 | "github.com/clusterlink-net/clusterlink/cmd/clusterlink/cmd/create" 20 | deletion "github.com/clusterlink-net/clusterlink/cmd/clusterlink/cmd/delete" 21 | "github.com/clusterlink-net/clusterlink/cmd/clusterlink/cmd/deploy" 22 | ) 23 | 24 | // NewCLADMCommand returns a cobra.Command to run the clusterlink command. 25 | func NewCLADMCommand() *cobra.Command { 26 | cmds := &cobra.Command{ 27 | Use: "clusterlink", 28 | Short: "clusterlink: bootstrap a clink fabric", 29 | Long: `clusterlink: bootstrap a clink fabric`, 30 | SilenceUsage: true, 31 | } 32 | 33 | cmds.AddCommand(create.NewCmdCreate()) 34 | cmds.AddCommand(deploy.NewCmdDeploy()) 35 | cmds.AddCommand(deletion.NewCmdDelete()) 36 | 37 | return cmds 38 | } 39 | -------------------------------------------------------------------------------- /cmd/clusterlink/cmd/create/create.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package create 15 | 16 | import ( 17 | "github.com/spf13/cobra" 18 | ) 19 | 20 | // NewCmdCreate returns a cobra.Command to run the create command. 21 | func NewCmdCreate() *cobra.Command { 22 | cmds := &cobra.Command{ 23 | Use: "create", 24 | Short: "Create ClusterLink resources", 25 | Long: "Create ClusterLink resources", 26 | } 27 | 28 | cmds.AddCommand(NewCmdCreateFabric()) 29 | cmds.AddCommand(NewCmdCreatePeerCert()) 30 | 31 | return cmds 32 | } 33 | -------------------------------------------------------------------------------- /cmd/clusterlink/cmd/delete/delete.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package deletion 15 | 16 | import ( 17 | "github.com/spf13/cobra" 18 | ) 19 | 20 | // NewCmdDelete returns a cobra.Command to run the delete command. 21 | func NewCmdDelete() *cobra.Command { 22 | cmds := &cobra.Command{ 23 | Use: "delete", 24 | Short: "Delete ClusterLink resources", 25 | Long: "Delete ClusterLink resources", 26 | } 27 | 28 | cmds.AddCommand(NewCmdDeletePeer()) 29 | 30 | return cmds 31 | } 32 | -------------------------------------------------------------------------------- /cmd/clusterlink/cmd/deploy/deploy.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package deploy 15 | 16 | import ( 17 | "github.com/spf13/cobra" 18 | ) 19 | 20 | // NewCmdDeploy returns a cobra.Command to run the deploy command. 21 | func NewCmdDeploy() *cobra.Command { 22 | cmds := &cobra.Command{ 23 | Use: "deploy", 24 | Short: "Deploy ClusterLink resources", 25 | Long: "Deploy ClusterLink resources", 26 | } 27 | 28 | cmds.AddCommand(NewCmdDeployPeer()) 29 | 30 | return cmds 31 | } 32 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package config 15 | 16 | import "embed" 17 | 18 | //go:embed * 19 | var ConfigFiles embed.FS 20 | -------------------------------------------------------------------------------- /config/operator/manager/manager.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | app.kubernetes.io/name: namespace 7 | app.kubernetes.io/instance: clusterlink-operator 8 | app.kubernetes.io/component: manager 9 | app.kubernetes.io/created-by: cl-operator 10 | app.kubernetes.io/part-of: cl-operator 11 | name: clusterlink-operator 12 | --- 13 | apiVersion: apps/v1 14 | kind: Deployment 15 | metadata: 16 | labels: 17 | app.kubernetes.io/component: manager 18 | app.kubernetes.io/created-by: cl-operator 19 | app.kubernetes.io/instance: controller-manager 20 | app.kubernetes.io/name: deployment 21 | app.kubernetes.io/part-of: cl-operator 22 | control-plane: controller-manager 23 | name: cl-operator-controller-manager 24 | namespace: clusterlink-operator 25 | spec: 26 | replicas: 1 27 | selector: 28 | matchLabels: 29 | control-plane: controller-manager 30 | template: 31 | metadata: 32 | annotations: 33 | kubectl.kubernetes.io/default-container: manager 34 | labels: 35 | control-plane: controller-manager 36 | spec: 37 | containers: 38 | - args: 39 | - --leader-elect 40 | command: 41 | - /cl-operator 42 | image: ghcr.io/clusterlink-net/cl-operator:latest 43 | imagePullPolicy: IfNotPresent 44 | livenessProbe: 45 | httpGet: 46 | path: /healthz 47 | port: 8081 48 | initialDelaySeconds: 15 49 | periodSeconds: 20 50 | name: manager 51 | readinessProbe: 52 | httpGet: 53 | path: /readyz 54 | port: 8081 55 | initialDelaySeconds: 5 56 | periodSeconds: 10 57 | resources: 58 | limits: 59 | cpu: 500m 60 | memory: 128Mi 61 | requests: 62 | cpu: 10m 63 | memory: 64Mi 64 | securityContext: 65 | allowPrivilegeEscalation: false 66 | capabilities: 67 | drop: 68 | - ALL 69 | securityContext: 70 | runAsNonRoot: true 71 | serviceAccountName: cl-operator-controller-manager 72 | terminationGracePeriodSeconds: 10 73 | --- 74 | apiVersion: v1 75 | kind: Namespace 76 | metadata: 77 | labels: 78 | app.kubernetes.io/name: namespace 79 | app.kubernetes.io/instance: clusterlink-system 80 | app.kubernetes.io/created-by: cl-operator 81 | app.kubernetes.io/part-of: cl-operator 82 | name: clusterlink-system 83 | -------------------------------------------------------------------------------- /config/operator/rbac/leader_election_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions to do leader election. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: role 7 | app.kubernetes.io/instance: leader-election-role 8 | app.kubernetes.io/component: rbac 9 | app.kubernetes.io/created-by: cl-operator 10 | app.kubernetes.io/part-of: cl-operator 11 | app.kubernetes.io/managed-by: kustomize 12 | name: cl-operator-leader-election-role 13 | namespace: clusterlink-operator 14 | rules: 15 | - apiGroups: 16 | - "" 17 | resources: 18 | - configmaps 19 | verbs: 20 | - get 21 | - list 22 | - watch 23 | - create 24 | - update 25 | - patch 26 | - delete 27 | - apiGroups: 28 | - coordination.k8s.io 29 | resources: 30 | - leases 31 | verbs: 32 | - get 33 | - list 34 | - watch 35 | - create 36 | - update 37 | - patch 38 | - delete 39 | - apiGroups: 40 | - "" 41 | resources: 42 | - events 43 | verbs: 44 | - create 45 | - patch 46 | -------------------------------------------------------------------------------- /config/operator/rbac/leader_election_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | labels: 5 | app.kubernetes.io/component: rbac 6 | app.kubernetes.io/created-by: cl-operator 7 | app.kubernetes.io/instance: leader-election-rolebinding 8 | app.kubernetes.io/managed-by: kustomize 9 | app.kubernetes.io/name: rolebinding 10 | app.kubernetes.io/part-of: cl-operator 11 | name: cl-operator-leader-election-rolebinding 12 | namespace: clusterlink-operator 13 | roleRef: 14 | apiGroup: rbac.authorization.k8s.io 15 | kind: Role 16 | name: cl-operator-leader-election-role 17 | subjects: 18 | - kind: ServiceAccount 19 | name: cl-operator-controller-manager 20 | namespace: clusterlink-operator 21 | -------------------------------------------------------------------------------- /config/operator/rbac/operator_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit operators. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: clusterrole 7 | app.kubernetes.io/instance: operator-editor-role 8 | app.kubernetes.io/component: rbac 9 | app.kubernetes.io/created-by: cl-operator 10 | app.kubernetes.io/part-of: cl-operator 11 | app.kubernetes.io/managed-by: kustomize 12 | name: cl-operator-operator-editor-role 13 | namespace: clusterlink-operator 14 | rules: 15 | - apiGroups: 16 | - clusterlink.net 17 | resources: 18 | - instances 19 | verbs: 20 | - create 21 | - delete 22 | - get 23 | - list 24 | - patch 25 | - update 26 | - watch 27 | - apiGroups: 28 | - clusterlink.net 29 | resources: 30 | - instances/status 31 | verbs: 32 | - get 33 | -------------------------------------------------------------------------------- /config/operator/rbac/operator_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view operators. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: clusterrole 7 | app.kubernetes.io/instance: operator-viewer-role 8 | app.kubernetes.io/component: rbac 9 | app.kubernetes.io/created-by: cl-operator 10 | app.kubernetes.io/part-of: cl-operator 11 | app.kubernetes.io/managed-by: kustomize 12 | name: cl-operator-operator-viewer-role 13 | namespace: clusterlink-operator 14 | rules: 15 | - apiGroups: 16 | - clusterlink.net 17 | resources: 18 | - instances 19 | verbs: 20 | - get 21 | - list 22 | - watch 23 | - apiGroups: 24 | - clusterlink.net 25 | resources: 26 | - instances/status 27 | verbs: 28 | - get 29 | -------------------------------------------------------------------------------- /config/operator/rbac/role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | labels: 5 | app.kubernetes.io/component: rbac 6 | app.kubernetes.io/created-by: cl-operator 7 | app.kubernetes.io/instance: manager-rolebinding 8 | app.kubernetes.io/managed-by: kustomize 9 | app.kubernetes.io/name: clusterrolebinding 10 | app.kubernetes.io/part-of: cl-operator 11 | name: cl-operator-manager-rolebinding 12 | namespace: clusterlink-operator 13 | roleRef: 14 | apiGroup: rbac.authorization.k8s.io 15 | kind: ClusterRole 16 | name: cl-operator-manager-role 17 | subjects: 18 | - kind: ServiceAccount 19 | name: cl-operator-controller-manager 20 | namespace: clusterlink-operator 21 | -------------------------------------------------------------------------------- /config/operator/rbac/service_account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: serviceaccount 6 | app.kubernetes.io/instance: controller-manager-sa 7 | app.kubernetes.io/component: rbac 8 | app.kubernetes.io/created-by: cl-operator 9 | app.kubernetes.io/part-of: cl-operator 10 | name: cl-operator-controller-manager 11 | namespace: clusterlink-operator 12 | -------------------------------------------------------------------------------- /demos/bookinfo/cloud/apply_lb.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) The ClusterLink Authors. 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | import sys 17 | import argparse 18 | 19 | projDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname( os.path.abspath(__file__))))) 20 | sys.path.insert(0,f'{projDir}') 21 | sys.path.insert(1,f'{projDir}/demos/utils/cloud/') 22 | 23 | from demos.bookinfo.test import applyPolicy 24 | from demos.utils.cloud import Cluster 25 | 26 | srcSvc1 = "productpage" 27 | srcSvc2 = "productpage2" 28 | destSvc = "reviews" 29 | clList = { "peer1gcp" : Cluster(name="peer1", zone = "us-west1-b" , platform = "gcp"), # Oregon 30 | "peer1ibm" : Cluster(name="peer1", zone = "sjc04" , platform = "ibm"), # San jose 31 | "peer2gcp" : Cluster(name="peer2", zone = "us-central1-b" , platform = "gcp"), # Iowa 32 | "peer2ibm" : Cluster(name="peer2", zone = "dal10" , platform = "ibm"), # Dallas 33 | "peer3gcp" : Cluster(name="peer3", zone = "us-east4-b" , platform = "gcp"), # Virginia 34 | "peer3ibm" : Cluster(name="peer3", zone = "wdc04" , platform = "ibm")} # Washington DC 35 | 36 | ############################### MAIN ########################## 37 | if __name__ == "__main__": 38 | parser = argparse.ArgumentParser(description='Description of your program') 39 | parser.add_argument('-p','--peer', help='Either peer1/peer2/peer3', required=False, default="peer1") 40 | parser.add_argument('-t','--type', help='Either round-robin/random/same/diff/clean/show', required=False, default="round-robin") 41 | parser.add_argument('-cloud','--cloud', help='Cloud setup using gcp/ibm', required=False, default="gcp") 42 | 43 | args = vars(parser.parse_args()) 44 | print(f'Working directory {projDir}') 45 | os.chdir(projDir) 46 | cl = clList[args["peer"] + args["cloud"]] 47 | cl.set_kube_config() 48 | applyPolicy(cl, args["type"]) 49 | -------------------------------------------------------------------------------- /demos/bookinfo/cloud/gw_failover.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) The ClusterLink Authors. 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | import sys 17 | import argparse 18 | 19 | projDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname( os.path.abspath(__file__))))) 20 | sys.path.insert(0,f'{projDir}') 21 | sys.path.insert(1,f'{projDir}/demos/utils/cloud/') 22 | from demos.bookinfo.test import apply_failover 23 | from demos.utils.cloud import Cluster 24 | testOutputFolder = f"{projDir}/bin/tests/bookinfo" 25 | 26 | # cl3 parameters 27 | cl3gcp = Cluster(name="peer3", zone = "us-east4-b" , platform = "gcp") # Virginia 28 | cl3ibm = Cluster(name="peer3", zone = "wdc04" , platform = "ibm") # Washington DC 29 | 30 | ############################### MAIN ########################## 31 | if __name__ == "__main__": 32 | parser = argparse.ArgumentParser(description='Description of your program') 33 | parser.add_argument('-t','--type', help='Either fail/start', required=False, default="fail") 34 | parser.add_argument('-cloud','--cloud', help='Cloud setup using gcp/ibm', required=False, default="ibm") 35 | args = vars(parser.parse_args()) 36 | print(f'Working directory {projDir}') 37 | os.chdir(projDir) 38 | cl3 = cl3gcp if args["cloud"] in ["gcp"] else cl3ibm 39 | apply_failover(cl3, args["type"], testOutputFolder) 40 | -------------------------------------------------------------------------------- /demos/bookinfo/kind/README.md: -------------------------------------------------------------------------------- 1 | 2 | # BookInfo application Test 3 | 4 | **This BookInfo script and demo are for internal use only.** 5 | 6 | This demo set [Istio BookInfo application](https://istio.io/latest/docs/examples/bookinfo/) in different clusters. 7 | This demo shows different load-balancing policies like: random, round-robin or static destination. 8 | This test create three kind clusters: 9 | 10 | * Two Product-Page microservice (application frontend) and details microservice run on the first cluster. 11 | * The Reviews-V2 (display rating with black stars) and Rating microservices run on the second cluster. 12 | * The Reviews-V3 (display rating with black stars) and Rating microservices run on the third cluster. 13 | System illustration: 14 | 15 | drawing 16 | 17 | ## Pre-requires installations 18 | 19 | To run a Kind test, check all pre-requires are installed (Go, docker, Kubectl, Kind): 20 | 21 | export PROJECT_FOLDER=`git rev-parse --show-toplevel` 22 | cd $PROJECT_FOLDER 23 | make prereqs 24 | 25 | ## BookInfo test 26 | 27 | Use a single script to build the kind clusters and BookInfo application. 28 | 29 | python3 ./test.py 30 | 31 | To run the BookInfo application use a Firefox web browser to connect the ProductPage microservice: 32 | 33 | export GW1IP=`kubectl get nodes -o jsonpath={.items[0].status.addresses[0].address}` 34 | firefox http://$GW1IP:30001/productpage 35 | firefox http://$GW1IP:30002/productpage 36 | 37 | Note: by default, a random policy is set. 38 | 39 | ### Apply round-robin load balancing policy. 40 | 41 | To apply round-robin load balancing policy to both ProductPage1 and ProductPage2: 42 | 43 | python3 ./apply_lb.py -t round-robin 44 | 45 | ### Apply static load balancing policy 46 | 47 | To apply static policy on ProductPage1 and ProductPage2 that directs them to the same Review service destination: 48 | 49 | python3 ./apply_lb.py -t same 50 | 51 | To apply a static policy on ProductPage1 and ProductPage2 that directs them to different Review service destinations: 52 | 53 | python3 ./apply_lb.py -t same 54 | 55 | ### Clean policy rules 56 | 57 | To clean all the policy rules: 58 | 59 | python3 ./apply_lb.py -t clean 60 | 61 | ### Cleanup 62 | 63 | Delete all Kind cluster. 64 | 65 | make clean-kind 66 | -------------------------------------------------------------------------------- /demos/bookinfo/kind/apply_lb.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) The ClusterLink Authors. 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | import sys 17 | import argparse 18 | 19 | projDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname( os.path.abspath(__file__))))) 20 | sys.path.insert(0,f'{projDir}') 21 | from demos.bookinfo.test import applyPolicy 22 | from demos.utils.kind import Cluster 23 | 24 | ############################### MAIN ########################## 25 | if __name__ == "__main__": 26 | parser = argparse.ArgumentParser(description='Description of your program') 27 | parser.add_argument('-p','--peer', help='Either peer1/peer2/peer3', required=False, default="peer1") 28 | parser.add_argument('-t','--type', help='Either round-robin/random/same/diff/clean/show', required=False, default="round-robin") 29 | 30 | args = vars(parser.parse_args()) 31 | print(f'Working directory {projDir}') 32 | os.chdir(projDir) 33 | cl = Cluster(name=args["peer"]) 34 | cl.set_kube_config() 35 | applyPolicy(cl, args["type"]) 36 | -------------------------------------------------------------------------------- /demos/bookinfo/kind/gw_failover.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) The ClusterLink Authors. 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | import sys 17 | import argparse 18 | 19 | projDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname( os.path.abspath(__file__))))) 20 | sys.path.insert(0,f'{projDir}') 21 | from demos.utils.kind import Cluster 22 | from demos.bookinfo.test import apply_failover 23 | testOutputFolder = f"{projDir}/bin/tests/bookinfo" 24 | 25 | ############################### MAIN ########################## 26 | if __name__ == "__main__": 27 | parser = argparse.ArgumentParser(description='Description of your program') 28 | parser.add_argument('-t','--type', help='Either fail/start', required=False, default="fail") 29 | args = vars(parser.parse_args()) 30 | print(f'Working directory {projDir}') 31 | os.chdir(projDir) 32 | cl3 = Cluster(name="peer3") 33 | apply_failover(cl3, args["type"], testOutputFolder, container_reg="docker.io/library", ingress_type="NodePort", ingress_port=30443) 34 | -------------------------------------------------------------------------------- /demos/bookinfo/manifests/clusterlink/allow-policy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink.net/v1alpha1 2 | kind: AccessPolicy 3 | metadata: 4 | name: allow-policy 5 | namespace: default 6 | spec: 7 | action: allow 8 | from: 9 | - workloadSelector: {} 10 | to: 11 | - workloadSelector: {} 12 | -------------------------------------------------------------------------------- /demos/bookinfo/manifests/clusterlink/deny-server1-policy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink.net/v1alpha1 2 | kind: PrivilegedAccessPolicy 3 | metadata: 4 | name: deny-from-server1 5 | spec: 6 | action: deny 7 | from: [{ 8 | workloadSelector: {} 9 | } 10 | ] 11 | to: 12 | [{ 13 | workloadSelector: { 14 | matchLabels: { 15 | peer.clusterlink.net/name: server1 16 | } 17 | } 18 | } 19 | ] 20 | 21 | -------------------------------------------------------------------------------- /demos/bookinfo/manifests/clusterlink/export-reviews.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink.net/v1alpha1 2 | kind: Export 3 | metadata: 4 | name: reviews 5 | namespace: default 6 | spec: 7 | port: 9080 8 | -------------------------------------------------------------------------------- /demos/bookinfo/manifests/clusterlink/import-reviews-lb-random.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink.net/v1alpha1 2 | kind: Import 3 | metadata: 4 | name: reviews 5 | namespace: default 6 | spec: 7 | port: 9080 8 | sources: 9 | - exportName: reviews 10 | exportNamespace: default 11 | peer: server1 12 | - exportName: reviews 13 | exportNamespace: default 14 | peer: server2 15 | lbScheme: random 16 | -------------------------------------------------------------------------------- /demos/bookinfo/manifests/clusterlink/import-reviews-lb-static.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink.net/v1alpha1 2 | kind: Import 3 | metadata: 4 | name: reviews 5 | namespace: default 6 | spec: 7 | port: 9080 8 | sources: 9 | - exportName: reviews 10 | exportNamespace: default 11 | peer: server1 12 | - exportName: reviews 13 | exportNamespace: default 14 | peer: server2 15 | lbScheme: static 16 | -------------------------------------------------------------------------------- /demos/bookinfo/manifests/clusterlink/import-reviews.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink.net/v1alpha1 2 | kind: Import 3 | metadata: 4 | name: reviews 5 | namespace: default 6 | spec: 7 | port: 9080 8 | sources: 9 | - exportName: reviews 10 | exportNamespace: default 11 | peer: server1 12 | - exportName: reviews 13 | exportNamespace: default 14 | peer: server2 15 | -------------------------------------------------------------------------------- /demos/bookinfo/manifests/clusterlink/peer-client.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink.net/v1alpha1 2 | kind: Peer 3 | metadata: 4 | name: client 5 | namespace: clusterlink-system 6 | spec: 7 | gateways: 8 | - host: "${CLIENT_IP}" 9 | port: 30443 10 | -------------------------------------------------------------------------------- /demos/bookinfo/manifests/clusterlink/peer-server1.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink.net/v1alpha1 2 | kind: Peer 3 | metadata: 4 | name: server1 5 | namespace: clusterlink-system 6 | spec: 7 | gateways: 8 | - host: "${SERVER1_IP}" 9 | port: 30443 10 | -------------------------------------------------------------------------------- /demos/bookinfo/manifests/clusterlink/peer-server2.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink.net/v1alpha1 2 | kind: Peer 3 | metadata: 4 | name: server2 5 | namespace: clusterlink-system 6 | spec: 7 | gateways: 8 | - host: "${SERVER2_IP}" 9 | port: 30443 10 | -------------------------------------------------------------------------------- /demos/bookinfo/manifests/product/details.yaml: -------------------------------------------------------------------------------- 1 | ################################################################################################## 2 | # Details service 3 | ################################################################################################## 4 | apiVersion: v1 5 | kind: Service 6 | metadata: 7 | name: details 8 | labels: 9 | app: details 10 | service: details 11 | spec: 12 | ports: 13 | - port: 9080 14 | name: http 15 | selector: 16 | app: details 17 | --- 18 | apiVersion: apps/v1 19 | kind: Deployment 20 | metadata: 21 | name: details-v1 22 | labels: 23 | app: details 24 | version: v1 25 | spec: 26 | replicas: 1 27 | selector: 28 | matchLabels: 29 | app: details 30 | template: 31 | metadata: 32 | annotations: 33 | sidecar.istio.io/inject: "true" 34 | labels: 35 | app: details 36 | version: v1 37 | spec: 38 | containers: 39 | - name: details 40 | image: docker.io/istio/examples-bookinfo-details-v1 41 | imagePullPolicy: IfNotPresent 42 | ports: 43 | - containerPort: 9080 44 | --- 45 | -------------------------------------------------------------------------------- /demos/bookinfo/manifests/product/product.yaml: -------------------------------------------------------------------------------- 1 | ################################################################################################## 2 | # Productpage services 3 | ################################################################################################## 4 | apiVersion: v1 5 | kind: Service 6 | metadata: 7 | name: productpage 8 | labels: 9 | app: productpage 10 | service: productpage 11 | spec: 12 | ports: 13 | - port: 9080 14 | #targetPort: 9080 15 | nodePort: 30001 16 | #protocol: TCP 17 | name: http 18 | selector: 19 | app: productpage 20 | #type: LoadBalancer 21 | type: NodePort 22 | --- 23 | apiVersion: apps/v1 24 | kind: Deployment 25 | metadata: 26 | name: productpage-v1 27 | labels: 28 | app: productpage 29 | version: v1 30 | spec: 31 | replicas: 1 32 | selector: 33 | matchLabels: 34 | app: productpage 35 | template: 36 | metadata: 37 | labels: 38 | app: productpage 39 | version: v1 40 | spec: 41 | containers: 42 | - name: productpage 43 | image: docker.io/istio/examples-bookinfo-productpage-v1 44 | imagePullPolicy: IfNotPresent 45 | ports: 46 | - containerPort: 9080 47 | -------------------------------------------------------------------------------- /demos/bookinfo/manifests/product/product2.yaml: -------------------------------------------------------------------------------- 1 | ################################################################################################## 2 | # Productpage services 3 | ################################################################################################## 4 | apiVersion: v1 5 | kind: Service 6 | metadata: 7 | name: productpage2 8 | labels: 9 | app: productpage2 10 | service: productpage2 11 | spec: 12 | ports: 13 | - port: 9080 14 | #targetPort: 9080 15 | nodePort: 30002 16 | #protocol: TCP 17 | name: http 18 | selector: 19 | app: productpage2 20 | #type: LoadBalancer 21 | type: NodePort 22 | --- 23 | apiVersion: apps/v1 24 | kind: Deployment 25 | metadata: 26 | name: productpage2-v1 27 | labels: 28 | app: productpage2 29 | version: v1 30 | spec: 31 | replicas: 1 32 | selector: 33 | matchLabels: 34 | app: productpage2 35 | template: 36 | metadata: 37 | labels: 38 | app: productpage2 39 | version: v1 40 | spec: 41 | containers: 42 | - name: productpage2 43 | image: docker.io/istio/examples-bookinfo-productpage-v1 44 | imagePullPolicy: IfNotPresent 45 | ports: 46 | - containerPort: 9080 47 | -------------------------------------------------------------------------------- /demos/bookinfo/manifests/review/rating.yaml: -------------------------------------------------------------------------------- 1 | ################################################################################################## 2 | # Ratings service 3 | ################################################################################################## 4 | apiVersion: v1 5 | kind: Service 6 | metadata: 7 | name: ratings 8 | labels: 9 | app: ratings 10 | service: ratings 11 | spec: 12 | ports: 13 | - port: 9080 14 | name: http 15 | selector: 16 | app: ratings 17 | --- 18 | apiVersion: apps/v1 19 | kind: Deployment 20 | metadata: 21 | name: ratings-v1 22 | labels: 23 | app: ratings 24 | version: v1 25 | spec: 26 | replicas: 1 27 | selector: 28 | matchLabels: 29 | app: ratings 30 | template: 31 | metadata: 32 | labels: 33 | app: ratings 34 | version: v1 35 | spec: 36 | containers: 37 | - name: ratings 38 | image: docker.io/istio/examples-bookinfo-ratings-v1 39 | imagePullPolicy: IfNotPresent 40 | ports: 41 | - containerPort: 9080 42 | --- 43 | -------------------------------------------------------------------------------- /demos/bookinfo/manifests/review/review-v2.yaml: -------------------------------------------------------------------------------- 1 | ################################################################################################## 2 | # Reviews service 3 | ################################################################################################## 4 | apiVersion: v1 5 | kind: Service 6 | metadata: 7 | name: reviews 8 | labels: 9 | app: reviews 10 | service: reviews 11 | spec: 12 | ports: 13 | - port: 9080 14 | name: http 15 | selector: 16 | app: reviews 17 | --- 18 | apiVersion: apps/v1 19 | kind: Deployment 20 | metadata: 21 | name: reviews-v2 22 | labels: 23 | app: reviews-v2 24 | version: v2 25 | spec: 26 | replicas: 1 27 | selector: 28 | matchLabels: 29 | app: reviews 30 | template: 31 | metadata: 32 | labels: 33 | app: reviews 34 | version: v2 35 | spec: 36 | containers: 37 | - name: reviews-v2 38 | image: docker.io/istio/examples-bookinfo-reviews-v2 39 | imagePullPolicy: IfNotPresent 40 | ports: 41 | - containerPort: 9080 42 | --- 43 | -------------------------------------------------------------------------------- /demos/bookinfo/manifests/review/review-v3.yaml: -------------------------------------------------------------------------------- 1 | ################################################################################################## 2 | # Reviews service 3 | ################################################################################################## 4 | apiVersion: v1 5 | kind: Service 6 | metadata: 7 | name: reviews 8 | labels: 9 | app: reviews 10 | service: reviews 11 | spec: 12 | ports: 13 | - port: 9080 14 | name: http 15 | selector: 16 | app: reviews 17 | --- 18 | apiVersion: apps/v1 19 | kind: Deployment 20 | metadata: 21 | name: reviews-v3 22 | labels: 23 | app: reviews-v3 24 | version: v3 25 | spec: 26 | replicas: 1 27 | selector: 28 | matchLabels: 29 | app: reviews 30 | template: 31 | metadata: 32 | labels: 33 | app: reviews 34 | version: v3 35 | spec: 36 | containers: 37 | - name: reviews-v3 38 | image: docker.io/istio/examples-bookinfo-reviews-v3 39 | imagePullPolicy: IfNotPresent 40 | ports: 41 | - containerPort: 9080 42 | --- 43 | -------------------------------------------------------------------------------- /demos/frp/kind/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) The ClusterLink Authors. 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ################################################################ 16 | # Name: FRP demo that connect 3 Kind clusters using FRP: 17 | # Desc: create 3 kind clusters : 18 | # 1) GW, iPerf3 client, FRP client, and FRP server 19 | # 2) GW, iPerf3 server, and FRP client 20 | # 3) GW, iPerf3 client, and FRP client 21 | ############################################################### 22 | import os 23 | import sys 24 | import time 25 | projDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname( os.path.abspath(__file__))))) 26 | sys.path.insert(0,f'{projDir}') 27 | 28 | from demos.utils.common import printHeader 29 | from demos.utils.kind import Cluster 30 | from demos.iperf3.kind.iperf3_client_start import directTestIperf3,testIperf3Client 31 | from demos.frp.test import frpTest 32 | 33 | testOutputFolder = f"{projDir}/bin/tests/frp" 34 | 35 | ############################### MAIN ########################## 36 | if __name__ == "__main__": 37 | printHeader("\n\nStart Kind Test\n\n") 38 | printHeader("Start pre-setting") 39 | 40 | # cl parameters 41 | cl1= Cluster("peer1") 42 | cl2= Cluster("peer2") 43 | cl3= Cluster("peer3") 44 | srcSvc = "iperf3-client" 45 | destSvc = "iperf3-server" 46 | destPort = 5000 47 | iperf3DirectPort = "30001" 48 | 49 | # Setup 50 | frpTest(cl1, cl2, cl3, testOutputFolder) 51 | #Testing 52 | printHeader("\n\nStart Iperf3 testing") 53 | cl2.useCluster() 54 | cl2.setKindIp() 55 | directTestIperf3(cl1, srcSvc, cl2.ip, iperf3DirectPort) 56 | time.sleep(5) 57 | testIperf3Client(cl1, srcSvc, destSvc, destPort) 58 | testIperf3Client(cl3, srcSvc, destSvc, destPort) 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /demos/frp/testdata/manifests/client/frpc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: frpc 5 | namespace: frp 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: frpc 11 | template: 12 | metadata: 13 | labels: 14 | app: frpc 15 | spec: 16 | # hostNetwork: true 17 | containers: 18 | - name: frpc 19 | image: snowdreamtech/frpc 20 | imagePullPolicy: IfNotPresent 21 | volumeMounts: 22 | - name: frpc-config-volume 23 | mountPath: /etc/frp 24 | volumes: 25 | - name: frpc-config-volume 26 | configMap: 27 | name: frpc-config 28 | -------------------------------------------------------------------------------- /demos/frp/testdata/manifests/client/peer1/frpc-configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: frpc-config 5 | namespace: frp 6 | data: 7 | frpc.toml: | 8 | # Set server address 9 | serverAddr = "${FRP_SERVER_IP}" 10 | serverPort = 30444 11 | 12 | [[proxies]] 13 | name = "clusterlink-peer1" 14 | type = "stcp" 15 | localIP = "clusterlink.clusterlink-system.svc.cluster.local" 16 | localPort = 443 17 | secretKey = "${FRP_SECRET_KEY}" 18 | 19 | [[visitors]] 20 | name = "clusterlink-peer1-to-peer2-visitor" 21 | type = "stcp" 22 | serverName = "clusterlink-peer2" 23 | secretKey = "${FRP_SECRET_KEY}" 24 | bindAddr = "::" 25 | bindPort = 6002 26 | 27 | [[visitors]] 28 | name = "clusterlink-peer1-to-peer3-visitor" 29 | type = "stcp" 30 | serverName = "clusterlink-peer3" 31 | secretKey = "${FRP_SECRET_KEY}" 32 | bindAddr = "::" 33 | bindPort = 6003 34 | -------------------------------------------------------------------------------- /demos/frp/testdata/manifests/client/peer1/peer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink.net/v1alpha1 2 | kind: Peer 3 | metadata: 4 | name: peer2 5 | namespace: clusterlink-system 6 | spec: 7 | gateways: 8 | - host: frp-peer2-clusterlink.frp.svc.cluster.local 9 | port: 6002 10 | 11 | --- 12 | apiVersion: v1 13 | kind: Service 14 | metadata: 15 | name: frp-peer2-clusterlink 16 | namespace: frp 17 | spec: 18 | type: ClusterIP 19 | selector: 20 | app: frpc 21 | ports: 22 | - port: 6002 23 | targetPort: 6002 24 | --- 25 | apiVersion: clusterlink.net/v1alpha1 26 | kind: Peer 27 | metadata: 28 | name: peer3 29 | namespace: clusterlink-system 30 | spec: 31 | gateways: 32 | - host: frp-peer3-clusterlink.frp.svc.cluster.local 33 | port: 6003 34 | 35 | --- 36 | apiVersion: v1 37 | kind: Service 38 | metadata: 39 | name: frp-peer3-clusterlink 40 | namespace: frp 41 | spec: 42 | type: ClusterIP 43 | selector: 44 | app: frpc 45 | ports: 46 | - port: 6003 47 | targetPort: 6003 48 | -------------------------------------------------------------------------------- /demos/frp/testdata/manifests/client/peer2/frpc-configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: frpc-config 5 | namespace: frp 6 | data: 7 | frpc.toml: | 8 | # Set server address 9 | serverAddr = "${FRP_SERVER_IP}" 10 | serverPort = 30444 11 | 12 | [[proxies]] 13 | name = "clusterlink-peer2" 14 | type = "stcp" 15 | localIP = "clusterlink.clusterlink-system.svc.cluster.local" 16 | localPort = 443 17 | secretKey = "${FRP_SECRET_KEY}" 18 | 19 | [[visitors]] 20 | name = "clusterlink-peer2-to-peer1-visitor" 21 | type = "stcp" 22 | serverName = "clusterlink-peer1" 23 | secretKey = "${FRP_SECRET_KEY}" 24 | bindAddr = "::" 25 | bindPort = 6001 26 | 27 | [[visitors]] 28 | name = "clusterlink-peer2-to-peer3-visitor" 29 | type = "stcp" 30 | serverName = "clusterlink-peer3" 31 | secretKey = "${FRP_SECRET_KEY}" 32 | bindAddr = "::" 33 | bindPort = 6003 34 | -------------------------------------------------------------------------------- /demos/frp/testdata/manifests/client/peer2/peer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink.net/v1alpha1 2 | kind: Peer 3 | metadata: 4 | name: peer1 5 | namespace: clusterlink-system 6 | spec: 7 | gateways: 8 | - host: frp-peer1-clusterlink.frp.svc.cluster.local 9 | port: 6001 10 | --- 11 | apiVersion: v1 12 | kind: Service 13 | metadata: 14 | name: frp-peer1-clusterlink 15 | namespace: frp 16 | spec: 17 | type: ClusterIP 18 | selector: 19 | app: frpc 20 | ports: 21 | - port: 6001 22 | targetPort: 6001 23 | --- 24 | apiVersion: clusterlink.net/v1alpha1 25 | kind: Peer 26 | metadata: 27 | name: peer3 28 | namespace: clusterlink-system 29 | spec: 30 | gateways: 31 | - host: frp-peer3-clusterlink.frp.svc.cluster.local 32 | port: 6003 33 | --- 34 | apiVersion: v1 35 | kind: Service 36 | metadata: 37 | name: frp-peer3-clusterlink 38 | namespace: frp 39 | spec: 40 | type: ClusterIP 41 | selector: 42 | app: frpc 43 | ports: 44 | - port: 6003 45 | targetPort: 6003 46 | -------------------------------------------------------------------------------- /demos/frp/testdata/manifests/client/peer3/frpc-configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: frpc-config 5 | namespace: frp 6 | data: 7 | frpc.toml: | 8 | # Set server address 9 | serverAddr = "${FRP_SERVER_IP}" 10 | serverPort = 30444 11 | 12 | [[proxies]] 13 | name = "clusterlink-peer3" 14 | type = "stcp" 15 | localIP = "clusterlink.clusterlink-system.svc.cluster.local" 16 | localPort = 443 17 | secretKey = "${FRP_SECRET_KEY}" 18 | 19 | [[visitors]] 20 | name = "clusterlink-peer3-to-peer1-visitor" 21 | type = "stcp" 22 | serverName = "clusterlink-peer1" 23 | secretKey = "${FRP_SECRET_KEY}" 24 | bindAddr = "::" 25 | bindPort = 6001 26 | 27 | [[visitors]] 28 | name = "clusterlink-peer3-to-peer2-visitor" 29 | type = "stcp" 30 | serverName = "clusterlink-peer2" 31 | secretKey = "${FRP_SECRET_KEY}" 32 | bindAddr = "::" 33 | bindPort = 6002 34 | -------------------------------------------------------------------------------- /demos/frp/testdata/manifests/client/peer3/peer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink.net/v1alpha1 2 | kind: Peer 3 | metadata: 4 | name: peer1 5 | namespace: clusterlink-system 6 | spec: 7 | gateways: 8 | - host: frp-peer1-clusterlink.frp.svc.cluster.local 9 | port: 6001 10 | 11 | --- 12 | apiVersion: v1 13 | kind: Service 14 | metadata: 15 | name: frp-peer1-clusterlink 16 | namespace: frp 17 | spec: 18 | type: ClusterIP 19 | selector: 20 | app: frpc 21 | ports: 22 | - port: 6001 23 | targetPort: 6001 24 | --- 25 | apiVersion: clusterlink.net/v1alpha1 26 | kind: Peer 27 | metadata: 28 | name: peer2 29 | namespace: clusterlink-system 30 | spec: 31 | gateways: 32 | - host: frp-peer2-clusterlink.frp.svc.cluster.local 33 | port: 6002 34 | 35 | --- 36 | apiVersion: v1 37 | kind: Service 38 | metadata: 39 | name: frp-peer2-clusterlink 40 | namespace: frp 41 | spec: 42 | type: ClusterIP 43 | selector: 44 | app: frpc 45 | ports: 46 | - port: 6002 47 | targetPort: 6002 48 | -------------------------------------------------------------------------------- /demos/frp/testdata/manifests/frp-ns.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: frp 5 | -------------------------------------------------------------------------------- /demos/frp/testdata/manifests/server/frps-configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: frps-config 5 | namespace: frp 6 | data: 7 | frps.toml: | 8 | bindPort = 4443 9 | -------------------------------------------------------------------------------- /demos/frp/testdata/manifests/server/frps.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: frps 5 | namespace: frp 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: frps 11 | template: 12 | metadata: 13 | labels: 14 | app: frps 15 | spec: 16 | hostNetwork: true 17 | containers: 18 | - name: frps 19 | image: snowdreamtech/frps 20 | imagePullPolicy: IfNotPresent 21 | volumeMounts: 22 | - name: frps-config-volume 23 | mountPath: /etc/frp/frps.toml 24 | subPath: frps.toml 25 | volumes: 26 | - name: frps-config-volume 27 | configMap: 28 | name: frps-config 29 | --- 30 | apiVersion: v1 31 | kind: Service 32 | metadata: 33 | name: clusterlink-frps 34 | namespace: frp 35 | spec: 36 | type: NodePort 37 | selector: 38 | app: frps 39 | ports: 40 | - port: 4443 41 | targetPort: 4443 42 | nodePort: 30444 43 | -------------------------------------------------------------------------------- /demos/iperf3/kind/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) The ClusterLink Authors. 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ################################################################ 16 | # Name: Simple iperf3 test 17 | # Desc: create 2 kind clusters : 18 | # 1) GW and iperf3 client 19 | # 2) GW and iperf3 server 20 | ############################################################### 21 | import os 22 | import sys 23 | import argparse 24 | projDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname( os.path.abspath(__file__))))) 25 | sys.path.insert(0,f'{projDir}') 26 | 27 | from demos.utils.common import printHeader 28 | from demos.utils.kind import Cluster 29 | from demos.iperf3.kind.iperf3_client_start import directTestIperf3,testIperf3Client 30 | from demos.iperf3.test import iperf3Test 31 | 32 | testOutputFolder = f"{projDir}/bin/tests/iperf3" 33 | 34 | ############################### MAIN ########################## 35 | if __name__ == "__main__": 36 | parser = argparse.ArgumentParser(description='Description of your program') 37 | parser.add_argument('-l','--logLevel', help='The log level. One of fatal, error, warn, info, debug.', required=False, default="info") 38 | parser.add_argument('-d','--dataplane', help='Which dataplane to use envoy/go', required=False, default="envoy") 39 | 40 | args = vars(parser.parse_args()) 41 | printHeader("\n\nStart Kind Test\n\n") 42 | printHeader("Start pre-setting") 43 | 44 | # cl parameters 45 | cl1= Cluster("peer1") 46 | cl2= Cluster("peer2") 47 | srcSvc = "iperf3-client" 48 | destSvc = "iperf3-server" 49 | destPort = 5000 50 | iperf3DirectPort = "30001" 51 | 52 | # Setup 53 | iperf3Test(cl1, cl2, testOutputFolder, args["logLevel"], args["dataplane"]) 54 | #Testing 55 | printHeader("\n\nStart Iperf3 testing") 56 | directTestIperf3(cl1, srcSvc, cl2.ip, iperf3DirectPort) 57 | testIperf3Client(cl1, srcSvc, destSvc, destPort) 58 | 59 | 60 | -------------------------------------------------------------------------------- /demos/iperf3/testdata/manifests/clusterlink/allow-policy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink.net/v1alpha1 2 | kind: AccessPolicy 3 | metadata: 4 | name: allow-policy 5 | namespace: default 6 | spec: 7 | action: allow 8 | from: 9 | - workloadSelector: {} 10 | to: 11 | - workloadSelector: {} 12 | -------------------------------------------------------------------------------- /demos/iperf3/testdata/manifests/clusterlink/export-iperf3.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink.net/v1alpha1 2 | kind: Export 3 | metadata: 4 | name: iperf3-server 5 | namespace: default 6 | spec: 7 | port: 5000 8 | -------------------------------------------------------------------------------- /demos/iperf3/testdata/manifests/clusterlink/import-iperf3.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink.net/v1alpha1 2 | kind: Import 3 | metadata: 4 | name: iperf3-server 5 | namespace: default 6 | spec: 7 | port: 5000 8 | sources: 9 | - exportName: iperf3-server 10 | exportNamespace: default 11 | peer: server 12 | -------------------------------------------------------------------------------- /demos/iperf3/testdata/manifests/clusterlink/peer-client.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink.net/v1alpha1 2 | kind: Peer 3 | metadata: 4 | name: client 5 | namespace: clusterlink-system 6 | spec: 7 | gateways: 8 | - host: "${CLIENT_IP}" 9 | port: 30443 10 | -------------------------------------------------------------------------------- /demos/iperf3/testdata/manifests/clusterlink/peer-server.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink.net/v1alpha1 2 | kind: Peer 3 | metadata: 4 | name: server 5 | namespace: clusterlink-system 6 | spec: 7 | gateways: 8 | - host: "${SERVER_IP}" 9 | port: 30443 10 | -------------------------------------------------------------------------------- /demos/iperf3/testdata/manifests/iperf3-client/iperf3-client.yaml: -------------------------------------------------------------------------------- 1 | ################################################################ 2 | #Name: iperf3_client 3 | #Desc: YAML file for creating iperf3 client to send test traffic. 4 | ################################################################ 5 | apiVersion: apps/v1 6 | kind: DaemonSet 7 | metadata: 8 | name: iperf3-client 9 | labels: 10 | app: iperf3-client 11 | spec: 12 | selector: 13 | matchLabels: 14 | app: iperf3-client 15 | template: 16 | metadata: 17 | labels: 18 | app: iperf3-client 19 | spec: 20 | tolerations: 21 | - key: node-role.kubernetes.io/master 22 | operator: Exists 23 | effect: NoSchedule 24 | containers: 25 | - name: iperf3-client 26 | #image: networkstatic/iperf3 27 | image: taoyou/iperf3-alpine 28 | imagePullPolicy: IfNotPresent 29 | command: ['/bin/sh', '-c', 'sleep infinity'] 30 | # To benchmark manually: kubectl exec iperf3-client-jlfxq -- /bin/sh -c 'iperf3 -c iperf3-server' 31 | terminationGracePeriodSeconds: 0 32 | -------------------------------------------------------------------------------- /demos/iperf3/testdata/manifests/iperf3-client/iperf3-client2.yaml: -------------------------------------------------------------------------------- 1 | ################################################################ 2 | #Name: iperf3_client 3 | #Desc: YAML file for creating iperf3 client to send test traffic. 4 | ################################################################ 5 | apiVersion: apps/v1 6 | kind: DaemonSet 7 | metadata: 8 | name: iperf3-client2 9 | labels: 10 | app: iperf3-client2 11 | spec: 12 | selector: 13 | matchLabels: 14 | app: iperf3-client2 15 | template: 16 | metadata: 17 | labels: 18 | app: iperf3-client2 19 | spec: 20 | tolerations: 21 | - key: node-role.kubernetes.io/master 22 | operator: Exists 23 | effect: NoSchedule 24 | containers: 25 | - name: iperf3-client2 26 | #image: networkstatic/iperf3 27 | image: taoyou/iperf3-alpine 28 | imagePullPolicy: IfNotPresent 29 | command: ['/bin/sh', '-c', 'sleep infinity'] 30 | # To benchmark manually: kubectl exec iperf3-client2-jlfxq -- /bin/sh -c 'iperf3 -c iperf3-server' 31 | terminationGracePeriodSeconds: 0 32 | -------------------------------------------------------------------------------- /demos/iperf3/testdata/manifests/iperf3-client/kind-config.yaml: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | #Name:config 3 | #Desc: Kind config for exposing port 30000 of kind cluster 4 | # Can access through :30000 or :20000 5 | ############################################################################### 6 | # cluster-config.yml 7 | kind: Cluster 8 | apiVersion: kind.x-k8s.io/v1alpha4 9 | nodes: 10 | - role: control-plane 11 | extraPortMappings: 12 | - containerPort: 30000 13 | hostPort: 20000 14 | protocol: TCP 15 | -------------------------------------------------------------------------------- /demos/iperf3/testdata/manifests/iperf3-server/iperf3-svc.yaml: -------------------------------------------------------------------------------- 1 | ################################################################ 2 | #Name: Iperf3-svc 3 | #Desc: service file for creating a load-balancer with external 4 | # port 5500 connect to iperf3-server 5 | ################################################################ 6 | apiVersion: v1 7 | kind: Service 8 | metadata: 9 | name: iperf3-loadbalancer-service 10 | spec: 11 | type: LoadBalancer 12 | selector: 13 | app: iperf3-server 14 | ports: 15 | # By default and for convenience, the `targetPort` is set to the same value as the `port` field. 16 | - port: 5000 17 | targetPort: 5000 18 | # Optional field 19 | # By default and for convenience, the Kubernetes control plane will allocate a port from a range (default: 30000-32767) 20 | nodePort: 30001 -------------------------------------------------------------------------------- /demos/iperf3/testdata/manifests/iperf3-server/iperf3.yaml: -------------------------------------------------------------------------------- 1 | ################################################################ 2 | #Name: iperf3 3 | #Desc: YAML file for creating iperf3 server for testing. 4 | ################################################################ 5 | apiVersion: apps/v1 6 | kind: Deployment 7 | metadata: 8 | name: iperf3-server-deployment 9 | labels: 10 | app: iperf3-server 11 | spec: 12 | replicas: 1 13 | selector: 14 | matchLabels: 15 | app: iperf3-server 16 | template: 17 | metadata: 18 | labels: 19 | app: iperf3-server 20 | spec: 21 | affinity: 22 | nodeAffinity: 23 | preferredDuringSchedulingIgnoredDuringExecution: 24 | - weight: 1 25 | preference: 26 | matchExpressions: 27 | - key: kubernetes.io/role 28 | operator: In 29 | values: 30 | - master 31 | tolerations: 32 | - key: node-role.kubernetes.io/master 33 | operator: Exists 34 | effect: NoSchedule 35 | # initContainers: 36 | # - name: init-sysctl 37 | # image: busybox 38 | # command: 39 | # - sysctl 40 | # - -w 41 | # - net.ipv4.tcp_congestion_control=bbr 42 | # securityContext: 43 | # privileged: true 44 | containers: 45 | - name: iperf3-server 46 | #image: networkstatic/iperf3 47 | image: taoyou/iperf3-alpine 48 | imagePullPolicy: IfNotPresent 49 | args: ['-s', '-p', '5000'] 50 | # ports: 51 | # - containerPort: 50051 52 | # name: server 53 | terminationGracePeriodSeconds: 0 54 | --- 55 | apiVersion: v1 56 | kind: Service 57 | metadata: 58 | name: iperf3-server 59 | spec: 60 | type: NodePort 61 | selector: 62 | app: iperf3-server 63 | ports: 64 | - port: 5000 65 | targetPort: 5000 66 | nodePort: 30001 67 | -------------------------------------------------------------------------------- /demos/iperf3/testdata/manifests/iperf3-server/kind-config.yaml: -------------------------------------------------------------------------------- 1 | ################################################################ 2 | #Name:config 3 | #Desc: Kind config for exposing port 30000 of kind 4 | # cluster 5 | ################################################################ 6 | # cluster-config.yml 7 | kind: Cluster 8 | apiVersion: kind.x-k8s.io/v1alpha4 9 | nodes: 10 | - role: control-plane 11 | extraPortMappings: 12 | - containerPort: 30000 13 | hostPort: 21000 14 | protocol: TCP -------------------------------------------------------------------------------- /demos/nginx/testdata/clusterlink/allow-policy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink.net/v1alpha1 2 | kind: AccessPolicy 3 | metadata: 4 | name: allow-policy 5 | namespace: default 6 | spec: 7 | action: allow 8 | from: 9 | - workloadSelector: {} 10 | to: 11 | - workloadSelector: {} 12 | -------------------------------------------------------------------------------- /demos/nginx/testdata/clusterlink/export-nginx.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink.net/v1alpha1 2 | kind: Export 3 | metadata: 4 | name: nginx 5 | namespace: default 6 | spec: 7 | port: 80 8 | -------------------------------------------------------------------------------- /demos/nginx/testdata/clusterlink/import-nginx-relay.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink.net/v1alpha1 2 | kind: Import 3 | metadata: 4 | name: nginx-relay 5 | namespace: default 6 | spec: 7 | port: 80 8 | sources: 9 | - exportName: nginx 10 | exportNamespace: default 11 | peer: server 12 | -------------------------------------------------------------------------------- /demos/nginx/testdata/clusterlink/import-nginx.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink.net/v1alpha1 2 | kind: Import 3 | metadata: 4 | name: nginx 5 | namespace: default 6 | spec: 7 | port: 80 8 | sources: 9 | - exportName: nginx 10 | exportNamespace: default 11 | peer: server 12 | -------------------------------------------------------------------------------- /demos/nginx/testdata/clusterlink/peer-client.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink.net/v1alpha1 2 | kind: Peer 3 | metadata: 4 | name: client 5 | namespace: clusterlink-system 6 | spec: 7 | gateways: 8 | - host: "${CLIENT_IP}" 9 | port: 30443 10 | -------------------------------------------------------------------------------- /demos/nginx/testdata/clusterlink/peer-relay.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink.net/v1alpha1 2 | kind: Peer 3 | metadata: 4 | name: relay 5 | namespace: clusterlink-system 6 | spec: 7 | gateways: 8 | - host: "${RELAY_IP}" 9 | port: 30443 10 | -------------------------------------------------------------------------------- /demos/nginx/testdata/clusterlink/peer-server.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink.net/v1alpha1 2 | kind: Peer 3 | metadata: 4 | name: server 5 | namespace: clusterlink-system 6 | spec: 7 | gateways: 8 | - host: "${SERVER_IP}" 9 | port: 30443 10 | -------------------------------------------------------------------------------- /demos/nginx/testdata/nginx-job.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: Job 3 | metadata: 4 | name: curl-nginx-homepage 5 | spec: 6 | template: 7 | spec: 8 | containers: 9 | - name: curl 10 | image: curlimages/curl:latest 11 | command: ["curl", "http://nginx.default.svc.cluster.local"] 12 | restartPolicy: Never 13 | -------------------------------------------------------------------------------- /demos/nginx/testdata/nginx-relay-job.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: Job 3 | metadata: 4 | name: curl-nginx-relay-homepage 5 | spec: 6 | template: 7 | spec: 8 | containers: 9 | - name: curl 10 | image: curlimages/curl:latest 11 | command: ["curl", "http://nginx-relay.default.svc.cluster.local"] 12 | restartPolicy: Never 13 | -------------------------------------------------------------------------------- /demos/nginx/testdata/nginx-server.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: nginx-deployment 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: nginx 10 | template: 11 | metadata: 12 | labels: 13 | app: nginx 14 | spec: 15 | containers: 16 | - name: nginx 17 | image: nginx:latest 18 | ports: 19 | - containerPort: 80 20 | --- 21 | apiVersion: v1 22 | kind: Service 23 | metadata: 24 | name: nginx 25 | spec: 26 | selector: 27 | app: nginx 28 | ports: 29 | - protocol: TCP 30 | port: 80 31 | targetPort: 80 32 | -------------------------------------------------------------------------------- /demos/qotd/manifests/qotd_db.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: qotd-db 5 | namespace: qotd-svc-iks 6 | labels: 7 | app: qotd 8 | tier: data 9 | spec: 10 | replicas: 1 11 | selector: 12 | matchLabels: 13 | app: qotd-db 14 | template: 15 | metadata: 16 | labels: 17 | app: qotd-db 18 | spec: 19 | containers: 20 | - name: qotd-db 21 | image: registry.gitlab.com/quote-of-the-day/qotd-db/v4.0.0:latest 22 | imagePullPolicy: Always 23 | ports: 24 | - name: mysql 25 | containerPort: 3306 26 | protocol: TCP 27 | env: 28 | - name: MYSQL_ROOT_PASSWORD 29 | value: "root" 30 | - name: MYSQL_DATABASE 31 | value: "qotd" 32 | --- 33 | apiVersion: v1 34 | kind: Service 35 | metadata: 36 | name: qotd-db 37 | namespace: qotd-svc-iks 38 | labels: 39 | app: qotd-db 40 | tier: data 41 | spec: 42 | type: ClusterIP 43 | ports: 44 | - name: mysql 45 | port: 3306 46 | targetPort: 3306 47 | protocol: TCP 48 | selector: 49 | app: qotd-db -------------------------------------------------------------------------------- /demos/qotd/manifests/qotd_engraving.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: qotd 5 | --- 6 | kind: Deployment 7 | apiVersion: apps/v1 8 | metadata: 9 | name: qotd-engraving 10 | namespace: qotd 11 | labels: 12 | app: qotd 13 | tier: api 14 | spec: 15 | replicas: 2 16 | selector: 17 | matchLabels: 18 | app: qotd-engraving 19 | template: 20 | metadata: 21 | labels: 22 | app: qotd-engraving 23 | spec: 24 | restartPolicy: Always 25 | containers: 26 | - name: qotd-engraving 27 | image: registry.gitlab.com/quote-of-the-day/qotd-engraving-service/v4.0.0:latest 28 | imagePullPolicy: Always 29 | ports: 30 | - name: http 31 | containerPort: 3006 32 | protocol: TCP 33 | env: 34 | - name: LOG_LEVEL 35 | value: "INFO" 36 | - name: ENABLE_INSTANA 37 | value: "false" 38 | - name: ANOMALY_GENERATOR_URL 39 | value: "qotd-usecase.qotd-load.svc.cluster.local:3012" 40 | - name: SUPPLY_CHAIN_URL 41 | value: "" 42 | - name: SUPPLY_CHAIN_SIMULATE 43 | value: "true" 44 | livenessProbe: 45 | httpGet: 46 | path: /health 47 | port: 3006 48 | initialDelaySeconds: 60 49 | timeoutSeconds: 20 50 | periodSeconds: 60 51 | successThreshold: 1 52 | failureThreshold: 10 53 | readinessProbe: 54 | httpGet: 55 | path: /health 56 | port: 3006 57 | initialDelaySeconds: 5 58 | timeoutSeconds: 10 59 | periodSeconds: 10 60 | successThreshold: 1 61 | failureThreshold: 5 62 | resources: 63 | requests: 64 | cpu: "25m" 65 | memory: "200Mi" 66 | limits: 67 | cpu: "200m" 68 | memory: "800Mi" 69 | --- 70 | apiVersion: v1 71 | kind: Service 72 | metadata: 73 | name: qotd-engraving 74 | namespace: qotd 75 | labels: 76 | app: qotd 77 | tier: api 78 | spec: 79 | type: ClusterIP 80 | ports: 81 | - port: 3006 82 | targetPort: 3006 83 | protocol: TCP 84 | name: http 85 | selector: 86 | app: qotd-engraving -------------------------------------------------------------------------------- /demos/qotd/manifests/qotd_image.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: qotd 5 | --- 6 | kind: Deployment 7 | apiVersion: apps/v1 8 | metadata: 9 | name: qotd-image 10 | namespace: qotd 11 | labels: 12 | app: qotd 13 | tier: api 14 | spec: 15 | replicas: 2 16 | selector: 17 | matchLabels: 18 | app: qotd-image 19 | template: 20 | metadata: 21 | labels: 22 | app: qotd-image 23 | spec: 24 | restartPolicy: Always 25 | containers: 26 | - name: qotd-image 27 | image: registry.gitlab.com/quote-of-the-day/qotd-image-service/v4.0.0:latest 28 | imagePullPolicy: Always 29 | ports: 30 | - name: http 31 | containerPort: 3003 32 | protocol: TCP 33 | env: 34 | - name: LOG_LEVEL 35 | value: "INFO" 36 | - name: ENABLE_INSTANA 37 | value: "false" 38 | - name: ANOMALY_GENERATOR_URL 39 | value: "qotd-usecase.qotd-load.svc.cluster.local:3012" 40 | livenessProbe: 41 | httpGet: 42 | path: /health 43 | port: 3003 44 | initialDelaySeconds: 60 45 | timeoutSeconds: 20 46 | periodSeconds: 60 47 | successThreshold: 1 48 | failureThreshold: 10 49 | readinessProbe: 50 | httpGet: 51 | path: /health 52 | port: 3003 53 | initialDelaySeconds: 5 54 | timeoutSeconds: 10 55 | periodSeconds: 10 56 | successThreshold: 1 57 | failureThreshold: 5 58 | resources: 59 | requests: 60 | cpu: "25m" 61 | memory: "200Mi" 62 | limits: 63 | cpu: "200m" 64 | memory: "800Mi" 65 | --- 66 | apiVersion: v1 67 | kind: Service 68 | metadata: 69 | name: qotd-image 70 | namespace: qotd 71 | labels: 72 | app: qotd 73 | tier: api 74 | spec: 75 | type: ClusterIP 76 | ports: 77 | - port: 3003 78 | targetPort: 3003 79 | protocol: TCP 80 | name: http 81 | selector: 82 | app: qotd-image -------------------------------------------------------------------------------- /demos/qotd/manifests/qotd_pdf.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: qotd-svc-ocp 5 | --- 6 | kind: Deployment 7 | apiVersion: apps/v1 8 | metadata: 9 | name: qotd-pdf 10 | namespace: qotd-svc-ocp 11 | labels: 12 | app: qotd 13 | tier: api 14 | spec: 15 | replicas: 1 16 | selector: 17 | matchLabels: 18 | app: qotd-pdf 19 | template: 20 | metadata: 21 | labels: 22 | app: qotd-pdf 23 | spec: 24 | restartPolicy: Always 25 | containers: 26 | - name: qotd-pdf 27 | image: registry.gitlab.com/quote-of-the-day/qotd-pdf-service/v4.0.0:latest 28 | imagePullPolicy: Always 29 | ports: 30 | - name: http 31 | containerPort: 3005 32 | protocol: TCP 33 | env: 34 | - name: LOG_LEVEL 35 | value: "INFO" 36 | - name: ENABLE_INSTANA 37 | value: "false" 38 | - name: ANOMALY_GENERATOR_URL 39 | value: "qotd-usecase.qotd-load.svc.cluster.local:3012" 40 | - name: QUOTE_SVC 41 | value: "qotd-quote:3001" 42 | livenessProbe: 43 | httpGet: 44 | path: /health 45 | port: 3005 46 | initialDelaySeconds: 60 47 | timeoutSeconds: 20 48 | periodSeconds: 60 49 | successThreshold: 1 50 | failureThreshold: 10 51 | readinessProbe: 52 | httpGet: 53 | path: /health 54 | port: 3005 55 | initialDelaySeconds: 5 56 | timeoutSeconds: 10 57 | periodSeconds: 10 58 | successThreshold: 1 59 | failureThreshold: 5 60 | resources: 61 | requests: 62 | cpu: "25m" 63 | memory: "300Mi" 64 | limits: 65 | cpu: "200m" 66 | memory: "800Mi" 67 | # nodeSelector: 68 | # ocpnode: qotd-svc-ocp 69 | --- 70 | apiVersion: v1 71 | kind: Service 72 | metadata: 73 | name: qotd-pdf 74 | namespace: qotd-svc-ocp 75 | labels: 76 | app: qotd 77 | tier: api 78 | spec: 79 | type: ClusterIP 80 | ports: 81 | - port: 3005 82 | targetPort: 3005 83 | protocol: TCP 84 | name: http 85 | selector: 86 | app: qotd-pdf -------------------------------------------------------------------------------- /demos/qotd/manifests/qotd_quote.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: qotd-svc-iks 5 | --- 6 | apiVersion: apps/v1 7 | kind: Deployment 8 | metadata: 9 | name: qotd-quote 10 | namespace: qotd-svc-iks 11 | labels: 12 | app: qotd 13 | tier: api 14 | spec: 15 | replicas: 1 16 | selector: 17 | matchLabels: 18 | app: qotd-quote 19 | template: 20 | metadata: 21 | labels: 22 | app: qotd-quote 23 | spec: 24 | restartPolicy: Always 25 | containers: 26 | - name: qotd-quote 27 | image: registry.gitlab.com/quote-of-the-day/quote-service/v4.0.0:latest 28 | imagePullPolicy: Always 29 | ports: 30 | - name: http 31 | containerPort: 3001 32 | protocol: TCP 33 | env: 34 | - name: LOG_LEVEL 35 | value: "INFO" 36 | - name: ENABLE_INSTANA 37 | value: "false" 38 | - name: ANOMALY_GENERATOR_URL 39 | value: "qotd-usecase.qotd-load.svc.cluster.local:3012" 40 | - name: DB_HOST 41 | value: "qotd-db.qotd-svc-iks.svc.cluster.local" 42 | - name: DB_USER 43 | value: "root" 44 | - name: DB_PASS 45 | value: "root" 46 | - name: DB_NAME 47 | value: "qotd" 48 | livenessProbe: 49 | httpGet: 50 | path: /health 51 | port: 3001 52 | initialDelaySeconds: 60 53 | timeoutSeconds: 20 54 | periodSeconds: 60 55 | successThreshold: 1 56 | failureThreshold: 10 57 | readinessProbe: 58 | httpGet: 59 | path: /health 60 | port: 3001 61 | initialDelaySeconds: 5 62 | timeoutSeconds: 10 63 | periodSeconds: 10 64 | successThreshold: 1 65 | failureThreshold: 5 66 | resources: 67 | requests: 68 | cpu: "25m" 69 | memory: "200Mi" 70 | limits: 71 | cpu: "200m" 72 | memory: "800Mi" 73 | --- 74 | apiVersion: v1 75 | kind: Service 76 | metadata: 77 | name: qotd-quote 78 | namespace: qotd-svc-iks 79 | labels: 80 | app: qotd 81 | tier: api 82 | spec: 83 | type: ClusterIP 84 | ports: 85 | - port: 3001 86 | targetPort: 3001 87 | protocol: TCP 88 | name: http 89 | selector: 90 | app: qotd-quote -------------------------------------------------------------------------------- /demos/qotd/manifests/qotd_rating.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: qotd-svc-ocp 5 | --- 6 | kind: Deployment 7 | apiVersion: apps/v1 8 | metadata: 9 | name: qotd-rating 10 | namespace: qotd-svc-ocp 11 | labels: 12 | app: qotd 13 | tier: api 14 | spec: 15 | replicas: 1 16 | selector: 17 | matchLabels: 18 | app: qotd-rating 19 | template: 20 | metadata: 21 | labels: 22 | app: qotd-rating 23 | spec: 24 | restartPolicy: Always 25 | containers: 26 | - name: qotd-rating 27 | image: registry.gitlab.com/quote-of-the-day/qotd-ratings-service/v4.0.0:latest 28 | imagePullPolicy: Always 29 | ports: 30 | - name: http 31 | containerPort: 3004 32 | protocol: TCP 33 | env: 34 | - name: LOG_LEVEL 35 | value: "INFO" 36 | - name: ENABLE_INSTANA 37 | value: "false" 38 | - name: ANOMALY_GENERATOR_URL 39 | value: "qotd-usecase.qotd-load.svc.cluster.local:3012" 40 | livenessProbe: 41 | httpGet: 42 | path: /health 43 | port: 3004 44 | initialDelaySeconds: 60 45 | timeoutSeconds: 20 46 | periodSeconds: 60 47 | successThreshold: 1 48 | failureThreshold: 10 49 | readinessProbe: 50 | httpGet: 51 | path: /health 52 | port: 3004 53 | initialDelaySeconds: 5 54 | timeoutSeconds: 10 55 | periodSeconds: 10 56 | successThreshold: 1 57 | failureThreshold: 5 58 | resources: 59 | requests: 60 | cpu: "25m" 61 | memory: "200Mi" 62 | limits: 63 | cpu: "200m" 64 | memory: "800Mi" 65 | # nodeSelector: 66 | # ocpnode: qotd-svc-ocp 67 | --- 68 | apiVersion: v1 69 | kind: Service 70 | metadata: 71 | name: qotd-rating 72 | namespace: qotd-svc-ocp 73 | labels: 74 | app: qotd 75 | tier: api 76 | spec: 77 | type: ClusterIP 78 | ports: 79 | - port: 3004 80 | targetPort: 3004 81 | protocol: TCP 82 | name: http 83 | selector: 84 | app: qotd-rating -------------------------------------------------------------------------------- /demos/speedtest/testdata/manifests/firefox.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: firefox 5 | labels: 6 | app: firefox 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: firefox 12 | template: 13 | metadata: 14 | labels: 15 | app: firefox 16 | spec: 17 | containers: 18 | - name: firefox 19 | image: jlesage/firefox 20 | imagePullPolicy: IfNotPresent 21 | ports: 22 | - containerPort: 5800 23 | # volumeMounts: 24 | # - name: config 25 | # mountPath: /config 26 | # volumes: 27 | # - name: config 28 | # hostPath: 29 | # path: /docker/appdata/firefox 30 | # type: DirectoryOrCreate -------------------------------------------------------------------------------- /demos/speedtest/testdata/manifests/firefox2.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: firefox2 5 | labels: 6 | app: firefox2 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: firefox2 12 | template: 13 | metadata: 14 | labels: 15 | app: firefox2 16 | spec: 17 | containers: 18 | - name: firefox2 19 | image: jlesage/firefox 20 | imagePullPolicy: IfNotPresent 21 | ports: 22 | - containerPort: 5800 23 | # volumeMounts: 24 | # - name: config 25 | # mountPath: /config 26 | # volumes: 27 | # - name: config 28 | # hostPath: 29 | # path: /docker/appdata/firefox2 30 | # type: DirectoryOrCreate -------------------------------------------------------------------------------- /demos/speedtest/testdata/manifests/speedtest.yaml: -------------------------------------------------------------------------------- 1 | ################################################################ 2 | #Name: openspeedtest 3 | #Desc: YAML file for creating openspeedtest server for testing. 4 | ################################################################ 5 | apiVersion: apps/v1 6 | kind: Deployment 7 | metadata: 8 | name: openspeedtest 9 | labels: 10 | app: openspeedtest 11 | spec: 12 | replicas: 1 13 | selector: 14 | matchLabels: 15 | app: openspeedtest 16 | template: 17 | metadata: 18 | labels: 19 | app: openspeedtest 20 | spec: 21 | containers: 22 | - name: speedtest 23 | image: openspeedtest/latest 24 | imagePullPolicy: IfNotPresent 25 | ports: 26 | - containerPort: 3000 27 | - containerPort: 3001 28 | resources: 29 | requests: 30 | memory: "64Mi" 31 | cpu: "50m" 32 | limits: 33 | memory: "128Mi" 34 | cpu: "100m" 35 | livenessProbe: 36 | tcpSocket: 37 | port: 3000 38 | initialDelaySeconds: 15 39 | periodSeconds: 10 40 | timeoutSeconds: 5 41 | failureThreshold: 3 42 | readinessProbe: 43 | tcpSocket: 44 | port: 3000 45 | initialDelaySeconds: 15 46 | periodSeconds: 10 47 | timeoutSeconds: 5 48 | failureThreshold: 3 49 | --- 50 | apiVersion: v1 51 | kind: Service 52 | metadata: 53 | name: openspeedtest 54 | labels: 55 | app: openspeedtest 56 | service: openspeedtest 57 | spec: 58 | ports: 59 | - port: 3000 60 | targetPort: 3000 61 | name: http 62 | selector: 63 | app: openspeedtest 64 | -------------------------------------------------------------------------------- /demos/speedtest/testdata/policy/denyFromGw.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink.net/v1alpha1 2 | kind: AccessPolicy 3 | metadata: 4 | name: deny-from-gw 5 | namespace: default 6 | spec: 7 | action: deny 8 | from: [{ 9 | workloadSelector: { 10 | matchLabels: { 11 | peer.clusterlink.net/name: peer3 12 | } 13 | } 14 | } 15 | ] 16 | to: [{ 17 | workloadSelector: {} 18 | } 19 | ] 20 | 21 | -------------------------------------------------------------------------------- /demos/speedtest/testdata/policy/denyToSpeedtest.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink.net/v1alpha1 2 | kind: AccessPolicy 3 | metadata: 4 | name: deny-to-speedtest 5 | namespace: default 6 | spec: 7 | action: deny 8 | from: [{ 9 | workloadSelector: { 10 | matchLabels: { 11 | client.clusterlink.net/labels.app: firefox 12 | } 13 | } 14 | } 15 | ] 16 | to: [{ 17 | workloadSelector: { 18 | matchLabels: { 19 | export.clusterlink.net/name: openspeedtest 20 | } 21 | } 22 | }] 23 | 24 | 25 | -------------------------------------------------------------------------------- /demos/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) The ClusterLink Authors. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | -------------------------------------------------------------------------------- /demos/utils/common.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) The ClusterLink Authors. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | import os 15 | import shutil 16 | import subprocess as sp 17 | from colorama import Fore 18 | from colorama import Style 19 | ProjDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 20 | 21 | # Log Functions 22 | # runcmd runs os system command. 23 | def runcmd(cmd): 24 | print(f'{Fore.YELLOW}{cmd} {Style.RESET_ALL}') 25 | os.system(cmd) 26 | 27 | # runcmdDir runs os system command in specific directory. 28 | def runcmdDir(cmd,dir): 29 | print(f'{Fore.YELLOW}{cmd} {Style.RESET_ALL}') 30 | sp.run(cmd, shell=True, cwd=dir, check=True) 31 | 32 | # runcmdb runs os system command in the background. 33 | def runcmdb(cmd): 34 | print(f'{Fore.YELLOW}{cmd} {Style.RESET_ALL}') 35 | os.system(cmd + ' &') 36 | 37 | # printHeader runs os system command in the background. 38 | def printHeader(msg): 39 | print(f'{Fore.GREEN}{msg} {Style.RESET_ALL}') 40 | 41 | # createFolder creates folder. 42 | def createFolder(name): 43 | if os.path.exists(name): 44 | shutil.rmtree(name) 45 | os.makedirs(name) 46 | 47 | # app cluster contains the application service information. 48 | class app: 49 | def __init__(self, name, namespace, host, port): 50 | self.name = name 51 | self.namespace = namespace 52 | self.host = host 53 | self.port = port -------------------------------------------------------------------------------- /demos/utils/manifests/kind/calico/calico-config.yaml: -------------------------------------------------------------------------------- 1 | ################################################################ 2 | #Name:config 3 | #Desc: Kind config for exposing port 30000 of kind 4 | # cluster 5 | ################################################################ 6 | # cluster-config.yml 7 | kind: Cluster 8 | apiVersion: kind.x-k8s.io/v1alpha4 9 | nodes: 10 | 11 | networking: 12 | disableDefaultCNI: true # disable kindnet 13 | podSubnet: 192.168.0.0/16 # set to Calico's default subnet -------------------------------------------------------------------------------- /demos/utils/manifests/kind/cl-svc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: cl-svc 5 | spec: 6 | type: NodePort 7 | selector: 8 | app: cl-dataplane 9 | ports: 10 | - port: 443 11 | targetPort: 443 12 | nodePort: 30443 13 | protocol: TCP 14 | name: http 15 | -------------------------------------------------------------------------------- /demos/utils/manifests/kind/flannel/create_cni_bridge.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) The ClusterLink Authors. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | import os 15 | import shutil 16 | 17 | projDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))) 18 | pluginsFol=f'{projDir}/bin/plugins/' 19 | 20 | #Download plugins bridge for flannel 21 | def createCniBridge(): 22 | if not os.path.exists(f"{pluginsFol}/bin/bridge"): 23 | print("Start building plugins for flannel") 24 | os.makedirs(pluginsFol, exist_ok=True) 25 | os.chdir(pluginsFol) 26 | os.system('git clone https://github.com/containernetworking/plugins.git') 27 | os.chdir(f'{pluginsFol}/plugins') 28 | os.system(f'./build_linux.sh') 29 | os.chdir(projDir) 30 | shutil.copytree(f'{projDir}/bin/plugins/plugins/bin', f'{projDir}/bin/plugins/bin') 31 | shutil.rmtree(f'{pluginsFol}/plugins') 32 | os.chdir(projDir) 33 | else: 34 | print(f"file {projDir}/bin/plugins/bridge exist") 35 | 36 | #Create kind config file with plugins for flannel 37 | def createKindCfgForflunnel(): 38 | cfgFile=f'{pluginsFol}/flannel-config.yaml' 39 | if not os.path.exists(cfgFile): 40 | with open(f"{projDir}/demos/utils/manifests/kind/flannel/flannel-config.yaml", 'r') as file: 41 | lines = file.readlines() 42 | 43 | # replace the line you want to modify (line 3 in this example) 44 | lines[12] = f' - hostPath: {projDir}/bin/plugins/bin \n' 45 | 46 | with open(cfgFile, 'w') as file: 47 | file.writelines(lines) 48 | 49 | if __name__ == "__main__": 50 | createCniBridge() 51 | createKindCfgForflunnel() -------------------------------------------------------------------------------- /demos/utils/manifests/kind/flannel/flannel-config.yaml: -------------------------------------------------------------------------------- 1 | ################################################################ 2 | #Name:config 3 | #Desc: Kind config for exposing port 30000 of kind 4 | # cluster 5 | ################################################################ 6 | # cluster-config.yml 7 | kind: Cluster 8 | apiVersion: kind.x-k8s.io/v1alpha4 9 | nodes: 10 | 11 | - role: control-plane 12 | extraMounts: 13 | - hostPath: /bin/plugins/bin 14 | containerPath: /opt/cni/bin 15 | 16 | networking: 17 | # the default CNI will not be installed 18 | disableDefaultCNI: true -------------------------------------------------------------------------------- /design-proposals/deployment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/design-proposals/deployment.png -------------------------------------------------------------------------------- /docs/ClusteLink.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/docs/ClusteLink.pdf -------------------------------------------------------------------------------- /docs/Policy.md: -------------------------------------------------------------------------------- 1 | 2 | ## This Document lays out the design of policy engine of ClusterLink 3 | 4 | 5 | 6 | 7 | The ClusterLink's control plane and data plane have hookpoints to publish events to another component called event manager. A hookpoint is a point in code where *a new connection request has arrived* or *a service listing request arrives*. The event manager receives an event-type, and standard attributes which are associated with the event-type. The event manager then publishes the event to Policy Dispatcher which maintains a list of policy agents (ACL, LB, Limiter, Monitor, etc) which have subscribed to a policy with a certain priority in which they need to executed. For example, *a new connection request* event could be subscribed by ACL, LB and Monitor with a priority of 0, 1 and 2 which means ACL will be the first policy agent and Monitor the last. This also means that Policy Dispatcher maintains the states and handles the stitching of multiple policy agents for an event and handles the movement of shared states between the policy agents. 8 | 9 | Policy engine is kept separate to allow extensibility. We could consider dynamically loadable policy agents in javascript web assembly (WASM). However, initially we will start with a set of well known policy agents manually written in golang. The Operator should be able to send commands to the policy dispatcher to program it (add policy, remove policy, subscribe policy event, etc) 10 | 11 | ### Events + Attributes + Policy Agents Interested + Responses 12 | 13 | | Event |Attributes |Policy Agents |Response | 14 | |----------------|-------------------------------|-----------------------------|----------| 15 | |New connection|Src Service, Dst Service, Direction, *Optional Src Peer* |ACL, LB, Connection Limiter, Rate Limiter, Monitor | Allow/Deny, Target Peer, Optional Rate-limit | 16 | |Expose Service | Service ID | ACL | Target Peers| 17 | |Service List Request | Src Peer |ACL|Services| 18 | 19 | More such events should be added and their attributes clearly attributed, This is just a first step towards that. 20 | -------------------------------------------------------------------------------- /docs/figures/BookInfo_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/docs/figures/BookInfo_demo.png -------------------------------------------------------------------------------- /docs/figures/clusterlink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/docs/figures/clusterlink.png -------------------------------------------------------------------------------- /docs/mbg-proto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/docs/mbg-proto.png -------------------------------------------------------------------------------- /docs/openspeedtest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/docs/openspeedtest.png -------------------------------------------------------------------------------- /docs/policy-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/docs/policy-arch.png -------------------------------------------------------------------------------- /examples/policies/allowAll.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "AccessPolicy", 3 | "apiVersion": "clusterlink.net/v1alpha1", 4 | "metadata": { 5 | "name": "allow-all", 6 | "namespace": "default" 7 | }, 8 | "spec": { 9 | "action": "allow", 10 | "from": [ 11 | { 12 | "workloadSelector": {} 13 | } 14 | ], 15 | "to": [ 16 | { 17 | "workloadSelector": {} 18 | } 19 | ] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/policies/allowToReviews.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "AccessPolicy", 3 | "apiVersion": "clusterlink.net/v1alpha1", 4 | "metadata": { 5 | "name": "allow-to-reviews", 6 | "namespace": "default" 7 | }, 8 | "spec": { 9 | "action": "allow", 10 | "from": [ 11 | { 12 | "workloadSelector": {} 13 | } 14 | ], 15 | "to": [ 16 | { 17 | "workloadSelector": { 18 | "matchLabels": { 19 | "export.clusterlink.net/name": "reviews" 20 | } 21 | } 22 | } 23 | ] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/policies/deny_from_gw1.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "AccessPolicy", 3 | "apiVersion": "clusterlink.net/v1alpha1", 4 | "metadata": { 5 | "name": "deny-from-gw1", 6 | "namespace": "default" 7 | }, 8 | "spec": { 9 | "action": "deny", 10 | "from": [ 11 | { 12 | "workloadSelector": { 13 | "matchLabels": { 14 | "peer.clusterlink.net/name": "gw1" 15 | } 16 | } 17 | } 18 | ], 19 | "to": [ 20 | { 21 | "workloadSelector": {} 22 | } 23 | ] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /hack/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | # Netlify expects the configuration file in the repo's top level. 2 | # In our case, we use it only to redirect to the `website` directory which 3 | # holds the site and configuration. 4 | # See https://docs.netlify.com/configure-builds/file-based-configuration/ 5 | [build] 6 | base = "website/" 7 | publish = "public" 8 | -------------------------------------------------------------------------------- /pkg/apis/clusterlink.net/v1alpha1/accesspolicy_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package v1alpha1_test 15 | 16 | import ( 17 | "testing" 18 | 19 | "github.com/stretchr/testify/require" 20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | 22 | "github.com/clusterlink-net/clusterlink/pkg/apis/clusterlink.net/v1alpha1" 23 | ) 24 | 25 | var ( 26 | trivialLabel = map[string]string{"key": "val"} 27 | trivialSelector = metav1.LabelSelector{MatchLabels: trivialLabel} 28 | trivialWorkloadSet = v1alpha1.WorkloadSetOrSelector{WorkloadSelector: &trivialSelector} 29 | ) 30 | 31 | func TestValidation(t *testing.T) { 32 | badPolicy := v1alpha1.AccessPolicy{} 33 | err := badPolicy.Spec.Validate() 34 | require.NotNil(t, err) // action is an empty string 35 | 36 | badPolicy.Spec.Action = "notAnAction" 37 | err = badPolicy.Spec.Validate() 38 | require.NotNil(t, err) // action is not a legal action 39 | 40 | badPolicy.Spec.Action = "deny" 41 | err = badPolicy.Spec.Validate() 42 | require.NotNil(t, err) // missing from 43 | 44 | badPolicy.Spec.From = []v1alpha1.WorkloadSetOrSelector{trivialWorkloadSet} 45 | err = badPolicy.Spec.Validate() 46 | require.NotNil(t, err) // missing to 47 | 48 | badPolicy.Spec.To = []v1alpha1.WorkloadSetOrSelector{trivialWorkloadSet} 49 | err = badPolicy.Spec.Validate() 50 | require.Nil(t, err) 51 | } 52 | -------------------------------------------------------------------------------- /pkg/apis/clusterlink.net/v1alpha1/export.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package v1alpha1 15 | 16 | import ( 17 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 18 | ) 19 | 20 | // +kubebuilder:object:root=true 21 | // +kubebuilder:subresource:status 22 | 23 | // Export defines a service being exported by the local Peer for use by others. 24 | // Only explicitly exported services can be accessed remotely. 25 | type Export struct { 26 | metav1.TypeMeta `json:",inline"` 27 | metav1.ObjectMeta `json:"metadata,omitempty"` 28 | 29 | // Spec represents the attributes of the exported service. 30 | Spec ExportSpec `json:"spec,omitempty"` 31 | // Status represents the export status. 32 | Status ExportStatus `json:"status,omitempty"` 33 | } 34 | 35 | // ExportSpec contains all attributes of an exported service. 36 | type ExportSpec struct { 37 | // Host of the exported service. 38 | // If empty, export will point to a service with the same 39 | // name and namespace as the export object. 40 | Host string `json:"host,omitempty"` 41 | // Port of the exported service. 42 | Port uint16 `json:"port,omitempty"` 43 | } 44 | 45 | const ( 46 | // ExportValid is a condition type for indicating whether the export is valid. 47 | ExportValid string = "ExportValid" 48 | ) 49 | 50 | // ExportStatus represents the status of an exported service. 51 | type ExportStatus struct { 52 | // Conditions of the export. 53 | Conditions []metav1.Condition `json:"conditions,omitempty"` 54 | } 55 | 56 | // +kubebuilder:object:root=true 57 | 58 | // ExportList is a list of export objects. 59 | type ExportList struct { 60 | metav1.TypeMeta `json:",inline"` 61 | metav1.ListMeta `json:"metadata,omitempty"` 62 | 63 | // Items is the list of export objects. 64 | Items []Export `json:"items"` 65 | } 66 | 67 | func init() { 68 | SchemeBuilder.Register(&Export{}, &ExportList{}) 69 | } 70 | -------------------------------------------------------------------------------- /pkg/apis/clusterlink.net/v1alpha1/groupversion_info.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Package v1alpha1 contains definitions of a ClusterLink deployment and installation related objects. 15 | // +kubebuilder:object:generate=true 16 | // +groupName=clusterlink.net 17 | package v1alpha1 18 | 19 | import ( 20 | "k8s.io/apimachinery/pkg/runtime/schema" 21 | "sigs.k8s.io/controller-runtime/pkg/scheme" 22 | ) 23 | 24 | var ( 25 | // GroupVersion is group version used to register these objects. 26 | GroupVersion = schema.GroupVersion{Group: "clusterlink.net", Version: "v1alpha1"} 27 | 28 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme. 29 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 30 | 31 | // AddToScheme adds the types in this group-version to the given scheme. 32 | AddToScheme = SchemeBuilder.AddToScheme 33 | ) 34 | -------------------------------------------------------------------------------- /pkg/apis/clusterlink.net/v1alpha1/peer.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package v1alpha1 15 | 16 | import ( 17 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 18 | ) 19 | 20 | // +kubebuilder:object:root=true 21 | // +kubebuilder:subresource:status 22 | 23 | // Peer represents a location (or site) that can be used to import services from. 24 | type Peer struct { 25 | metav1.TypeMeta `json:",inline"` 26 | metav1.ObjectMeta `json:"metadata,omitempty"` 27 | 28 | // Spec represents the peer attributes. 29 | Spec PeerSpec `json:"spec"` 30 | // Status represents the peer status. 31 | Status PeerStatus `json:"status,omitempty"` 32 | } 33 | 34 | // Endpoint represents a network endpoint (i.e., host or IP and a port). 35 | type Endpoint struct { 36 | // Host or IP address of the endpoint. 37 | Host string `json:"host"` 38 | // Port of the endpoint. 39 | Port uint16 `json:"port"` 40 | } 41 | 42 | // PeerSpec contains all peer attributes. 43 | type PeerSpec struct { 44 | // Gateways serving the Peer. 45 | Gateways []Endpoint `json:"gateways"` 46 | } 47 | 48 | const ( 49 | // PeerReachable is a condition type for indicating whether a peer is reachable (heartbeat responding). 50 | PeerReachable string = "PeerReachable" 51 | ) 52 | 53 | // PeerStatus represents the status of a peer. 54 | type PeerStatus struct { 55 | // Conditions of the peer. 56 | Conditions []metav1.Condition `json:"conditions,omitempty"` 57 | // Labels holds peer labels, as reported by the remote peer 58 | Labels map[string]string `json:"labels,omitempty"` 59 | } 60 | 61 | // +kubebuilder:object:root=true 62 | 63 | // PeerList is a list of peer objects. 64 | type PeerList struct { 65 | metav1.TypeMeta `json:",inline"` 66 | metav1.ListMeta `json:"metadata,omitempty"` 67 | 68 | // Items is the list of peer objects. 69 | Items []Peer `json:"items"` 70 | } 71 | 72 | func init() { 73 | SchemeBuilder.Register(&Peer{}, &PeerList{}) 74 | } 75 | -------------------------------------------------------------------------------- /pkg/controlplane/api/const.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package api 15 | 16 | const ( 17 | // ListenPort is the port used by the dataplane to access the controlplane. 18 | ListenPort = 4444 19 | // ReadinessListenPort is the port used to probe for controlplane readiness. 20 | ReadinessListenPort = 4445 21 | // Name is the controlplane name. 22 | Name = "cl-controlplane" 23 | ) 24 | -------------------------------------------------------------------------------- /pkg/controlplane/api/heartbeat.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package api 15 | 16 | const ( 17 | // HeartbeatPath is the path for Heartbeat requests from remote peers. 18 | HeartbeatPath = "/healthz" 19 | // PeerLabelsCustomHeader is a custom HTTP response header for reporting peer labels. 20 | PeerLabelsCustomHeader = "ClusterLinkPeerLabels" 21 | ) 22 | -------------------------------------------------------------------------------- /pkg/controlplane/api/xds.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package api 15 | 16 | const ( 17 | // cluster names. 18 | 19 | // ControlplaneCluster is the cluster name of the controlplane gRPC server. 20 | ControlplaneCluster = "controlplane" 21 | // EgressRouterCluster is the cluster name of the internal egress router. 22 | EgressRouterCluster = "egress-router" 23 | // ExportClusterPrefix is the prefix of clusters representing exported services. 24 | ExportClusterPrefix = "export-" 25 | // RemotePeerClusterPrefix is the prefix of clusters representing remote peers. 26 | RemotePeerClusterPrefix = "remote-peer-" 27 | 28 | // listener names. 29 | 30 | // EgressRouterListener is the listener name of the internal egress router. 31 | EgressRouterListener = "egress-router" 32 | // ImportListenerPrefix is the prefix of listeners representing imported services. 33 | ImportListenerPrefix = "import-" 34 | // IngressRouterListener is the listener name of the ingress router. 35 | IngressRouterListener = "ingress-router" 36 | 37 | // secret names. 38 | 39 | // ValidationSecret is the secret name of the dataplane certificate validation context 40 | // (which includes the CA certificate). 41 | ValidationSecret = "validation" 42 | // CertificateSecret is the secret name of the dataplane certificate. 43 | CertificateSecret = "certificate" 44 | ) 45 | 46 | // ExportClusterName returns the cluster name of an exported service. 47 | func ExportClusterName(name, namespace string) string { 48 | return ExportClusterPrefix + namespace + "/" + name 49 | } 50 | 51 | // RemotePeerClusterName returns the cluster name of a remote peer. 52 | func RemotePeerClusterName(name string) string { 53 | return RemotePeerClusterPrefix + name 54 | } 55 | 56 | // ImportListenerName returns the listener name of an imported service. 57 | func ImportListenerName(name, namespace string) string { 58 | return ImportListenerPrefix + namespace + "/" + name 59 | } 60 | -------------------------------------------------------------------------------- /pkg/controlplane/authz/connectivitypdp/accesspolicy.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package connectivitypdp 15 | 16 | import ( 17 | "github.com/clusterlink-net/clusterlink/pkg/apis/clusterlink.net/v1alpha1" 18 | "k8s.io/apimachinery/pkg/types" 19 | ) 20 | 21 | // AccessPolicy is an opaque, PDP-internal, generalized representation of AccessPolicy and PrivilegedAccessPolicy CRDs. 22 | type AccessPolicy struct { 23 | name types.NamespacedName 24 | privileged bool 25 | spec v1alpha1.AccessPolicySpec 26 | } 27 | 28 | // PolicyFromCR converts the AccessPolicy Custom Resource into the PDP's AccessPolicy. 29 | func PolicyFromCR(vap *v1alpha1.AccessPolicy) *AccessPolicy { 30 | return &AccessPolicy{ 31 | name: types.NamespacedName{Namespace: vap.Namespace, Name: vap.Name}, 32 | privileged: false, 33 | spec: vap.Spec, 34 | } 35 | } 36 | 37 | // PolicyFromPrivilegedCR converts the PrivilegedAccessPolicy Custom Resource into the PDP's AccessPolicy. 38 | func PolicyFromPrivilegedCR(vap *v1alpha1.PrivilegedAccessPolicy) *AccessPolicy { 39 | return &AccessPolicy{ 40 | name: types.NamespacedName{Name: vap.Name}, 41 | privileged: true, 42 | spec: vap.Spec, 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /pkg/controlplane/authz/connectivitypdp/test_data/all_layers.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink.net/v1alpha1 2 | kind: PrivilegedAccessPolicy 3 | metadata: 4 | name: deny-private-workloads-connecting-to-metering-service-on-port-5051-on-prod 5 | spec: 6 | action: deny 7 | from: 8 | - workloadSelector: 9 | matchLabels: 10 | classification: private 11 | environment: prod 12 | to: 13 | - workloadSelector: 14 | matchLabels: 15 | workloadName: global-metering-service 16 | environment: prod 17 | --- 18 | apiVersion: clusterlink.net/v1alpha1 19 | kind: PrivilegedAccessPolicy 20 | metadata: 21 | name: allow-connecting-to-metering-service-on-port-5051 22 | spec: 23 | action: allow 24 | from: 25 | - workloadSelector: {} 26 | to: 27 | - workloadSelector: 28 | matchLabels: 29 | workloadName: global-metering-service 30 | environment: prod 31 | --- 32 | apiVersion: clusterlink.net/v1alpha1 33 | kind: AccessPolicy 34 | metadata: 35 | name: deny-connecting-to-metering-service-on-some-ports 36 | namespace: default 37 | spec: 38 | action: deny 39 | from: 40 | - workloadSelector: {} 41 | to: 42 | - workloadSelector: 43 | matchLabels: 44 | workloadName: global-metering-service 45 | --- 46 | apiVersion: clusterlink.net/v1alpha1 47 | kind: AccessPolicy 48 | metadata: 49 | name: allow-connecting-to-named_workloads 50 | namespace: default 51 | spec: 52 | action: allow 53 | from: 54 | - workloadSelector: {} 55 | to: 56 | - workloadSelector: 57 | matchExpressions: 58 | - key: workloadName 59 | operator: Exists 60 | -------------------------------------------------------------------------------- /pkg/controlplane/authz/connectivitypdp/test_data/mixed_policies_and_other_resources.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink/v1alpha1 2 | kind: PrivilegedConnectivityPolicy # Looks like a privileged connectivity policy, but it is not 3 | metadata: 4 | name: policy-with-bad-kay 5 | spec: 6 | badKey: deny 7 | --- 8 | apiVersion: clusterlink/v1alpha1 9 | kind: ConnectivityPolicy # Looks like a connectivity policy, but it is not 10 | metadata: 11 | name: policy-without-from 12 | spec: 13 | action: deny 14 | --- 15 | apiVersion: clusterlink/v1alpha1 16 | kind: PrivilegedConnectivityPolicy 17 | metadata: 18 | name: policy-with-bad-action 19 | spec: 20 | action: den 21 | from: 22 | - workloadSelector: 23 | matchLabels: {} 24 | to: 25 | - workloadSelector: 26 | matchLabels: 27 | workloadName: global-metering-service 28 | --- 29 | apiVersion: clusterlink/v1alpha1 30 | kind: PrivilegedConnectivityPolicy 31 | metadata: 32 | name: deny-connecting-to-metering-service-on-port-5051 33 | spec: 34 | action: deny 35 | from: 36 | - workloadSelector: 37 | labelSelector: {} 38 | to: 39 | - workloadSelector: 40 | matchLabels: 41 | workloadName: global-metering-service 42 | connectionAttrs: 43 | - protocol: TCP 44 | port: 5051 45 | --- 46 | apiVersion: clusterlink/v1alpha1 47 | kind: NotAPolicy 48 | metadata: 49 | name: allow-connecting-to-metering-service 50 | spec: 51 | action: allow 52 | from: 53 | - workloadSelector: 54 | labelSelector: {} 55 | to: 56 | - workloadSelector: 57 | matchLabels: 58 | workloadName: global-metering-service 59 | --- 60 | badkey: 1 # Doesn't even have a kind 61 | anotherBadKey: "sdf" 62 | --- 63 | # - blabla # not even a map 64 | --- 65 | apiVersion: clusterlink/v1alpha1 66 | kind: ConnectivityPolicy 67 | metadata: 68 | name: allow-connecting-to-metering-service 69 | spec: 70 | action: allow 71 | from: 72 | - workloadSelector: 73 | labelSelector: {} 74 | to: 75 | - workloadSelector: 76 | matchLabels: 77 | workloadName: global-metering-service 78 | -------------------------------------------------------------------------------- /pkg/controlplane/authz/connectivitypdp/test_data/not_a_yaml: -------------------------------------------------------------------------------- 1 | just: for: testing: bad: path -------------------------------------------------------------------------------- /pkg/controlplane/authz/connectivitypdp/test_data/privileged_and_regular.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink/v1alpha1 2 | kind: PrivilegedConnectivityPolicy 3 | metadata: 4 | name: deny-connecting-to-metering-service-on-port-5051 5 | spec: 6 | action: deny 7 | from: 8 | - workloadSelector: {} 9 | to: 10 | - workloadSelector: 11 | matchLabels: 12 | workloadName: global-metering-service 13 | connectionAttrs: 14 | - protocol: TCP 15 | port: 5051 16 | --- 17 | apiVersion: clusterlink/v1alpha1 18 | kind: ConnectivityPolicy 19 | metadata: 20 | name: allow-connecting-to-metering-service 21 | spec: 22 | action: allow 23 | from: 24 | - workloadSelector: {} 25 | to: 26 | - workloadSelector: 27 | matchLabels: 28 | workloadName: global-metering-service 29 | -------------------------------------------------------------------------------- /pkg/controlplane/authz/connectivitypdp/test_data/simple_privileged.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink/v1alpha1 2 | kind: PrivilegedConnectivityPolicy 3 | metadata: 4 | name: allow-connecting-to-metering-service 5 | spec: 6 | action: allow 7 | from: 8 | - workloadSelector: {} 9 | to: 10 | - workloadSelector: 11 | matchLabels: 12 | workloadName: global-metering-service 13 | connectionAttrs: 14 | - protocol: TCP 15 | port: 5051 16 | -------------------------------------------------------------------------------- /pkg/controlplane/xds/controllers.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package xds 15 | 16 | import ( 17 | "context" 18 | 19 | "k8s.io/apimachinery/pkg/types" 20 | ctrl "sigs.k8s.io/controller-runtime" 21 | 22 | "github.com/clusterlink-net/clusterlink/pkg/apis/clusterlink.net/v1alpha1" 23 | "github.com/clusterlink-net/clusterlink/pkg/util/controller" 24 | ) 25 | 26 | // CreateControllers creates the various k8s controllers used to update the xDS manager. 27 | func CreateControllers(mgr *Manager, controllerManager ctrl.Manager) error { 28 | err := controller.AddToManager(controllerManager, &controller.Spec{ 29 | Name: "xds.export", 30 | Object: &v1alpha1.Export{}, 31 | AddHandler: func(ctx context.Context, object any) error { 32 | return mgr.AddExport(object.(*v1alpha1.Export)) 33 | }, 34 | DeleteHandler: func(ctx context.Context, name types.NamespacedName) error { 35 | return mgr.DeleteExport(name) 36 | }, 37 | }) 38 | if err != nil { 39 | return err 40 | } 41 | 42 | err = controller.AddToManager(controllerManager, &controller.Spec{ 43 | Name: "xds.peer", 44 | Object: &v1alpha1.Peer{}, 45 | AddHandler: func(ctx context.Context, object any) error { 46 | return mgr.AddPeer(object.(*v1alpha1.Peer)) 47 | }, 48 | DeleteHandler: func(ctx context.Context, name types.NamespacedName) error { 49 | return mgr.DeletePeer(name.Name) 50 | }, 51 | }) 52 | if err != nil { 53 | return err 54 | } 55 | 56 | return controller.AddToManager(controllerManager, &controller.Spec{ 57 | Name: "xds.import", 58 | Object: &v1alpha1.Import{}, 59 | AddHandler: func(ctx context.Context, object any) error { 60 | return mgr.AddImport(object.(*v1alpha1.Import)) 61 | }, 62 | DeleteHandler: func(ctx context.Context, name types.NamespacedName) error { 63 | return mgr.DeleteImport(name) 64 | }, 65 | }) 66 | } 67 | -------------------------------------------------------------------------------- /pkg/controlplane/xds/server.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package xds 15 | 16 | import ( 17 | "context" 18 | 19 | discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" 20 | "github.com/envoyproxy/go-control-plane/pkg/cache/v3" 21 | "github.com/envoyproxy/go-control-plane/pkg/resource/v3" 22 | "github.com/envoyproxy/go-control-plane/pkg/server/v3" 23 | "google.golang.org/grpc" 24 | ) 25 | 26 | // RegisterService registers an xDS service backed by Manager to the given gRPC server. 27 | func RegisterService(ctx context.Context, manager *Manager, grpcServer *grpc.Server) { 28 | // create a combined mux cache of listeners, clusters and secrets 29 | muxCache := &cache.MuxCache{ 30 | Classify: func(req *cache.Request) string { 31 | return req.TypeUrl 32 | }, 33 | ClassifyDelta: func(req *cache.DeltaRequest) string { 34 | return req.TypeUrl 35 | }, 36 | Caches: map[string]cache.Cache{ 37 | resource.ClusterType: manager.clusters, 38 | resource.ListenerType: manager.listeners, 39 | resource.SecretType: manager.secrets, 40 | }, 41 | } 42 | 43 | srv := server.NewServer(ctx, muxCache, nil) 44 | discovery.RegisterAggregatedDiscoveryServiceServer(grpcServer, srv) 45 | } 46 | -------------------------------------------------------------------------------- /pkg/dataplane/api/const.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package api 15 | 16 | const ( 17 | // ListenPort is the dataplane external listening port. 18 | ListenPort = 4443 19 | // Name is the dataplane name. 20 | Name = "cl-dataplane" 21 | // Name of the go-dataplane image. 22 | GoDataplaneName = "cl-go-dataplane" 23 | // ReadinessListenPort is the port used to probe for dataplane readiness. 24 | ReadinessListenPort = 4445 25 | ) 26 | -------------------------------------------------------------------------------- /pkg/util/controller/manager.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package controller 15 | 16 | import ( 17 | "context" 18 | 19 | ctrl "sigs.k8s.io/controller-runtime" 20 | ) 21 | 22 | // Manager is a wrapper of a k8s controller manager. 23 | type Manager struct { 24 | manager ctrl.Manager 25 | cancelFunc context.CancelFunc 26 | } 27 | 28 | // Name of the manager. 29 | func (m *Manager) Name() string { 30 | return "controller-manager" 31 | } 32 | 33 | // Start the manager. 34 | func (m *Manager) Start() error { 35 | ctx, cancelFunc := context.WithCancel(context.Background()) 36 | m.cancelFunc = cancelFunc 37 | return m.manager.Start(ctx) 38 | } 39 | 40 | // Stop the manager. 41 | func (m *Manager) Stop() error { 42 | if m.cancelFunc != nil { 43 | m.cancelFunc() 44 | } 45 | return nil 46 | } 47 | 48 | // GracefulStop does a graceful stop of the manager. 49 | func (m *Manager) GracefulStop() error { 50 | return m.Stop() 51 | } 52 | 53 | // NewManager returns a new manager. 54 | func NewManager(manager ctrl.Manager) *Manager { 55 | return &Manager{ 56 | manager: manager, 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /pkg/util/grpc/server.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package grpc 15 | 16 | import ( 17 | "crypto/tls" 18 | 19 | "google.golang.org/grpc" 20 | "google.golang.org/grpc/credentials" 21 | 22 | "github.com/clusterlink-net/clusterlink/pkg/util/tcp" 23 | ) 24 | 25 | // Server is a wrapper of a gRPC server. 26 | type Server struct { 27 | tcp.Listener 28 | 29 | server *grpc.Server 30 | } 31 | 32 | // GetGRPCServer returns the underlying gRPC server instance. 33 | func (s *Server) GetGRPCServer() *grpc.Server { 34 | return s.server 35 | } 36 | 37 | // Serve starts the server. 38 | func (s *Server) Start() error { 39 | return s.server.Serve(s.GetListener()) 40 | } 41 | 42 | // Stop the server. 43 | func (s *Server) Stop() error { 44 | s.server.Stop() 45 | return nil 46 | } 47 | 48 | // GracefulStop does a graceful stop of the server. 49 | func (s *Server) GracefulStop() error { 50 | s.server.GracefulStop() 51 | return nil 52 | } 53 | 54 | // NewServer returns a new server. 55 | func NewServer(name string, tlsConfig *tls.Config) *Server { 56 | return &Server{ 57 | Listener: tcp.NewListener(name), 58 | server: grpc.NewServer(grpc.Creds(credentials.NewTLS(tlsConfig))), 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /pkg/util/tcp/listener.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package tcp 15 | 16 | import ( 17 | "net" 18 | 19 | "github.com/sirupsen/logrus" 20 | ) 21 | 22 | // Listener is a wrapper of a TCP listener. 23 | type Listener struct { 24 | name string 25 | address string 26 | listener net.Listener 27 | 28 | logger *logrus.Entry 29 | } 30 | 31 | // Listen starts the listener. 32 | func (l *Listener) Listen(address string) error { 33 | l.logger.Infof("Creating listener on %s.", address) 34 | 35 | lis, err := net.Listen("tcp", address) 36 | if err != nil { 37 | return err 38 | } 39 | 40 | l.address = address 41 | l.listener = lis 42 | return nil 43 | } 44 | 45 | // GetAddress returns the listening address. 46 | func (l *Listener) GetAddress() string { 47 | return l.address 48 | } 49 | 50 | // GetListener returns the wrapped listener. 51 | func (l *Listener) GetListener() net.Listener { 52 | return l.listener 53 | } 54 | 55 | // Name returns the name of listener. 56 | func (l *Listener) Name() string { 57 | return l.name 58 | } 59 | 60 | // Close the listener. 61 | func (l *Listener) Close() error { 62 | l.logger.Infof("Closing listener.") 63 | 64 | if l.listener != nil { 65 | return l.listener.Close() 66 | } 67 | return nil 68 | } 69 | 70 | // NewListener returns a new listener. 71 | func NewListener(name string) Listener { 72 | return Listener{ 73 | name: name, 74 | logger: logrus.WithFields(logrus.Fields{ 75 | "component": "listener", 76 | "name": name, 77 | }), 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /pkg/versioninfo/variables.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | // Package versioninfo uses Go's runtime/debug to set executable revision information. 15 | package versioninfo 16 | 17 | import ( 18 | "time" 19 | ) 20 | 21 | var ( 22 | // GitTag will hold the Git tag information. 23 | GitTag string 24 | // Version will be the version tag if the binary is built or "(devel)". 25 | Version = "unknown" 26 | // Revision is taken from the vcs.revision tag. 27 | Revision = "unknown" 28 | // LastCommit is taken from the vcs.time tag. 29 | LastCommit time.Time 30 | // DirtyBuild is taken from the vcs.modified tag. 31 | DirtyBuild = false 32 | ) 33 | -------------------------------------------------------------------------------- /pkg/versioninfo/version.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | package versioninfo 14 | 15 | import ( 16 | "runtime/debug" 17 | "strings" 18 | "time" 19 | ) 20 | 21 | func init() { 22 | info, ok := debug.ReadBuildInfo() 23 | if !ok { 24 | return 25 | } 26 | 27 | for _, kv := range info.Settings { 28 | switch kv.Key { 29 | case "vcs.revision": 30 | Revision = kv.Value 31 | case "vcs.time": 32 | LastCommit, _ = time.Parse(time.RFC3339, kv.Value) //nolint:errcheck // ignore last commit not being a valid time 33 | case "vcs.modified": 34 | DirtyBuild = kv.Value == "true" 35 | } 36 | } 37 | } 38 | 39 | // Short provides a short string summarizing available version information. 40 | // The format is -[-]. 41 | func Short() string { 42 | parts := make([]string, 0, 4) 43 | if GitTag != "" { 44 | parts = append(parts, GitTag) 45 | } 46 | if Version != "unknown" && Version != "(devel)" { 47 | parts = append(parts, Version) 48 | } 49 | if Revision != "unknown" && Revision != "" { 50 | parts = append(parts, "rev") 51 | commit := Revision 52 | if len(commit) > 7 { 53 | commit = commit[:7] 54 | } 55 | parts = append(parts, commit) 56 | if DirtyBuild { 57 | parts = append(parts, "dirty") 58 | } 59 | } 60 | 61 | if len(parts) == 0 { 62 | return "devel" 63 | } 64 | return strings.Join(parts, "-") 65 | } 66 | -------------------------------------------------------------------------------- /tests/e2e/k8s/k8s_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package k8s_test 15 | 16 | import ( 17 | "testing" 18 | 19 | "github.com/stretchr/testify/suite" 20 | 21 | "github.com/clusterlink-net/clusterlink/tests/e2e/k8s" 22 | ) 23 | 24 | // TestE2EK8S runs all k8s e2e tests. 25 | func TestE2EK8S(t *testing.T) { 26 | suite.Run(t, &k8s.TestSuite{}) 27 | } 28 | -------------------------------------------------------------------------------- /tests/e2e/k8s/services/errors.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package services 15 | 16 | // ServiceNotFoundError represents error accessing an imported service which does not exist. 17 | type ServiceNotFoundError struct{} 18 | 19 | func (e ServiceNotFoundError) Error() string { 20 | return "service not found" 21 | } 22 | 23 | // ConnectionRefusedError represents a connection refused error when trying to access an imported service. 24 | type ConnectionRefusedError struct{} 25 | 26 | func (e ConnectionRefusedError) Error() string { 27 | return "connection refused" 28 | } 29 | 30 | // ConnectionRefusedError represents a connection reset error when trying to access an imported service. 31 | type ConnectionResetError struct{} 32 | 33 | func (e ConnectionResetError) Error() string { 34 | return "connection reset" 35 | } 36 | -------------------------------------------------------------------------------- /tests/e2e/k8s/services/httpecho/server.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package httpecho 15 | 16 | import "github.com/clusterlink-net/clusterlink/tests/e2e/k8s/util" 17 | 18 | func ServerPod(service util.Service, echoValue string) *util.PodAndService { 19 | return &util.PodAndService{ 20 | Service: service, 21 | Image: "hashicorp/http-echo", 22 | Args: []string{"-listen=:8080", "-text=" + echoValue}, 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/e2e/k8s/services/iperf3/client.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package iperf3 15 | 16 | import ( 17 | "encoding/json" 18 | "fmt" 19 | "strconv" 20 | "time" 21 | 22 | "github.com/clusterlink-net/clusterlink/tests/e2e/k8s/util" 23 | ) 24 | 25 | // RunClient runs iperf3 client. Returns bits/second. 26 | func RunClient(cluster *util.KindCluster, server *util.Service) (float64, error) { 27 | type iperfOutput struct { 28 | //nolint:tagliatelle // iperf output is out of our control 29 | End struct { 30 | SumSent struct { 31 | BitsPerSecond float64 `json:"bits_per_second"` 32 | } `json:"sum_sent"` 33 | SumReceived struct { 34 | BitsPerSecond float64 `json:"bits_per_second"` 35 | } `json:"sum_received"` 36 | } 37 | } 38 | 39 | var output string 40 | var err error 41 | for t := time.Now(); time.Since(t) < time.Second*60; time.Sleep(time.Millisecond * 500) { 42 | output, err = cluster.RunPod(&util.Pod{ 43 | Name: "iperf3-client", 44 | Namespace: server.Namespace, 45 | Image: "networkstatic/iperf3", 46 | Args: []string{"-J", "-c", server.Name, "-p", strconv.Itoa(int(server.Port))}, 47 | }) 48 | if err == nil { 49 | break 50 | } 51 | } 52 | if err != nil { 53 | return 0, err 54 | } 55 | 56 | var result iperfOutput 57 | if err := json.Unmarshal([]byte(output), &result); err != nil { 58 | return 0, fmt.Errorf("cannot decode iperf results: %w", err) 59 | } 60 | 61 | return (result.End.SumReceived.BitsPerSecond + result.End.SumSent.BitsPerSecond) / 2, nil 62 | } 63 | -------------------------------------------------------------------------------- /tests/e2e/k8s/services/iperf3/server.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package iperf3 15 | 16 | import "github.com/clusterlink-net/clusterlink/tests/e2e/k8s/util" 17 | 18 | // ServerPod returns an iperf3 server pod. 19 | func ServerPod(service util.Service) *util.PodAndService { 20 | return &util.PodAndService{ 21 | Service: service, 22 | Image: "networkstatic/iperf3", 23 | Args: []string{"-s", "-p", "5201"}, 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/e2e/k8s/test_basic.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package k8s 15 | 16 | import ( 17 | "github.com/clusterlink-net/clusterlink/tests/e2e/k8s/services/httpecho" 18 | "github.com/clusterlink-net/clusterlink/tests/e2e/k8s/util" 19 | "github.com/stretchr/testify/require" 20 | ) 21 | 22 | func (s *TestSuite) TestConnectivity() { 23 | s.RunOnAllDataplaneTypes(func(cfg *util.PeerConfig) { 24 | cl, err := s.fabric.DeployClusterlinks(2, cfg) 25 | require.Nil(s.T(), err) 26 | 27 | require.Nil(s.T(), cl[0].CreateService(&httpEchoService)) 28 | require.Nil(s.T(), cl[0].CreateExport(&httpEchoService)) 29 | require.Nil(s.T(), cl[0].CreatePolicy(util.PolicyAllowAll)) 30 | require.Nil(s.T(), cl[1].CreatePeer(cl[0])) 31 | 32 | importedService := &util.Service{ 33 | Name: httpEchoService.Name, 34 | Port: 80, 35 | } 36 | require.Nil(s.T(), cl[1].CreateImport(importedService, cl[0], httpEchoService.Name)) 37 | 38 | require.Nil(s.T(), cl[1].CreatePolicy(util.PolicyAllowAll)) 39 | 40 | data, err := cl[1].AccessService(httpecho.GetEchoValue, importedService, true, nil) 41 | require.Nil(s.T(), err) 42 | require.Equal(s.T(), cl[0].Name(), data) 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /tests/e2e/k8s/test_performance.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package k8s 15 | 16 | import ( 17 | "fmt" 18 | 19 | "github.com/stretchr/testify/require" 20 | 21 | "github.com/clusterlink-net/clusterlink/tests/e2e/k8s/services/iperf3" 22 | "github.com/clusterlink-net/clusterlink/tests/e2e/k8s/util" 23 | ) 24 | 25 | func (s *TestSuite) TestPerformance() { 26 | // measure baseline performance 27 | baseBPS, err := iperf3.RunClient(s.clusters[0], &iperf3Service) 28 | require.Nil(s.T(), err) 29 | s.exportLogs() 30 | 31 | fmt.Printf("Baseline performance: %.2f Gbit/s\n", baseBPS/(1024*1024*1024)) 32 | 33 | s.RunOnAllDataplaneTypes(func(cfg *util.PeerConfig) { 34 | // iperf is expected to generate many MBs of traffic 35 | cfg.ExpectLargeDataplaneTraffic = true 36 | 37 | cl, err := s.fabric.DeployClusterlinks(2, cfg) 38 | require.Nil(s.T(), err) 39 | 40 | require.Nil(s.T(), cl[0].CreateService(&iperf3Service)) 41 | require.Nil(s.T(), cl[0].CreateExport(&iperf3Service)) 42 | require.Nil(s.T(), cl[0].CreatePolicy(util.PolicyAllowAll)) 43 | require.Nil(s.T(), cl[1].CreatePeer(cl[0])) 44 | 45 | importedService := &util.Service{ 46 | Name: iperf3Service.Name, 47 | Namespace: cl[1].Namespace(), 48 | Port: 80, 49 | } 50 | require.Nil(s.T(), cl[1].CreateImport(importedService, cl[0], iperf3Service.Name)) 51 | 52 | require.Nil(s.T(), cl[1].CreatePolicy(util.PolicyAllowAll)) 53 | 54 | bps, err := iperf3.RunClient(cl[1].Cluster(), importedService) 55 | require.Nil(s.T(), err) 56 | 57 | fmt.Printf("Performance drop: %.2f\n", baseBPS/bps) 58 | }) 59 | } 60 | -------------------------------------------------------------------------------- /tests/e2e/k8s/test_redundancy.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package k8s 15 | 16 | import ( 17 | "github.com/clusterlink-net/clusterlink/tests/e2e/k8s/services/httpecho" 18 | "github.com/clusterlink-net/clusterlink/tests/e2e/k8s/util" 19 | "github.com/stretchr/testify/require" 20 | ) 21 | 22 | func (s *TestSuite) TestRedundancy() { 23 | s.RunOnAllDataplaneTypes(func(cfg *util.PeerConfig) { 24 | cfg.Controlplanes = 3 25 | cfg.Dataplanes = 3 26 | cl, err := s.fabric.DeployClusterlinks(2, cfg) 27 | require.Nil(s.T(), err) 28 | 29 | require.Nil(s.T(), cl[0].CreateService(&httpEchoService)) 30 | require.Nil(s.T(), cl[0].CreateExport(&httpEchoService)) 31 | require.Nil(s.T(), cl[0].CreatePolicy(util.PolicyAllowAll)) 32 | require.Nil(s.T(), cl[1].CreatePeer(cl[0])) 33 | 34 | importedService := &util.Service{ 35 | Name: httpEchoService.Name, 36 | Port: 80, 37 | } 38 | require.Nil(s.T(), cl[1].CreateImport(importedService, cl[0], httpEchoService.Name)) 39 | 40 | require.Nil(s.T(), cl[1].CreatePolicy(util.PolicyAllowAll)) 41 | 42 | data, err := cl[1].AccessService(httpecho.GetEchoValue, importedService, true, nil) 43 | require.Nil(s.T(), err) 44 | require.Equal(s.T(), cl[0].Name(), data) 45 | 46 | for i := 0; i < 100; i++ { 47 | data, err := cl[1].AccessService(httpecho.GetEchoValue, importedService, false, nil) 48 | require.Nil(s.T(), err) 49 | require.Equal(s.T(), cl[0].Name(), data) 50 | } 51 | }) 52 | } 53 | -------------------------------------------------------------------------------- /tests/e2e/k8s/util/async.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) The ClusterLink Authors. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package util 15 | 16 | import ( 17 | "sync" 18 | ) 19 | 20 | // AsyncRunner allows asynchronous running of functions with error tracking. 21 | type AsyncRunner struct { 22 | wg sync.WaitGroup 23 | 24 | lock sync.RWMutex 25 | err error 26 | } 27 | 28 | // Error returns the (combined) errors collected. 29 | func (r *AsyncRunner) Run(f func() error) { 30 | if r.Error() != nil { 31 | return 32 | } 33 | 34 | r.wg.Add(1) 35 | go func() { 36 | r.SetError(f()) 37 | r.wg.Done() 38 | }() 39 | } 40 | 41 | // SetError collects a new error. 42 | func (r *AsyncRunner) SetError(err error) { 43 | if err == nil { 44 | return 45 | } 46 | 47 | r.lock.Lock() 48 | defer r.lock.Unlock() 49 | if r.err == nil { 50 | r.err = err 51 | } 52 | } 53 | 54 | // Error returns the (combined) errors collected. 55 | func (r *AsyncRunner) Error() error { 56 | r.lock.RLock() 57 | defer r.lock.RUnlock() 58 | return r.err 59 | } 60 | 61 | // Wait for async operations to finish. 62 | func (r *AsyncRunner) Wait() error { 63 | r.wg.Wait() 64 | return r.Error() 65 | } 66 | 67 | // ClearErrors clears all errors. 68 | func (r *AsyncRunner) ClearErrors() { 69 | r.lock.Lock() 70 | defer r.lock.Unlock() 71 | r.err = nil 72 | } 73 | -------------------------------------------------------------------------------- /website/.gitignore: -------------------------------------------------------------------------------- 1 | # hugo files 2 | .hugo_build.lock 3 | public/ 4 | resources/ 5 | node_modules/ 6 | package-lock.json 7 | -------------------------------------------------------------------------------- /website/Makefile: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------ 2 | # website targets 3 | #------------------------------------------------------ 4 | .PHONY: module_update build clean serve serve-dev checklinks checklinks-dev 5 | 6 | module_update: ; $(info updating hugo modules...) 7 | @hugo mod get -u ./... 8 | @hugo mod tidy 9 | 10 | build: ; $(info building site...) 11 | @hugo 12 | 13 | # delete hugo modules under clean? 14 | clean: ; $(info cleaning website output directory...) 15 | @rm -rf ./public 16 | 17 | serve: ; $(info running locally with env=production...) 18 | @hugo server --gc --cleanDestinationDir --disableFastRender 19 | 20 | serve-dev: ; $(info running locally env=dev \(drafts and future\)...) 21 | @hugo server --gc --cleanDestinationDir --disableFastRender --buildDrafts --buildFuture --enableGitInfo 22 | 23 | checklinks: ; $(info running link check on https://clusterlink.net...) 24 | @muffet -c 16 -b 65536 --rate-limit 16 https://clusterlink.net 25 | 26 | # run "make serve-dev" in one terminal and this command in another... 27 | checklinks-dev: ; $(info running link check on http://localhost:1313...) 28 | @muffet -c 16 -b 65536 --rate-limit 16 http://localhost:1313 29 | -------------------------------------------------------------------------------- /website/assets/icons/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/assets/icons/logo.png -------------------------------------------------------------------------------- /website/assets/scss/_variables_project.scss: -------------------------------------------------------------------------------- 1 | /* 2 | Add styles or override variables from the theme here. 3 | */ 4 | 5 | html.smooth-scroll { 6 | scroll-behavior: smooth; 7 | } 8 | 9 | // Theme colors 10 | $primary: #4279f4; 11 | $secondary: #fff; 12 | $tertiary: #dee2e6; 13 | $dark: #213d7a; 14 | $info: #adb5bd; 15 | $light: #faaf40; 16 | 17 | // Nav bar colors 18 | $white: #fff; 19 | $navbar-dark-color: rgba($white, 1); 20 | $navbar-dark-hover-color: rgba($white, 0.75); 21 | $navbar-dark-active-color: $navbar-dark-color; 22 | 23 | .td-navbar .navbar-brand__name { 24 | display: none; 25 | } -------------------------------------------------------------------------------- /website/content/en/blog/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ClusterLink Blogs 3 | linkTitle: Blog 4 | menu: {main: {weight: 20, pre: "" }} 5 | --- 6 | -------------------------------------------------------------------------------- /website/content/en/blog/clusterlink-intro/field.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/content/en/blog/clusterlink-intro/field.jpg -------------------------------------------------------------------------------- /website/content/en/blog/hello-world/sunflower.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/content/en/blog/hello-world/sunflower.jpg -------------------------------------------------------------------------------- /website/content/en/docs/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Documentation Versions 3 | linkTitle: Versions 4 | simple_list: true 5 | --- 6 | 7 | Welcome to the ClusterLink docs! The following doc versions are available: 8 | -------------------------------------------------------------------------------- /website/content/en/docs/main/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: main (DRAFT) 3 | cascade: 4 | version: main 5 | versName: &name main (DRAFT) 6 | git_version_tag: main 7 | exclude_search: false 8 | linkTitle: *name 9 | simple_list: true 10 | weight: -9999 # Weight for doc version vX.Y should be -(100*X + Y)0 11 | # For example: v0.2.x => -20 v3.6.7 => -3060. `main` is arbitrarily set to -9999 12 | --- 13 | -------------------------------------------------------------------------------- /website/content/en/docs/main/concepts/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Core Concepts 3 | description: Core Concepts of the ClusterLink system 4 | weight: 30 5 | --- 6 | -------------------------------------------------------------------------------- /website/content/en/docs/main/concepts/fabric.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Fabric 3 | description: Defining a ClusterLink fabric 4 | weight: 10 5 | --- 6 | 7 | The concept of a *Fabric* encapsulates a set of cooperating [peers][]. 8 | All peers in a fabric can communicate and may share [services][] 9 | between them, with access governed by [policies][]. 10 | The Fabric acts as a root of trust for peer-to-peer communications (i.e., 11 | it functions as the certificate authority enabling mutual authentication between 12 | peers). 13 | 14 | Currently, the concept of a Fabric is just that - a concept. It is not represented 15 | or backed by any managed resource in a ClusterLink deployment. Once a Fabric is created, 16 | its only relevance is in providing a certificate for use by each peer's gateways. 17 | One could potentially consider a more elaborate implementation where a central 18 | management entity explicitly deals with Fabric life cycle, association of peers to 19 | a fabric, etc. The role of this central management component in ClusterLink is currently 20 | delegated to users who are responsible for coordinating the transfer of certificates 21 | between peers, out of band. 22 | 23 | ## Initializing a new fabric 24 | 25 | ### Prerequisites 26 | 27 | The following sections assume that you have access to the `clusterlink` CLI and one or more 28 | peers (i.e., clusters) where you'll deploy ClusterLink. The CLI can be downloaded 29 | from the ClusterLink [releases page on GitHub][]. 30 | 31 | ### Create a new fabric CA 32 | 33 | To create a new fabric certificate authority (CA), execute the following CLI command: 34 | 35 | ```sh 36 | clusterlink create fabric --name 37 | ``` 38 | 39 | This command will create the CA files `cert.pem` and `key.pem` in a directory named . 40 | The `--name` option is optional, and by default, "default_fabric" will be used. 41 | While you will need access to these files to create the peers` gateway certificates later, 42 | the private key file should be protected and not shared with others. 43 | 44 | ## Related tasks 45 | 46 | Once a Fabric has been created and initialized, you can proceed with configuring 47 | [peers][]. For a complete, end-to-end use case, please refer to the 48 | [iperf tutorial][]. 49 | 50 | [peers]: {{< relref "peers" >}} 51 | [services]: {{< relref "services" >}} 52 | [policies]: {{< relref "policies" >}} 53 | [releases page on GitHub]: https://github.com/clusterlink-net/clusterlink/releases/latest 54 | [iperf tutorial]: {{< relref "../tutorials/iperf" >}} 55 | -------------------------------------------------------------------------------- /website/content/en/docs/main/getting-started/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started 3 | description: Getting started guides for users and developers 4 | weight: 20 5 | --- 6 | 7 | The following sections provide quick start guides for [users][] and [developers][]. 8 | 9 | If you're a content author who wishes to contribute additional documentation or guides, 10 | please refer to the [contribution guidelines][]. 11 | 12 | [users]: {{< relref "users" >}} 13 | [developers]: {{< relref "developers" >}} 14 | [contribution guidelines]: {{< relref "../doc-contribution/" >}} 15 | -------------------------------------------------------------------------------- /website/content/en/docs/main/tasks/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Tasks 3 | description: How to do single specific targeted activities with ClusterLink 4 | weight: 35 5 | --- 6 | -------------------------------------------------------------------------------- /website/content/en/docs/main/tasks/private-networks/frp-system.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/content/en/docs/main/tasks/private-networks/frp-system.png -------------------------------------------------------------------------------- /website/content/en/docs/main/tasks/relay/nginx-relay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/content/en/docs/main/tasks/relay/nginx-relay.png -------------------------------------------------------------------------------- /website/content/en/docs/main/tutorials/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Tutorials 3 | description: Guided ClusterLink tutorials 4 | weight: 40 5 | --- -------------------------------------------------------------------------------- /website/content/en/docs/main/tutorials/bookinfo/bookinfo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/content/en/docs/main/tutorials/bookinfo/bookinfo.png -------------------------------------------------------------------------------- /website/content/en/docs/v0.1/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: v0.1 3 | cascade: 4 | version: v0.1 5 | versName: &name v0.1 6 | git_version_tag: v0.1.0 7 | exclude_search: false 8 | linkTitle: *name 9 | simple_list: true 10 | weight: -10 # Weight for doc version vX.Y should be -(100*X + Y)0 11 | --- 12 | -------------------------------------------------------------------------------- /website/content/en/docs/v0.1/concepts/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Core Concepts 3 | description: Core Concepts and Tasks 4 | weight: 30 5 | --- 6 | -------------------------------------------------------------------------------- /website/content/en/docs/v0.1/getting-started/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started 3 | description: Getting started guides for users and developers 4 | weight: 20 5 | --- 6 | 7 | The following provide quick start guides for [users][user-getting-started] and 8 | [developers][dev-getting-started]. 9 | 10 | If you're a content author, wishing to contribute additional documentation or guides, 11 | please refer to the [contribution guidelines][doc-contribution]. 12 | 13 | [user-getting-started]: {{< relref "users" >}} 14 | [dev-getting-started]: {{< relref "developers" >}} 15 | [doc-contribution]: {{< relref "../doc-contribution/" >}} 16 | -------------------------------------------------------------------------------- /website/content/en/docs/v0.1/tutorials/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Tutorials 3 | description: Guided ClusterLink tutorials 4 | weight: 40 5 | --- -------------------------------------------------------------------------------- /website/content/en/docs/v0.2/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: v0.2 3 | cascade: 4 | version: v0.2 5 | versName: &name v0.2 6 | git_version_tag: v0.2.0 7 | exclude_search: false 8 | linkTitle: *name 9 | simple_list: true 10 | weight: -20 # Weight for doc version vX.Y should be -(100*X + Y)0 11 | --- 12 | -------------------------------------------------------------------------------- /website/content/en/docs/v0.2/concepts/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Core Concepts 3 | description: Core Concepts of the ClusterLink system. 4 | weight: 30 5 | --- 6 | -------------------------------------------------------------------------------- /website/content/en/docs/v0.2/getting-started/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started 3 | description: Getting started guides for users and developers 4 | weight: 20 5 | --- 6 | 7 | The following provide quick start guides for [users][user-getting-started] and 8 | [developers][dev-getting-started]. 9 | 10 | If you're a content author, wishing to contribute additional documentation or guides, 11 | please refer to the [contribution guidelines][doc-contribution]. 12 | 13 | [user-getting-started]: {{< relref "users" >}} 14 | [dev-getting-started]: {{< relref "developers" >}} 15 | [doc-contribution]: {{< relref "../doc-contribution/" >}} 16 | -------------------------------------------------------------------------------- /website/content/en/docs/v0.2/tasks/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Tasks 3 | description: How to do single specific targeted activities with ClusterLink. 4 | weight: 35 5 | --- 6 | -------------------------------------------------------------------------------- /website/content/en/docs/v0.2/tutorials/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Tutorials 3 | description: Guided ClusterLink tutorials 4 | weight: 40 5 | --- -------------------------------------------------------------------------------- /website/content/en/docs/v0.2/tutorials/bookinfo/bookinfo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/content/en/docs/v0.2/tutorials/bookinfo/bookinfo.png -------------------------------------------------------------------------------- /website/content/en/docs/v0.3/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: v0.3 3 | cascade: 4 | version: v0.3 5 | versName: &name v0.3 6 | git_version_tag: v0.3.0 7 | exclude_search: false 8 | linkTitle: *name 9 | simple_list: true 10 | weight: -30 # Weight for doc version vX.Y should be -(100*X + Y)0 11 | # For example: v0.2.x => -20 v3.6.7 => -3060. `main` is arbitrarily set to -9999 12 | --- 13 | -------------------------------------------------------------------------------- /website/content/en/docs/v0.3/concepts/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Core Concepts 3 | description: Core Concepts of the ClusterLink system 4 | weight: 30 5 | --- 6 | -------------------------------------------------------------------------------- /website/content/en/docs/v0.3/concepts/fabric.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Fabric 3 | description: Defining a ClusterLink fabric 4 | weight: 10 5 | --- 6 | 7 | The concept of a *Fabric* encapsulates a set of cooperating [peers][]. 8 | All peers in a fabric can communicate and may share [services][] 9 | between them, with access governed by [policies][]. 10 | The Fabric acts as a root of trust for peer-to-peer communications (i.e., 11 | it functions as the certificate authority enabling mutual authentication between 12 | peers). 13 | 14 | Currently, the concept of a Fabric is just that - a concept. It is not represented 15 | or backed by any managed resource in a ClusterLink deployment. Once a Fabric is created, 16 | its only relevance is in providing a certificate for use by each peer's gateways. 17 | One could potentially consider a more elaborate implementation where a central 18 | management entity explicitly deals with Fabric life cycle, association of peers to 19 | a fabric, etc. The role of this central management component in ClusterLink is currently 20 | delegated to users who are responsible for coordinating the transfer of certificates 21 | between peers, out of band. 22 | 23 | ## Initializing a new fabric 24 | 25 | ### Prerequisites 26 | 27 | The following sections assume that you have access to the `clusterlink` CLI and one or more 28 | peers (i.e., clusters) where you'll deploy ClusterLink. The CLI can be downloaded 29 | from the ClusterLink [releases page on GitHub][]. 30 | 31 | ### Create a new fabric CA 32 | 33 | To create a new fabric certificate authority (CA), execute the following CLI command: 34 | 35 | ```sh 36 | clusterlink create fabric --name 37 | ``` 38 | 39 | This command will create the CA files `cert.pem` and `key.pem` in a directory named . 40 | The `--name` option is optional, and by default, "default_fabric" will be used. 41 | While you will need access to these files to create the peers` gateway certificates later, 42 | the private key file should be protected and not shared with others. 43 | 44 | ## Related tasks 45 | 46 | Once a Fabric has been created and initialized, you can proceed with configuring 47 | [peers][]. For a complete, end-to-end use case, please refer to the 48 | [iperf tutorial][]. 49 | 50 | [peers]: {{< relref "peers" >}} 51 | [services]: {{< relref "services" >}} 52 | [policies]: {{< relref "policies" >}} 53 | [releases page on GitHub]: https://github.com/clusterlink-net/clusterlink/releases/tag/{{% param git_version_tag %}} 54 | [iperf tutorial]: {{< relref "../tutorials/iperf" >}} 55 | -------------------------------------------------------------------------------- /website/content/en/docs/v0.3/getting-started/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started 3 | description: Getting started guides for users and developers 4 | weight: 20 5 | --- 6 | 7 | The following sections provide quick start guides for [users][] and [developers][]. 8 | 9 | If you're a content author who wishes to contribute additional documentation or guides, 10 | please refer to the [contribution guidelines][]. 11 | 12 | [users]: {{< relref "users" >}} 13 | [developers]: {{< relref "developers" >}} 14 | [contribution guidelines]: {{< relref "../doc-contribution/" >}} 15 | -------------------------------------------------------------------------------- /website/content/en/docs/v0.3/tasks/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Tasks 3 | description: How to do single specific targeted activities with ClusterLink 4 | weight: 35 5 | --- 6 | -------------------------------------------------------------------------------- /website/content/en/docs/v0.3/tasks/relay/nginx-relay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/content/en/docs/v0.3/tasks/relay/nginx-relay.png -------------------------------------------------------------------------------- /website/content/en/docs/v0.3/tutorials/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Tutorials 3 | description: Guided ClusterLink tutorials 4 | weight: 40 5 | --- -------------------------------------------------------------------------------- /website/content/en/docs/v0.3/tutorials/bookinfo/bookinfo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/content/en/docs/v0.3/tutorials/bookinfo/bookinfo.png -------------------------------------------------------------------------------- /website/content/en/docs/v0.4/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: v0.4 3 | cascade: 4 | version: v0.4 5 | versName: &name v0.4 6 | git_version_tag: v0.4.0 7 | exclude_search: false 8 | linkTitle: *name 9 | simple_list: true 10 | weight: -9999 # Weight for doc version vX.Y should be -(100*X + Y)0 11 | # For example: v0.2.x => -20 v3.6.7 => -3060. `main` is arbitrarily set to -9999 12 | --- 13 | -------------------------------------------------------------------------------- /website/content/en/docs/v0.4/concepts/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Core Concepts 3 | description: Core Concepts of the ClusterLink system 4 | weight: 30 5 | --- 6 | -------------------------------------------------------------------------------- /website/content/en/docs/v0.4/concepts/fabric.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Fabric 3 | description: Defining a ClusterLink fabric 4 | weight: 10 5 | --- 6 | 7 | The concept of a *Fabric* encapsulates a set of cooperating [peers][]. 8 | All peers in a fabric can communicate and may share [services][] 9 | between them, with access governed by [policies][]. 10 | The Fabric acts as a root of trust for peer-to-peer communications (i.e., 11 | it functions as the certificate authority enabling mutual authentication between 12 | peers). 13 | 14 | Currently, the concept of a Fabric is just that - a concept. It is not represented 15 | or backed by any managed resource in a ClusterLink deployment. Once a Fabric is created, 16 | its only relevance is in providing a certificate for use by each peer's gateways. 17 | One could potentially consider a more elaborate implementation where a central 18 | management entity explicitly deals with Fabric life cycle, association of peers to 19 | a fabric, etc. The role of this central management component in ClusterLink is currently 20 | delegated to users who are responsible for coordinating the transfer of certificates 21 | between peers, out of band. 22 | 23 | ## Initializing a new fabric 24 | 25 | ### Prerequisites 26 | 27 | The following sections assume that you have access to the `clusterlink` CLI and one or more 28 | peers (i.e., clusters) where you'll deploy ClusterLink. The CLI can be downloaded 29 | from the ClusterLink [releases page on GitHub][]. 30 | 31 | ### Create a new fabric CA 32 | 33 | To create a new fabric certificate authority (CA), execute the following CLI command: 34 | 35 | ```sh 36 | clusterlink create fabric --name 37 | ``` 38 | 39 | This command will create the CA files `cert.pem` and `key.pem` in a directory named . 40 | The `--name` option is optional, and by default, "default_fabric" will be used. 41 | While you will need access to these files to create the peers` gateway certificates later, 42 | the private key file should be protected and not shared with others. 43 | 44 | ## Related tasks 45 | 46 | Once a Fabric has been created and initialized, you can proceed with configuring 47 | [peers][]. For a complete, end-to-end use case, please refer to the 48 | [iperf tutorial][]. 49 | 50 | [peers]: {{< relref "peers" >}} 51 | [services]: {{< relref "services" >}} 52 | [policies]: {{< relref "policies" >}} 53 | [releases page on GitHub]: https://github.com/clusterlink-net/clusterlink/releases/tag/{{% param git_version_tag %}} 54 | [iperf tutorial]: {{< relref "../tutorials/iperf" >}} 55 | -------------------------------------------------------------------------------- /website/content/en/docs/v0.4/getting-started/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started 3 | description: Getting started guides for users and developers 4 | weight: 20 5 | --- 6 | 7 | The following sections provide quick start guides for [users][] and [developers][]. 8 | 9 | If you're a content author who wishes to contribute additional documentation or guides, 10 | please refer to the [contribution guidelines][]. 11 | 12 | [users]: {{< relref "users" >}} 13 | [developers]: {{< relref "developers" >}} 14 | [contribution guidelines]: {{< relref "../doc-contribution/" >}} 15 | -------------------------------------------------------------------------------- /website/content/en/docs/v0.4/tasks/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Tasks 3 | description: How to do single specific targeted activities with ClusterLink 4 | weight: 35 5 | --- 6 | -------------------------------------------------------------------------------- /website/content/en/docs/v0.4/tasks/private-networks/frp-system.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/content/en/docs/v0.4/tasks/private-networks/frp-system.png -------------------------------------------------------------------------------- /website/content/en/docs/v0.4/tasks/relay/nginx-relay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/content/en/docs/v0.4/tasks/relay/nginx-relay.png -------------------------------------------------------------------------------- /website/content/en/docs/v0.4/tutorials/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Tutorials 3 | description: Guided ClusterLink tutorials 4 | weight: 40 5 | --- -------------------------------------------------------------------------------- /website/content/en/docs/v0.4/tutorials/bookinfo/bookinfo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/content/en/docs/v0.4/tutorials/bookinfo/bookinfo.png -------------------------------------------------------------------------------- /website/content/en/featured-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/content/en/featured-background.png -------------------------------------------------------------------------------- /website/content/en/search.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Search Results 3 | layout: search 4 | --- -------------------------------------------------------------------------------- /website/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/clusterlink-net/clusterlink/website 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/FortAwesome/Font-Awesome v0.0.0-20240402185447-c0f460dca7f7 // indirect 7 | github.com/google/docsy v0.10.0 // indirect 8 | github.com/google/docsy/dependencies v0.7.2 // indirect 9 | github.com/martignoni/hugo-notice v0.0.0-20240524142214-36e5ee342fef // indirect 10 | github.com/twbs/bootstrap v5.3.3+incompatible // indirect 11 | ) 12 | -------------------------------------------------------------------------------- /website/go.sum: -------------------------------------------------------------------------------- 1 | github.com/FortAwesome/Font-Awesome v0.0.0-20230327165841-0698449d50f2/go.mod h1:IUgezN/MFpCDIlFezw3L8j83oeiIuYoj28Miwr/KUYo= 2 | github.com/FortAwesome/Font-Awesome v0.0.0-20240402185447-c0f460dca7f7 h1:2aWEKCRLqQ9nPyXaz4/IYtRrDr3PzEiX0DUSUr2/EDs= 3 | github.com/FortAwesome/Font-Awesome v0.0.0-20240402185447-c0f460dca7f7/go.mod h1:IUgezN/MFpCDIlFezw3L8j83oeiIuYoj28Miwr/KUYo= 4 | github.com/google/docsy v0.10.0 h1:6tMDacPwAyRWNCfvsn/9qGOZDQ8b0aRzjRZvnZPY5dg= 5 | github.com/google/docsy v0.10.0/go.mod h1:c0nIAqmRTOuJ01F85U/wJPQtc3Zj9N58Kea9bOT2AJc= 6 | github.com/google/docsy/dependencies v0.7.2 h1:+t5ufoADQAj4XneFphz4A+UU0ICAxmNaRHVWtMYXPSI= 7 | github.com/google/docsy/dependencies v0.7.2/go.mod h1:gihhs5gmgeO+wuoay4FwOzob+jYJVyQbNaQOh788lD4= 8 | github.com/martignoni/hugo-notice v0.0.0-20240524142214-36e5ee342fef h1:usUOTC/Tia7tnLy46EqakzYFDQVj1JjuDud00Cn/3y8= 9 | github.com/martignoni/hugo-notice v0.0.0-20240524142214-36e5ee342fef/go.mod h1:MIQPOMgEcbyRC0gNLzQFSgrS+wIy3RuQ/HbaZYtTOKU= 10 | github.com/twbs/bootstrap v5.2.3+incompatible/go.mod h1:fZTSrkpSf0/HkL0IIJzvVspTt1r9zuf7XlZau8kpcY0= 11 | github.com/twbs/bootstrap v5.3.3+incompatible h1:goFoqinzdHfkeegpFP7pvhbd0g+A3O2hbU3XCjuNrEQ= 12 | github.com/twbs/bootstrap v5.3.3+incompatible/go.mod h1:fZTSrkpSf0/HkL0IIJzvVspTt1r9zuf7XlZau8kpcY0= 13 | -------------------------------------------------------------------------------- /website/i18n/en.toml: -------------------------------------------------------------------------------- 1 | # Set the search placeholder empty instead of the default "Search this site..." 2 | [ui_search] 3 | other = " " 4 | -------------------------------------------------------------------------------- /website/layouts/index.redirects: -------------------------------------------------------------------------------- 1 | {{- $latest := site.Params.latest_stable_version -}} 2 | /docs /docs/{{ $latest }} 301! 3 | /docs/latest /docs/{{ $latest }} 4 | /docs/latest/* /docs/{{ $latest }}/:splat 5 | -------------------------------------------------------------------------------- /website/layouts/partials/favicons.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /website/layouts/shortcodes/abbr.html: -------------------------------------------------------------------------------- 1 | 7 | {{ .Get 0 | safeHTML }} 8 | -------------------------------------------------------------------------------- /website/layouts/shortcodes/anchor.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | {{ if .IsNamedParams }} 8 | {{ $id := .Get `id` | lower | safeHTML }}{{ $anchor := anchorize $id }} 9 | 10 | {{ else }} 11 | {{ $id := .Get 0 | lower | safeHTML }}{{ $anchor := anchorize $id }} 12 | 13 | {{ end }} 14 | -------------------------------------------------------------------------------- /website/layouts/shortcodes/clusterlink/card.html: -------------------------------------------------------------------------------- 1 | {{- $href := .Get "url" }} 2 | {{- $pattern := "@VERSION@"}} 3 | {{- with $href }} 4 | {{- if findRE $pattern . }} 5 | {{- $href = strings.Replace . $pattern site.Params.latest_stable_version -}} 6 | {{- end }} 7 | {{- end -}} 8 | 9 |
10 |
 
11 |
12 |
13 | 14 |
15 |

{{ .Get "title" }}

16 |
17 | {{ .Inner }} 18 |
 
19 |

20 | Learn More 21 |

22 |
23 |
24 |
 
25 |
26 | -------------------------------------------------------------------------------- /website/layouts/shortcodes/clusterlink/feature.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |

{{ .Get "title" }}

{{ if strings.ContainsNonSpace .Inner }} 6 |
7 | {{ trim .Inner "\r\n" }} 8 |
9 | {{ end }} 10 |
11 | -------------------------------------------------------------------------------- /website/layouts/shortcodes/clusterlink/intro.html: -------------------------------------------------------------------------------- 1 | {{- $href := .Get "url" }} 2 | {{- $pattern := "@VERSION@"}} 3 | {{- with $href }} 4 | {{- if findRE $pattern . }} 5 | {{- $href = strings.Replace . $pattern site.Params.latest_stable_version -}} 6 | {{- end }} 7 | {{- end -}} 8 | 9 | 10 | 11 |
12 |

{{ .Inner }}

13 | {{ .Get "buttontext" }} 14 |
15 | -------------------------------------------------------------------------------- /website/layouts/shortcodes/clusterlink/main.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |

What is ClusterLink?


4 |

{{ .Inner }}

5 |

6 | Learn More 7 |

8 |
9 | 10 | 11 |
12 | -------------------------------------------------------------------------------- /website/layouts/shortcodes/expand.html: -------------------------------------------------------------------------------- 1 | 7 | 8 | {{- $summary := "" }} 9 | {{- $open := false }} 10 | {{- if .IsNamedParams }} 11 | {{- $summary = .Get "summary" }} 12 | {{- with .Get "open" }}{{ $open = . }}{{ end }} 13 | {{- else }} 14 | {{- $summary = .Get 0 }} 15 | {{- with .Get 1 }}{{ $open = . }}{{ end }} 16 | {{- end }} 17 |
18 | {{ $summary | markdownify }} 19 | {{ .Inner | markdownify }} 20 |
21 | -------------------------------------------------------------------------------- /website/netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | publish = "public" 3 | command = "hugo --minify --gc" 4 | 5 | [context.production.environment] 6 | HUGO_VERSION = "0.124.1" 7 | HUGO_ENV = "production" 8 | HUGO_ENABLEGITINFO = "true" 9 | 10 | [context.deploy-preview] 11 | command = "hugo --minify --buildFuture --buildDrafts --gc -b $DEPLOY_PRIME_URL" 12 | 13 | [context.deploy-preview.environment] 14 | HUGO_VERSION = "0.124.1" 15 | HUGO_ENV = "production" 16 | -------------------------------------------------------------------------------- /website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "autoprefixer": "^10.4.16", 4 | "postcss": "^8.4.31", 5 | "postcss-cli": "^10.1.0" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /website/static/backgrounds/section2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/static/backgrounds/section2.jpg -------------------------------------------------------------------------------- /website/static/backgrounds/section4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/static/backgrounds/section4.jpg -------------------------------------------------------------------------------- /website/static/clusterlink_figure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/static/clusterlink_figure.png -------------------------------------------------------------------------------- /website/static/clusterlink_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/static/clusterlink_logo.png -------------------------------------------------------------------------------- /website/static/favicons/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/static/favicons/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /website/static/favicons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/static/favicons/favicon-32x32.png -------------------------------------------------------------------------------- /website/static/favicons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/static/favicons/favicon.ico -------------------------------------------------------------------------------- /website/static/favicons/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/static/favicons/favicon.png -------------------------------------------------------------------------------- /website/static/files/peer_crd_sample.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: clusterlink.net/v1alpha1 2 | kind: Peer 3 | metadata: 4 | name: cluster17.us-east 5 | namespace: clusterlink-system 6 | spec: 7 | gateways: 8 | - host: "192.168.0.12" 9 | port: 443 10 | - host: "192.168.0.17" 11 | port: 443 12 | -------------------------------------------------------------------------------- /website/static/files/tutorials/allow-all-policy.md: -------------------------------------------------------------------------------- 1 | 2 | ```sh 3 | echo " 4 | apiVersion: clusterlink.net/v1alpha1 5 | kind: AccessPolicy 6 | metadata: 7 | name: allow-policy 8 | namespace: default 9 | spec: 10 | action: allow 11 | from: 12 | - workloadSelector: {} 13 | to: 14 | - workloadSelector: {} 15 | " | kubectl apply -f - 16 | ``` 17 | -------------------------------------------------------------------------------- /website/static/files/tutorials/cli-installation.md: -------------------------------------------------------------------------------- 1 | 2 | 1. Install ClusterLink CLI on Linux or Mac using the installation script: 3 | 4 | ```sh 5 | curl -L https://github.com/clusterlink-net/clusterlink/releases/latest/download/clusterlink.sh | sh - 6 | ``` 7 | 8 | 2. Verify the installation: 9 | 10 | ```sh 11 | clusterlink --version 12 | ``` 13 | -------------------------------------------------------------------------------- /website/static/files/tutorials/deploy-clusterlink.md: -------------------------------------------------------------------------------- 1 | 2 | 1. Create the fabric and peer certificates for the clusters: 3 | 4 | *Client cluster*: 5 | 6 | ```sh 7 | clusterlink create fabric 8 | clusterlink create peer-cert --name client 9 | ``` 10 | 11 | *Server cluster*: 12 | 13 | ```sh 14 | clusterlink create peer-cert --name server 15 | ``` 16 | 17 | All peer certificates (i.e., for the `client` and `server` clusters, in this tutorial) should be created from the same fabric CA files. 18 | In this tutorial, we assume the server has access to the Fabric certificate created in the `default_fabric` folder. 19 | In this tutorial, we assume the `server` cluster creation has access to the fabric certificate stored in the 20 | `default_fabric` folder. If it doesn't, the fabric certificate should be copied from the `client` to the `server`. 21 | 22 | For more details regarding fabric and peer see [core concepts][]. 23 | 24 | 2. Deploy ClusterLink on each cluster: 25 | 26 | *Client cluster*: 27 | 28 | ```sh 29 | clusterlink deploy peer --name client --ingress=NodePort --ingress-port=30443 30 | ``` 31 | 32 | *Server cluster*: 33 | 34 | ```sh 35 | clusterlink deploy peer --name server --ingress=NodePort --ingress-port=30443 36 | ``` 37 | 38 | This tutorial uses **NodePort** to create an external access point for the kind clusters. 39 | By default `deploy peer` creates an ingress of type **LoadBalancer**, 40 | which is more suitable for Kubernetes clusters running in the cloud. 41 | 42 | 3. Verify that ClusterLink control and data plane components are running: 43 | 44 | It may take a few seconds for the deployments to be successfully created. 45 | 46 | *Client cluster*: 47 | 48 | ```sh 49 | kubectl rollout status deployment cl-controlplane -n clusterlink-system 50 | kubectl rollout status deployment cl-dataplane -n clusterlink-system 51 | ``` 52 | 53 | *Server cluster*: 54 | 55 | ```sh 56 | kubectl rollout status deployment cl-controlplane -n clusterlink-system 57 | kubectl rollout status deployment cl-dataplane -n clusterlink-system 58 | ``` 59 | 60 | {{% expand summary="Sample output" %}} 61 | 62 | deployment "cl-controlplane" successfully rolled out 63 | deployment "cl-dataplane" successfully rolled out 64 | 65 | {{% /expand %}} 66 | 67 | [core concepts]: {{< relref "../../concepts/_index.md" >}} 68 | -------------------------------------------------------------------------------- /website/static/files/tutorials/envsubst.md: -------------------------------------------------------------------------------- 1 | 2 | Note that the provided YAML configuration files refer to environment variables 3 | (defined below) that should be set when running the tutorial. The values are 4 | replaced in the YAMLs using `envsubst` utility. 5 | 6 | {{% expand summary="Installing `envsubst` on macOS" %}} 7 | In case `envsubst` does not exist, you can install it with: 8 | 9 | ```sh 10 | brew install gettext 11 | brew link --force gettext 12 | ``` 13 | 14 | {{% /expand %}} 15 | -------------------------------------------------------------------------------- /website/static/files/tutorials/nginx/nginx-output.md: -------------------------------------------------------------------------------- 1 | 2 | {{% expand summary="Sample output" %}} 3 | 4 | ```sh 5 | % Total % Received % Xferd Average Speed Time Time Time Current 6 | Dload Upload Total Spent Left Speed 7 | 8 | 9 | 10 | Welcome to nginx! 11 | 16 | 17 | 18 |

Welcome to nginx!

19 |

If you see this page, the nginx web server is successfully installed and 20 | working. Further configuration is required.

21 | 22 |

For online documentation and support please refer to 23 | nginx.org.
24 | Commercial support is available at 25 | nginx.com.

26 | 27 |

Thank you for using nginx.

28 | 29 | 30 | ``` 31 | 32 | {{% /expand %}} 33 | -------------------------------------------------------------------------------- /website/static/icons/icon1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/static/icons/icon1.png -------------------------------------------------------------------------------- /website/static/icons/icon2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/static/icons/icon2.png -------------------------------------------------------------------------------- /website/static/icons/icon3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/static/icons/icon3.png -------------------------------------------------------------------------------- /website/static/icons/icon4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/static/icons/icon4.png -------------------------------------------------------------------------------- /website/static/icons/icon5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/static/icons/icon5.png -------------------------------------------------------------------------------- /website/static/icons/icon6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/static/icons/icon6.png -------------------------------------------------------------------------------- /website/static/logo-darkblue-yellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/static/logo-darkblue-yellow.png -------------------------------------------------------------------------------- /website/static/logo-lightblue-yellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/static/logo-lightblue-yellow.png -------------------------------------------------------------------------------- /website/static/logo-white-yellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clusterlink-net/clusterlink/2326217813f7350dd7699d8c610cdfedfc16b9c7/website/static/logo-white-yellow.png --------------------------------------------------------------------------------