├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ └── feature_request.yml ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── documentation.yml │ └── pull_request.yml ├── .gitignore ├── .golangci.toml ├── .goreleaser.yml ├── .semaphore └── semaphore.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── cmd ├── cleanup │ └── cleanup.go ├── configuration.go ├── context.go ├── loaders.go ├── log.go ├── mesh │ └── mesh.go ├── prepare │ └── prepare.go └── version │ └── version.go ├── docs ├── .dockerignore ├── .markdownlint.json ├── Makefile ├── check.Dockerfile ├── content │ ├── api.md │ ├── assets │ │ ├── img │ │ │ ├── after-traefik-mesh-graphic.png │ │ │ ├── before-traefik-mesh-graphic.png │ │ │ ├── smi.png │ │ │ ├── traefik-mesh-logo.svg │ │ │ └── traefik-mesh.png │ │ └── js │ │ │ ├── extra.js │ │ │ └── hljs │ │ │ ├── LICENSE │ │ │ └── highlight.pack.js │ ├── compatibility.md │ ├── configuration.md │ ├── contributing │ │ ├── building-testing.md │ │ ├── documentation.md │ │ ├── maintainers.md │ │ ├── submitting-issues.md │ │ ├── submitting-pull-requests.md │ │ └── thank-you.md │ ├── examples.md │ ├── index.md │ ├── install.md │ ├── migration │ │ ├── helm-chart.md │ │ └── traefik-mesh-v1.md │ └── quickstart.md ├── docs.Dockerfile ├── mkdocs.yml ├── readme.md ├── requirements.txt ├── runtime.txt └── scripts │ ├── lint.sh │ ├── netlify-run.sh │ └── verify.sh ├── go.mod ├── go.sum ├── integration ├── acl_disabled_test.go ├── acl_enabled_test.go ├── coredns_test.go ├── integration_test.go ├── k3d │ └── k3d.go ├── kubedns_test.go ├── testdata │ ├── acl_disabled │ │ ├── http │ │ │ └── 1.server.yaml │ │ ├── tcp │ │ │ └── 1.server.yaml │ │ ├── traffic-split │ │ │ ├── 1.server.yaml │ │ │ ├── 2.server-v1.yaml │ │ │ └── 3.server-v2.yaml │ │ └── udp │ │ │ └── 1.server.yaml │ ├── acl_enabled │ │ ├── http │ │ │ ├── 0.service-account.yaml │ │ │ ├── 1.server.yaml │ │ │ ├── 3.server-api.yaml │ │ │ ├── 4.server-header.yaml │ │ │ ├── 6.traffic-target.yaml │ │ │ ├── 7.traffic-target-path-filter.yaml │ │ │ └── 8.traffic-target-header-filter.yaml │ │ └── traffic-split │ │ │ ├── 0.service-account.yaml │ │ │ └── 1.server-split.yaml │ ├── coredns │ │ └── whoami-shadow-service.yaml │ ├── crds │ │ ├── smi-access.yaml │ │ ├── smi-specs.yaml │ │ └── smi-split.yaml │ ├── kubedns │ │ ├── coredns.yaml │ │ └── kubedns.yaml │ ├── tool │ │ ├── tool-authorized.yaml │ │ ├── tool-forbidden.yaml │ │ └── tool.yaml │ └── traefik-mesh │ │ ├── controller-acl-disabled.yaml │ │ ├── controller-acl-enabled.yaml │ │ ├── proxy.yaml │ │ └── values.yaml ├── tool │ └── tool.go └── try │ ├── condition.go │ └── try.go ├── netlify.toml ├── pkg ├── annotations │ ├── annotations.go │ ├── annotations_test.go │ ├── middleware.go │ └── middleware_test.go ├── api │ ├── api.go │ ├── api_test.go │ └── testdata │ │ ├── getmeshnodeconfiguration_empty.yaml │ │ ├── getmeshnodeconfiguration_simple.yaml │ │ ├── getmeshnodes_empty.yaml │ │ ├── getmeshnodes_one_mesh_pod.yaml │ │ └── getmeshnodes_one_nonready_mesh_pod.yaml ├── cleanup │ ├── cleanup.go │ ├── cleanup_test.go │ └── testdata │ │ └── mock.yaml ├── controller │ ├── controller.go │ ├── controller_test.go │ ├── handler.go │ ├── handler_test.go │ ├── portmapping.go │ ├── portmapping_test.go │ ├── service.go │ ├── service_test.go │ └── testdata │ │ ├── getmeshnodeconfiguration_empty.yaml │ │ ├── getmeshnodeconfiguration_simple.yaml │ │ ├── getmeshnodes_empty.yaml │ │ ├── getmeshnodes_one_mesh_pod.yaml │ │ ├── getmeshnodes_one_nonready_mesh_pod.yaml │ │ └── mock.yaml ├── dns │ ├── dns.go │ ├── dns_test.go │ └── testdata │ │ ├── checkdnsprovider_coredns_using_label.yaml │ │ ├── checkdnsprovider_kubedns.yaml │ │ ├── checkdnsprovider_no_provider.yaml │ │ ├── checkdnsprovider_supported_min_version_suffix.yaml │ │ ├── checkdnsprovider_supported_version.yaml │ │ ├── checkdnsprovider_supported_version_suffix.yaml │ │ ├── checkdnsprovider_unsupported_version.yaml │ │ ├── configurecoredns_17.yaml │ │ ├── configurecoredns_17_already_patched.yaml │ │ ├── configurecoredns_17_custom_already_patched.yaml │ │ ├── configurecoredns_17_suffix.yaml │ │ ├── configurecoredns_already_patched.yaml │ │ ├── configurecoredns_custom_already_patched.yaml │ │ ├── configurecoredns_custom_not_patched.yaml │ │ ├── configurecoredns_missing_configmap.yaml │ │ ├── configurecoredns_missing_deployment.yaml │ │ ├── configurecoredns_not_patched.yaml │ │ ├── configurekubedns_already_patched.yaml │ │ ├── configurekubedns_missing_deployment.yaml │ │ ├── configurekubedns_not_patched.yaml │ │ ├── configurekubedns_optional_configmap.yaml │ │ ├── restorecoredns_custom_not_patched.yaml │ │ ├── restorecoredns_custom_patched.yaml │ │ ├── restorecoredns_not_patched.yaml │ │ ├── restorecoredns_patched.yaml │ │ ├── restorekubedns_already_patched.yaml │ │ └── restorekubedns_not_patched.yaml ├── k8s │ ├── client.go │ ├── client_mock.go │ ├── constants.go │ ├── filter.go │ ├── filter_test.go │ ├── smi.go │ └── testdata │ │ └── mock.yaml ├── provider │ ├── key.go │ ├── provider.go │ ├── provider_test.go │ ├── rule.go │ └── testdata │ │ ├── acl-disabled-http-basic-config.json │ │ ├── acl-disabled-http-basic-topology.json │ │ ├── acl-disabled-http-traffic-split-config.json │ │ ├── acl-disabled-http-traffic-split-topology.json │ │ ├── acl-disabled-tcp-basic-config.json │ │ ├── acl-disabled-tcp-basic-topology.json │ │ ├── acl-disabled-udp-basic-config.json │ │ ├── acl-disabled-udp-basic-topology.json │ │ ├── acl-enabled-http-basic-config.json │ │ ├── acl-enabled-http-basic-topology.json │ │ ├── acl-enabled-http-route-group-config.json │ │ ├── acl-enabled-http-route-group-topology.json │ │ ├── acl-enabled-http-traffic-split-config.json │ │ ├── acl-enabled-http-traffic-split-http-route-group-config.json │ │ ├── acl-enabled-http-traffic-split-http-route-group-topology.json │ │ ├── acl-enabled-http-traffic-split-topology.json │ │ ├── acl-enabled-tcp-basic-config.json │ │ ├── acl-enabled-tcp-basic-topology.json │ │ ├── annotations-scheme-config.json │ │ ├── annotations-scheme-topology.json │ │ ├── annotations-traffic-type-config.json │ │ └── annotations-traffic-type-topology.json ├── safe │ ├── recover.go │ ├── recover_test.go │ ├── safe.go │ └── safe_test.go ├── topology │ ├── builder.go │ ├── builder_test.go │ ├── testdata │ │ ├── topology-empty-destination-port.json │ │ ├── topology-multi-sources-destinations.json │ │ ├── topology-service-with-pod-port-mixture.json │ │ ├── topology-spec-with-empty-match.json │ │ ├── topology-traffic-split-specs.json │ │ ├── topology-traffic-split-traffic-target.json │ │ ├── topology-traffic-target-service-port-mismatch.json │ │ └── topology-traffic-target.json │ ├── topology.go │ └── topology_test.go └── version │ └── version.go ├── spinach.yaml └── tmpl.Dockerfile /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Create a report to help us improve Traefik Mesh. 3 | labels: ['status/0-needs-triage'] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | **DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.** 9 | 10 | The issue tracker is for reporting bugs and feature requests only. 11 | For end-user related support questions, please use the [Traefik Mesh community forum](https://community.traefik.io/c/traefik-mesh). 12 | 13 | ### How to write a good bug report? 14 | 15 | - Respect the issue template as much as possible. 16 | - The title should be short and descriptive. 17 | - Explain the conditions which led you to report this issue: the context. 18 | - The context should lead to something, an idea or a problem that you’re facing. 19 | - Remain clear and concise. 20 | - Format your messages to help the reader focus on what matters and understand the structure of your message, use [Markdown syntax](https://help.github.com/articles/github-flavored-markdown) 21 | 22 | - type: checkboxes 23 | id: terms 24 | attributes: 25 | label: Welcome! 26 | options: 27 | - label: Yes, I've searched similar issues on [GitHub](https://github.com/traefik/mesh/issues) and didn't find any. 28 | required: true 29 | - label: Yes, I've searched similar issues on the [Traefik Mesh community forum](https://community.traefik.io/c/traefik-mesh) and didn't find any. 30 | required: true 31 | 32 | - type: textarea 33 | id: expect 34 | attributes: 35 | label: What did you expect to see? 36 | description: Use [Markdown syntax](https://help.github.com/articles/github-flavored-markdown) if needed. 37 | placeholder: What did you expect to see? 38 | validations: 39 | required: true 40 | 41 | - type: textarea 42 | id: instead 43 | attributes: 44 | label: What did you see instead? 45 | description: Use [Markdown syntax](https://help.github.com/articles/github-flavored-markdown) if needed. 46 | placeholder: What did you see instead? 47 | validations: 48 | required: true 49 | 50 | - type: input 51 | id: version 52 | attributes: 53 | label: What version of Traefik Mesh are you using? 54 | description: | 55 | `latest` is not considered as a valid version. 56 | We need a release number! 57 | validations: 58 | required: true 59 | 60 | - type: textarea 61 | id: controller 62 | attributes: 63 | label: Output of controller log 64 | placeholder: paste your output here 65 | render: shell 66 | validations: 67 | required: true 68 | 69 | - type: textarea 70 | id: environment 71 | attributes: 72 | label: What is your environment & configuration? 73 | description: | 74 | arguments, YAML, TOML, provider, platform, etc. 75 | 76 | Use [Markdown syntax](https://help.github.com/articles/github-flavored-markdown) if needed. 77 | placeholder: Add information here. 78 | validations: 79 | required: true 80 | 81 | - type: textarea 82 | id: objects 83 | attributes: 84 | label: If applicable, please paste the yaml objects required to reproduce your issue 85 | placeholder: Paste your objects here 86 | render: shell 87 | validations: 88 | required: false 89 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Question 4 | about: Please ask and answer questions here. 5 | url: https://community.traefik.io/c/traefik-mesh 6 | - name: Documentation 7 | url: https://doc.traefik.io/traefik-mesh/ 8 | about: Please take a look to our documenation. 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Propose a change to Traefik Mesh! 3 | labels: ['status/0-needs-triage'] 4 | body: 5 | - type: checkboxes 6 | id: terms 7 | attributes: 8 | label: Welcome! 9 | options: 10 | - label: Yes, I've searched similar issues on [GitHub](https://github.com/traefik/mesh/issues) and didn't find any. 11 | required: true 12 | - label: Yes, I've searched similar issues on the [Traefik Mesh community forum](https://community.traefik.io/c/traefik-mesh) and didn't find any. 13 | required: true 14 | 15 | - type: textarea 16 | id: proposal 17 | attributes: 18 | label: Proposal 19 | description: | 20 | Write your feature request in the form of a proposal to be considered for implementation. 21 | 22 | Use [Markdown syntax](https://help.github.com/articles/github-flavored-markdown) if needed. 23 | placeholder: Write your feature request in the form of a proposal to be considered for implementation. 24 | validations: 25 | required: true 26 | 27 | - type: textarea 28 | id: background 29 | attributes: 30 | label: Background 31 | description: | 32 | Describe the background problem or need that led to this feature request. 33 | 34 | Use [Markdown syntax](https://help.github.com/articles/github-flavored-markdown) if needed. 35 | placeholder: Describe the background problem or need that led to this feature request. 36 | validations: 37 | required: true 38 | 39 | - type: textarea 40 | id: workarounds 41 | attributes: 42 | label: Workarounds 43 | description: | 44 | Are there any current workarounds that you're using that others in similar positions should know? 45 | 46 | Use [Markdown syntax](https://help.github.com/articles/github-flavored-markdown) if needed. 47 | placeholder: Are there any current workarounds that you're using that others in similar positions should know? 48 | validations: 49 | required: true 50 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## What does this PR do? 2 | 3 | This PR: 4 | 5 | - 6 | 7 | Fixes # 8 | 9 | 10 | 11 | ### How to test it 12 | 13 | * Step 1 14 | * Step 2 15 | * ... 16 | 17 | ## Additional Notes 18 | 19 | 23 | -------------------------------------------------------------------------------- /.github/workflows/documentation.yml: -------------------------------------------------------------------------------- 1 | name: Build and Publish Documentation 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - v* 8 | 9 | jobs: 10 | 11 | docs: 12 | name: Doc Process 13 | runs-on: ubuntu-latest 14 | if: github.repository == 'traefik/mesh' 15 | env: 16 | STRUCTOR_VERSION: v1.11.2 17 | MIXTUS_VERSION: v0.4.1 18 | 19 | steps: 20 | 21 | - name: Check out code 22 | uses: actions/checkout@v2 23 | with: 24 | fetch-depth: 0 25 | 26 | - name: Login to DockerHub 27 | uses: docker/login-action@v1 28 | with: 29 | username: ${{ secrets.DOCKERHUB_USERNAME }} 30 | password: ${{ secrets.DOCKERHUB_TOKEN }} 31 | 32 | - name: Install Structor ${{ env.STRUCTOR_VERSION }} 33 | run: curl -sSfL https://raw.githubusercontent.com/traefik/structor/master/godownloader.sh | sh -s -- -b $HOME/bin ${STRUCTOR_VERSION} 34 | 35 | - name: Install Seo-doc 36 | run: curl -sSfL https://raw.githubusercontent.com/traefik/seo-doc/master/godownloader.sh | sh -s -- -b "${HOME}/bin" 37 | 38 | - name: Install Mixtus ${{ env.MIXTUS_VERSION }} 39 | run: curl -sSfL https://raw.githubusercontent.com/traefik/mixtus/master/godownloader.sh | sh -s -- -b $HOME/bin ${MIXTUS_VERSION} 40 | 41 | - name: Build documentation 42 | run: $HOME/bin/structor -o traefik -r mesh --dockerfile-url="https://raw.githubusercontent.com/traefik/mesh/master/docs/docs.Dockerfile" --menu.js-url="https://raw.githubusercontent.com/traefik/structor/master/traefik-menu.js.gotmpl" --rqts-url="https://raw.githubusercontent.com/traefik/structor/master/requirements-override.txt" --force-edit-url --exp-branch=master --debug 43 | env: 44 | STRUCTOR_LATEST_TAG: ${{ secrets.STRUCTOR_LATEST_TAG }} 45 | 46 | - name: Apply seo 47 | run: $HOME/bin/seo -path=./site -product="traefik-mesh" 48 | 49 | - name: Publish documentation 50 | run: $HOME/bin/mixtus --dst-doc-path="./traefik-mesh" --dst-owner=traefik --dst-repo-name=doc --git-user-email="30906710+traefiker@users.noreply.github.com" --git-user-name=traefiker --src-doc-path="./site" --src-owner=traefik --src-repo-name=mesh 51 | env: 52 | GITHUB_TOKEN: ${{ secrets.GH_TOKEN_REPO }} 53 | -------------------------------------------------------------------------------- /.github/workflows/pull_request.yml: -------------------------------------------------------------------------------- 1 | name: Build Documentation 2 | 3 | on: 4 | pull_request: 5 | 6 | jobs: 7 | 8 | docs: 9 | name: Doc package 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | 14 | - name: Check out code 15 | uses: actions/checkout@v2 16 | with: 17 | fetch-depth: 0 18 | 19 | - name: Package documentation 20 | run: make docs-package 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .intellij/ 3 | *.iml 4 | .DS_Store 5 | /static/ 6 | /site/ 7 | /docs/site/ 8 | /dist 9 | *.log 10 | *.exe 11 | cover.out 12 | traefik-mesh 13 | /dist/ 14 | .vscode 15 | vendor 16 | pages/ 17 | mesh-gh-pages* 18 | *.zip 19 | -------------------------------------------------------------------------------- /.golangci.toml: -------------------------------------------------------------------------------- 1 | [run] 2 | timeout = "5m" 3 | 4 | [linters-settings] 5 | 6 | [linters-settings.govet] 7 | check-shadowing = true 8 | 9 | [linters-settings.golint] 10 | min-confidence = 0.0 11 | 12 | [linters-settings.gocyclo] 13 | min-complexity = 15.0 14 | 15 | [linters-settings.gocognit] 16 | min-complexity = 15.0 17 | 18 | [linters-settings.goconst] 19 | min-len = 3.0 20 | min-occurrences = 3.0 21 | 22 | [linters-settings.misspell] 23 | locale = "US" 24 | 25 | [linters-settings.stylecheck] 26 | checks = ["all", "-ST1000"] 27 | 28 | [linters-settings.gomoddirectives] 29 | replace-allow-list = [ 30 | "github.com/abbot/go-http-auth", 31 | "github.com/go-check/check", 32 | "github.com/gorilla/mux", 33 | "github.com/mailgun/minheap", 34 | ] 35 | 36 | [linters] 37 | enable-all = true 38 | disable = [ 39 | "sqlclosecheck", # Not relevant (SQL) 40 | "rowserrcheck", # Not relevant (SQL) 41 | "ifshort", # Deprecated 42 | "interfacer", # Deprecated 43 | "golint", # Deprecated 44 | "maligned", # Deprecated 45 | "scopelint", # Deprecated 46 | "cyclop", # Duplicate of gocyclo 47 | "lll", # Long lines are ok. 48 | "dupl", # Not relevant 49 | "prealloc", # Not relevant 50 | "gochecknoinits", # Too strict 51 | "gochecknoglobals", # Too strict 52 | "gomnd", # Does not allow for any config or time values 53 | "gosec", # Does not allow exec.Command with variable 54 | "bodyclose", # Too many false positives 55 | "goconst", # Too many false positives 56 | "wrapcheck", # Too strict 57 | "goerr113", # Forces wrapping all errors 58 | "noctx", # Too strict 59 | "exhaustive", # Too strict 60 | "exhaustivestruct", # Too strict 61 | "exhaustruct", # Duplicate of exhaustivestruct 62 | "nlreturn", # Too strict 63 | "ireturn", # Not relevant 64 | "varnamelen", # Not relevant 65 | "nilnil", # Not relevant 66 | "testpackage", # Does not allow testing private funcs 67 | "tparallel", # Not relevant 68 | "paralleltest", # Not relevant 69 | "forcetypeassert", # Too strict 70 | "nonamedreturns", # Not relevant 71 | "structcheck", # Duplicate of unused 72 | "funlen", 73 | ] 74 | 75 | [issues] 76 | exclude-use-default = false 77 | max-per-linter = 0 78 | max-same-issues = 0 79 | exclude = [ 80 | "Error return value of .((os\\.)?std(out|err)\\..*|.*Close|.*Flush|os\\.Remove(All)?|.*printf?|os\\.(Un)?Setenv). is not checked", 81 | "should have a package comment, unless it's in another file for this package", 82 | ] 83 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | project_name: traefik-mesh 2 | 3 | builds: 4 | - binary: traefik-mesh 5 | 6 | main: ./cmd/mesh/mesh.go 7 | env: 8 | - CGO_ENABLED=0 9 | ldflags: 10 | - -s -w 11 | - -X github.com/traefik/mesh/pkg/version.Version={{.Version}} 12 | - -X github.com/traefik/mesh/pkg/version.Commit={{.Commit}} 13 | - -X github.com/traefik/mesh/pkg/version.Date={{.Date}} 14 | goos: 15 | - linux 16 | - freebsd 17 | - openbsd 18 | goarch: 19 | - amd64 20 | - 386 21 | - arm 22 | - arm64 23 | goarm: 24 | - 7 25 | 26 | ignore: 27 | - goos: freebsd 28 | goarch: arm64 29 | - goos: openbsd 30 | goarch: arm64 31 | 32 | archives: 33 | - id: traefik-mesh 34 | name_template: '{{ .ProjectName }}_v{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}' 35 | format: tar.gz 36 | files: 37 | - LICENSE 38 | 39 | checksum: 40 | name_template: "{{ .ProjectName }}_v{{ .Version }}_checksums.txt" 41 | 42 | changelog: 43 | sort: asc 44 | filters: 45 | exclude: 46 | - '^docs:' 47 | - '^doc:' 48 | - '^chore:' 49 | - '^test:' 50 | - '^tests:' 51 | -------------------------------------------------------------------------------- /.semaphore/semaphore.yml: -------------------------------------------------------------------------------- 1 | version: v1.0 2 | name: Traefik Mesh Pipeline 3 | 4 | agent: 5 | machine: 6 | type: e1-standard-4 7 | os_image: ubuntu1804 8 | 9 | auto_cancel: 10 | running: 11 | when: "branch != 'master'" 12 | 13 | fail_fast: 14 | stop: 15 | when: "branch != 'master'" 16 | 17 | global_job_config: 18 | secrets: 19 | - name: dockerhub-pull-secrets 20 | prologue: 21 | commands: 22 | - curl -sSfL https://raw.githubusercontent.com/ldez/semgo/master/godownloader.sh | sudo sh -s -- -b "/usr/local/bin" 23 | - sudo semgo go1.19 24 | - echo "${DOCKERHUB_PASSWORD}" | docker login -u "${DOCKERHUB_USERNAME}" --password-stdin 25 | - checkout 26 | 27 | blocks: 28 | - name: Build 29 | run: 30 | when: "branch =~ '.*' OR pull_request =~ '.*'" 31 | task: 32 | jobs: 33 | - name: Cache Go dependencies 34 | commands: 35 | - cache restore 36 | - go mod tidy 37 | - git diff --exit-code go.mod 38 | - git diff --exit-code go.sum 39 | - cache store 40 | - name: Build and check 41 | commands: 42 | - make check build 43 | - cache store traefik-mesh-dist-$SEMAPHORE_GIT_BRANCH-$SEMAPHORE_WORKFLOW_ID dist 44 | - docker save traefik/mesh:latest > traefik-mesh-img.tar 45 | - cache store traefik-mesh-img-$SEMAPHORE_GIT_BRANCH-$SEMAPHORE_WORKFLOW_ID traefik-mesh-img.tar 46 | 47 | - name: Unit Tests 48 | run: 49 | when: "branch =~ '.*' OR pull_request =~ '.*'" 50 | task: 51 | prologue: 52 | commands: 53 | - cache restore 54 | jobs: 55 | - name: Unit Tests 56 | commands: 57 | - make local-test 58 | 59 | - name: Integration Tests 60 | run: 61 | when: "branch =~ '.*' OR pull_request =~ '.*'" 62 | task: 63 | prologue: 64 | commands: 65 | - cache restore 66 | - cache restore traefik-mesh-dist-$SEMAPHORE_GIT_BRANCH-$SEMAPHORE_WORKFLOW_ID 67 | - cache restore traefik-mesh-img-$SEMAPHORE_GIT_BRANCH-$SEMAPHORE_WORKFLOW_ID 68 | - docker load < traefik-mesh-img.tar 69 | jobs: 70 | - name: ACL Enabled Suite 71 | commands: 72 | - "make test-integration-nobuild TESTFLAGS=\"-check.f ACLEnabledSuite\"" 73 | - name: ACL Disabled Suite 74 | commands: 75 | - "make test-integration-nobuild TESTFLAGS=\"-check.f ACLDisabledSuite\"" 76 | - name: CoreDNS Suite 77 | commands: 78 | - "make test-integration-nobuild TESTFLAGS=\"-check.f CoreDNSSuite\"" 79 | - name: KubeDNS Suite 80 | commands: 81 | - "make test-integration-nobuild TESTFLAGS=\"-check.f KubeDNSSuite\"" 82 | 83 | - name: Release 84 | run: 85 | when: "tag =~ '.*'" 86 | task: 87 | secrets: 88 | - name: mesh 89 | 90 | env_vars: 91 | - name: SEIHON_VERSION 92 | value: v0.9.0 93 | 94 | jobs: 95 | - name: Release 96 | commands: 97 | - curl -sL https://git.io/goreleaser | bash -s -- --timeout="60m" 98 | 99 | - name: Publish Images 100 | commands: 101 | # Install Docker image multi-arch builder 102 | - curl -sfL https://raw.githubusercontent.com/ldez/seihon/master/godownloader.sh | sudo bash -s -- -b "/usr/local/bin" ${SEIHON_VERSION} 103 | - seihon --version 104 | - docker run --rm --privileged hypriot/qemu-register 105 | - make publish-images 106 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience,nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. 34 | Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. 35 | Representation of a project may be further defined and clarified by project maintainers. 36 | 37 | ## Enforcement 38 | 39 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at contact@traefik.io 40 | All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. 41 | The project team is obligated to maintain confidentiality with regard to the reporter of an incident. 42 | Further details of specific enforcement policies may be posted separately. 43 | 44 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 45 | 46 | ## Attribution 47 | 48 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 49 | 50 | [homepage]: http://contributor-covenant.org 51 | [version]: http://contributor-covenant.org/version/1/4/ 52 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Traefik Mesh is an open source project, and your feedback and contributions are needed and always welcome. 4 | 5 | [Issues] and [Pull Requests] are opened at https://github.com/traefik/mesh. 6 | 7 | Non trivial changes should be discussed with the project maintainers by opening a [Feature Request] clearly explaining rationale, 8 | background and possible implementation ideas. Feel free to provide code in such discussions. 9 | 10 | Once the proposal is approved, a Pull Request can be opened. If you want to provide early visibility to reviewers, create a [Draft Pull Request]. 11 | 12 | [Issues]: https://github.com/traefik/mesh/issues 13 | [Pull Requests]: https://github.com/traefik/mesh/issues 14 | [Feature Request]: https://github.com/traefik/mesh/issues/new?template=feature_request.md 15 | [Draft Pull Request]: https://github.blog/2019-02-14-introducing-draft-pull-requests/ 16 | 17 | ## Release Process 18 | 19 | Traefik Mesh follows the [semver](https://semver.org/) scheme when releasing new versions. 20 | 21 | Therefore, all PR's (except fixing a bug in a specific version) should be made against the `master` branch. 22 | If you're attempting to fix a bug in an already released version, please use the correct branch of that release (e.g. `v1.1`). 23 | All bug-fixes made to a specific branch will be backported to master, prior to releasing a new (major / minor) version. Patch releases will be made out of the version branch. 24 | 25 | ### Release steps 26 | 27 | In order to release a new version of Traefik Mesh, the following steps have to be done: 28 | 29 | * Create a Prepare release PR updating the chart version and app version to upcoming release 30 | * Prepare a list of release notes for the #traefik-mesh 31 | * Merge Prepare release PR 32 | * Create and push a tag on the release commit 33 | * Create a new release branch (v1.x) on upstream to allow versioned docs to be built 34 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.19-alpine AS base-image 2 | 3 | # Package dependencies 4 | RUN apk --no-cache --no-progress add \ 5 | bash \ 6 | gcc \ 7 | git \ 8 | make \ 9 | musl-dev \ 10 | mercurial \ 11 | curl \ 12 | tar \ 13 | ca-certificates \ 14 | tzdata \ 15 | && update-ca-certificates \ 16 | && rm -rf /var/cache/apk/* 17 | 18 | WORKDIR /go/src/github.com/traefik/mesh 19 | 20 | # Download goreleaser binary to bin folder in $GOPATH 21 | RUN curl -sfL https://gist.githubusercontent.com/traefiker/6d7ac019c11d011e4f131bb2cca8900e/raw/goreleaser.sh | sh 22 | 23 | # Download golangci-lint binary to bin folder in $GOPATH 24 | RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $GOPATH/bin v1.48.0 25 | 26 | ENV GO111MODULE on 27 | COPY go.mod go.sum ./ 28 | RUN go mod download 29 | COPY . . 30 | 31 | FROM base-image as maker 32 | 33 | ARG MAKE_TARGET=local-build 34 | 35 | RUN make ${MAKE_TARGET} 36 | 37 | ## IMAGE 38 | FROM alpine:3.15 39 | 40 | RUN addgroup -g 1000 -S app && \ 41 | adduser -u 1000 -S app -G app 42 | 43 | COPY --from=base-image /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ 44 | COPY --from=maker /go/src/github.com/traefik/mesh/dist/traefik-mesh /app/ 45 | 46 | ENTRYPOINT ["/app/traefik-mesh"] 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Traefik Mesh 3 |

4 | 5 | 6 | [![Semaphore CI Build Status](https://traefik.semaphoreci.com/badges/mesh/branches/master.svg?style=shields)](https://traefik.semaphoreci.com/projects/mesh) 7 | [![Docs](https://img.shields.io/badge/docs-current-brightgreen.svg)](https://doc.traefik.io/traefik-mesh) 8 | [![Go Report Card](https://goreportcard.com/badge/github.com/traefik/mesh)](https://goreportcard.com/report/github.com/traefik/mesh) 9 | [![Release](https://img.shields.io/github/tag-date/traefik/mesh.svg)](https://github.com/traefik/mesh/releases) 10 | [![GitHub](https://img.shields.io/github/license/traefik/mesh)](https://github.com/traefik/mesh/blob/master/LICENSE) 11 | [![Discourse status](https://img.shields.io/discourse/https/community.traefik.io/status?label=Community&style=social)](https://community.traefik.io/c/traefik-mesh) 12 | 13 | ## Traefik Mesh: Simpler Service Mesh 14 | 15 | Traefik Mesh is a simple, yet full-featured service mesh. 16 | It is container-native and fits as your de-facto service mesh in your Kubernetes cluster. 17 | It supports the latest Service Mesh Interface specification [SMI](https://smi-spec.io) that facilitates integration with pre-existing solution. 18 | Moreover, Traefik Mesh is opt-in by default, which means that your existing services are unaffected until you decide to add them to the mesh. 19 | 20 |

21 | SMI 22 |

23 | 24 | 25 | ## Non-Invasive Service Mesh 26 | 27 | Traefik Mesh does not use any sidecar container but handles routing through proxy endpoints running on each node. 28 | The mesh controller runs in a dedicated pod and handles all the configuration parsing and deployment to the proxy nodes. 29 | Traefik Mesh supports multiple configuration options: annotations on user service objects, and SMI objects. 30 | Not using sidecars means that Traefik Mesh does not modify your Kubernetes objects, and does not modify your traffic without your knowledge. 31 | Using the Traefik Mesh endpoints is all that is required. 32 | 33 |

34 | Traefik Mesh 35 | Traefik Mesh 36 |

37 | 38 | ## Prerequisites 39 | 40 | To run this app, you require the following: 41 | 42 | - Kubernetes 1.11+ 43 | - CoreDNS installed as [Cluster DNS Provider](https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/) (versions 1.3+ supported) 44 | - Helm v3 45 | 46 | ## Install (Helm v3 only) 47 | 48 | ```shell 49 | helm repo add traefik https://traefik.github.io/charts 50 | helm repo update 51 | helm install traefik-mesh traefik/traefik-mesh 52 | ``` 53 | 54 | You can find the complete documentation at https://doc.traefik.io/traefik-mesh. 55 | 56 | 57 | ## Contributing 58 | 59 | [Contributing guide](CONTRIBUTING.md). 60 | -------------------------------------------------------------------------------- /cmd/cleanup/cleanup.go: -------------------------------------------------------------------------------- 1 | package cleanup 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os/signal" 7 | "syscall" 8 | 9 | "github.com/traefik/mesh/cmd" 10 | "github.com/traefik/mesh/pkg/cleanup" 11 | "github.com/traefik/mesh/pkg/k8s" 12 | "github.com/traefik/paerser/cli" 13 | ) 14 | 15 | // NewCmd builds a new Cleanup command. 16 | func NewCmd(cConfig *cmd.CleanupConfiguration, loaders []cli.ResourceLoader) *cli.Command { 17 | return &cli.Command{ 18 | Name: "cleanup", 19 | Description: `Removes Traefik Mesh shadow services from a Kubernetes cluster.`, 20 | Configuration: cConfig, 21 | Run: func(_ []string) error { 22 | return cleanupCommand(cConfig) 23 | }, 24 | Resources: loaders, 25 | } 26 | } 27 | 28 | func cleanupCommand(cConfig *cmd.CleanupConfiguration) error { 29 | ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT) 30 | defer cancel() 31 | 32 | logger, err := cmd.NewLogger(cConfig.LogFormat, cConfig.LogLevel, false) 33 | if err != nil { 34 | return fmt.Errorf("could not create logger: %w", err) 35 | } 36 | 37 | logger.Debug("Starting cleanup...") 38 | logger.Debugf("Using masterURL: %q", cConfig.MasterURL) 39 | logger.Debugf("Using kubeconfig: %q", cConfig.KubeConfig) 40 | 41 | clients, err := k8s.NewClient(logger, cConfig.MasterURL, cConfig.KubeConfig) 42 | if err != nil { 43 | return fmt.Errorf("error building clients: %w", err) 44 | } 45 | 46 | c := cleanup.NewCleanup(logger, clients.KubernetesClient(), cConfig.Namespace) 47 | 48 | if err := c.CleanShadowServices(ctx); err != nil { 49 | return fmt.Errorf("error encountered during cluster cleanup: %w", err) 50 | } 51 | 52 | if err := c.RestoreDNSConfig(ctx); err != nil { 53 | return fmt.Errorf("error encountered during DNS restore: %w", err) 54 | } 55 | 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /cmd/context.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | // ContextWithStopChan creates a context canceled when the given stopCh receives a message 8 | // or get closed. 9 | func ContextWithStopChan(ctx context.Context, stopCh <-chan struct{}) context.Context { 10 | ctx, cancel := context.WithCancel(ctx) 11 | 12 | go func() { 13 | defer cancel() 14 | 15 | select { 16 | case <-ctx.Done(): 17 | case <-stopCh: 18 | } 19 | }() 20 | 21 | return ctx 22 | } 23 | -------------------------------------------------------------------------------- /cmd/log.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/sirupsen/logrus" 8 | ) 9 | 10 | // parseLogLevel parses a given log level and returns a standardized level. 11 | func parseLogLevel(level string) (logrus.Level, error) { 12 | return logrus.ParseLevel(level) 13 | } 14 | 15 | // parseLogFormat parses a log format and returns a formatter. 16 | func parseLogFormat(format string) (logrus.Formatter, error) { 17 | switch format { 18 | case "json": 19 | return &logrus.JSONFormatter{}, nil 20 | case "common": 21 | return &logrus.TextFormatter{DisableColors: false, FullTimestamp: true, DisableSorting: true}, nil 22 | default: 23 | return nil, fmt.Errorf("invalid logging format: %s", format) 24 | } 25 | } 26 | 27 | // NewLogger returns a new field logger with the provided format, level, and debug configurations. 28 | func NewLogger(format, level string, debug bool) (logrus.FieldLogger, error) { 29 | log := logrus.New() 30 | log.SetOutput(os.Stdout) 31 | 32 | logLevelStr := level 33 | if debug { 34 | logLevelStr = "debug" 35 | 36 | log.Warnf("Debug flag is deprecated, please consider using --loglevel=DEBUG instead") 37 | } 38 | 39 | logLevel, err := parseLogLevel(logLevelStr) 40 | if err != nil { 41 | return log, err 42 | } 43 | 44 | log.SetLevel(logLevel) 45 | 46 | logFormat, err := parseLogFormat(format) 47 | if err != nil { 48 | return log, err 49 | } 50 | 51 | log.SetFormatter(logFormat) 52 | 53 | return log, nil 54 | } 55 | -------------------------------------------------------------------------------- /cmd/prepare/prepare.go: -------------------------------------------------------------------------------- 1 | package prepare 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os/signal" 7 | "syscall" 8 | 9 | "github.com/traefik/mesh/cmd" 10 | "github.com/traefik/mesh/pkg/dns" 11 | "github.com/traefik/mesh/pkg/k8s" 12 | "github.com/traefik/paerser/cli" 13 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 14 | ) 15 | 16 | // NewCmd builds a new Prepare command. 17 | func NewCmd(pConfig *cmd.PrepareConfiguration, loaders []cli.ResourceLoader) *cli.Command { 18 | return &cli.Command{ 19 | Name: "prepare", 20 | Description: `Prepare command.`, 21 | Configuration: pConfig, 22 | Run: func(_ []string) error { 23 | return prepareCommand(pConfig) 24 | }, 25 | Resources: loaders, 26 | } 27 | } 28 | 29 | func prepareCommand(pConfig *cmd.PrepareConfiguration) error { 30 | ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT) 31 | defer cancel() 32 | 33 | log, err := cmd.NewLogger(pConfig.LogFormat, pConfig.LogLevel, pConfig.Debug) 34 | if err != nil { 35 | return fmt.Errorf("could not create logger: %w", err) 36 | } 37 | 38 | log.Debug("Starting prepare...") 39 | log.Debugf("Using masterURL: %q", pConfig.MasterURL) 40 | log.Debugf("Using kubeconfig: %q", pConfig.KubeConfig) 41 | 42 | client, err := k8s.NewClient(log, pConfig.MasterURL, pConfig.KubeConfig) 43 | if err != nil { 44 | return fmt.Errorf("unable to create kubernetes client: %w", err) 45 | } 46 | 47 | dnsClient := dns.NewClient(log, client.KubernetesClient()) 48 | 49 | if pConfig.SMI { 50 | log.Warnf("SMI mode is deprecated, please consider using --acl instead") 51 | } 52 | 53 | aclEnabled := pConfig.ACL || pConfig.SMI 54 | 55 | log.Debugf("ACL mode enabled: %t", aclEnabled) 56 | 57 | if err = k8s.CheckSMIVersion(client.KubernetesClient(), aclEnabled); err != nil { 58 | return fmt.Errorf("unsupported SMI version: %w", err) 59 | } 60 | 61 | var dnsProvider dns.Provider 62 | 63 | dnsProvider, err = dnsClient.CheckDNSProvider(ctx) 64 | if err != nil { 65 | return fmt.Errorf("unable to find suitable DNS provider: %w", err) 66 | } 67 | 68 | switch dnsProvider { 69 | case dns.CoreDNS: 70 | if err := dnsClient.ConfigureCoreDNS(ctx, metav1.NamespaceSystem, pConfig.ClusterDomain, pConfig.Namespace); err != nil { 71 | return fmt.Errorf("unable to configure CoreDNS: %w", err) 72 | } 73 | case dns.KubeDNS: 74 | if err := dnsClient.ConfigureKubeDNS(ctx, pConfig.ClusterDomain, pConfig.Namespace); err != nil { 75 | return fmt.Errorf("unable to configure KubeDNS: %w", err) 76 | } 77 | } 78 | 79 | return nil 80 | } 81 | -------------------------------------------------------------------------------- /cmd/version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "runtime" 8 | 9 | "github.com/traefik/mesh/pkg/version" 10 | "github.com/traefik/paerser/cli" 11 | ) 12 | 13 | const versionFormat = ` 14 | version : %s 15 | commit : %s 16 | build date : %s 17 | go version : %s 18 | go compiler : %s 19 | platform : %s/%s 20 | ` 21 | 22 | // NewCmd builds a new Version command. 23 | func NewCmd() *cli.Command { 24 | return &cli.Command{ 25 | Name: "version", 26 | Description: `Shows the current Traefik Mesh version.`, 27 | Configuration: nil, 28 | Run: func(_ []string) error { 29 | return printVersion(os.Stdout) 30 | }, 31 | } 32 | } 33 | 34 | func printVersion(w io.Writer) error { 35 | _, err := io.WriteString(w, fmt.Sprintf( 36 | versionFormat, 37 | version.Version, 38 | version.Commit, 39 | version.Date, 40 | runtime.Version(), 41 | runtime.Compiler, 42 | runtime.GOOS, 43 | runtime.GOARCH, 44 | )) 45 | 46 | return err 47 | } 48 | -------------------------------------------------------------------------------- /docs/.dockerignore: -------------------------------------------------------------------------------- 1 | site/ 2 | -------------------------------------------------------------------------------- /docs/.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "no-hard-tabs": false, 3 | "MD007": { "indent": 4 }, 4 | "MD009": false, 5 | "MD013": false, 6 | "MD024": false, 7 | "MD025": false, 8 | "MD026": false, 9 | "MD033": false, 10 | "MD034": false, 11 | "MD036": false, 12 | "MD046": false 13 | } 14 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | ####### 2 | # This Makefile contains all targets related to the documentation. 3 | ####### 4 | 5 | DOCS_VERIFY_SKIP ?= false 6 | DOCS_LINT_SKIP ?= false 7 | 8 | TRAEFIK_MESH_DOCS_BUILD_IMAGE ?= traefik-mesh-docs 9 | TRAEFIK_MESH_DOCS_CHECK_IMAGE ?= $(TRAEFIK_MESH_DOCS_BUILD_IMAGE)-check 10 | 11 | SITE_DIR := $(CURDIR)/site 12 | 13 | DOCKER_RUN_DOC_PORT := 8000 14 | DOCKER_RUN_DOC_MOUNTS := -v $(CURDIR):/mkdocs 15 | DOCKER_RUN_DOC_OPTS := --rm $(DOCKER_RUN_DOC_MOUNTS) -p $(DOCKER_RUN_DOC_PORT):8000 16 | 17 | # Default: generates the documentation into $(SITE_DIR) 18 | docs: clean image lint build verify 19 | 20 | # Writer Mode: build and serve docs on http://localhost:8000 with livereload 21 | serve: image 22 | docker run $(DOCKER_RUN_DOC_OPTS) $(TRAEFIK_MESH_DOCS_BUILD_IMAGE) mkdocs serve 23 | 24 | # Utilities Targets for each step 25 | image: 26 | docker build -t $(TRAEFIK_MESH_DOCS_BUILD_IMAGE) -f docs.Dockerfile ./ 27 | 28 | build: image 29 | docker run $(DOCKER_RUN_DOC_OPTS) $(TRAEFIK_MESH_DOCS_BUILD_IMAGE) sh -c "mkdocs build \ 30 | && chown -R $(shell id -u):$(shell id -g) ./site" 31 | 32 | verify: build 33 | @if [ "$(DOCS_VERIFY_SKIP)" != "true" ]; then \ 34 | docker build -t $(TRAEFIK_MESH_DOCS_CHECK_IMAGE) -f check.Dockerfile ./; \ 35 | docker run --rm -v $(CURDIR):/app $(TRAEFIK_MESH_DOCS_CHECK_IMAGE) /verify.sh; \ 36 | else \ 37 | echo "DOCS_VERIFY_SKIP is true: no verification done."; \ 38 | fi 39 | 40 | lint: 41 | @if [ "$(DOCS_LINT_SKIP)" != "true" ]; then \ 42 | docker build -t $(TRAEFIK_MESH_DOCS_CHECK_IMAGE) -f check.Dockerfile ./ && \ 43 | docker run --rm -v $(CURDIR):/app $(TRAEFIK_MESH_DOCS_CHECK_IMAGE) /lint.sh; \ 44 | else \ 45 | echo "DOCS_LINT_SKIP is true: no linting done."; \ 46 | fi 47 | 48 | clean: 49 | rm -rf $(SITE_DIR) 50 | 51 | .PHONY: all verify docs clean build lint 52 | -------------------------------------------------------------------------------- /docs/check.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.15 2 | 3 | # The "build-dependencies" virtual package provides build tools for html-proofer installation. 4 | # It compile ruby-nokogiri, because alpine native version is always out of date 5 | # This virtual package is cleaned at the end. 6 | RUN apk --no-cache --no-progress add \ 7 | libcurl \ 8 | ruby \ 9 | ruby-bigdecimal \ 10 | ruby-etc \ 11 | ruby-ffi \ 12 | ruby-json \ 13 | ruby-nokogiri \ 14 | ruby-dev \ 15 | build-base 16 | 17 | RUN gem install --no-document html-proofer -v 3.19.0 -- --use-system-libraries 18 | 19 | # After Ruby, some NodeJS YAY! 20 | RUN apk --no-cache --no-progress add \ 21 | git \ 22 | nodejs \ 23 | npm 24 | 25 | # To handle 'not get uid/gid' 26 | RUN npm config set unsafe-perm true 27 | 28 | RUN npm install --global \ 29 | markdownlint@0.23.1 \ 30 | markdownlint-cli@0.28.1 31 | 32 | # Finally the shell tools we need for later 33 | # tini helps to terminate properly all the parallelized tasks when sending CTRL-C 34 | RUN apk --no-cache --no-progress add \ 35 | ca-certificates \ 36 | curl \ 37 | tini 38 | 39 | COPY ./scripts/verify.sh /verify.sh 40 | COPY ./scripts/lint.sh /lint.sh 41 | 42 | WORKDIR /app 43 | VOLUME ["/tmp","/app"] 44 | 45 | ENTRYPOINT ["/sbin/tini","-g","sh"] 46 | -------------------------------------------------------------------------------- /docs/content/api.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Traefik Mesh API" 3 | description: "Traefik Mesh includes a built-in API that can be used for debugging purposes. Read the documentation to learn more." 4 | --- 5 | 6 | # API 7 | 8 | Traefik Mesh includes a built-in API that can be used for debugging purposes. 9 | This can be useful when Traefik Mesh is not working as intended. 10 | The API is accessed via the controller pod, and for security reasons is not exposed via service. 11 | The API can be accessed by making a `GET` request to `http://:9000` combined with one of the following paths: 12 | 13 | ## `/api/configuration/current` 14 | 15 | This endpoint provides raw json of the current configuration built by the controller. 16 | 17 | !!! Note 18 | This may change on each request, as it is a live data structure. 19 | 20 | ## `/api/status/nodes` 21 | 22 | This endpoint provides a json array containing some details about the readiness of the Traefik Mesh nodes visible by the controller. 23 | This endpoint will still return a 200 if there are no visible nodes. 24 | 25 | ## `/api/status/node/{traefik-mesh-pod-name}/configuration` 26 | 27 | This endpoint provides raw json of the current configuration on the Traefik Mesh node with the pod name given in `{traefik-mesh-pod-name}`. 28 | This endpoint provides a 404 response if the pod cannot be found, or other non-200 status codes on other errors. 29 | If errors are encountered, the error will be returned in the body, and logged on the controller. 30 | 31 | ## `/api/status/readiness` 32 | 33 | This endpoint returns a 200 response if the controller has successfully started. 34 | Otherwise, it will return a 500. 35 | -------------------------------------------------------------------------------- /docs/content/assets/img/after-traefik-mesh-graphic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/traefik/mesh/5ee481aca18c81bd710a8c15cbdf75d3d599bea1/docs/content/assets/img/after-traefik-mesh-graphic.png -------------------------------------------------------------------------------- /docs/content/assets/img/before-traefik-mesh-graphic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/traefik/mesh/5ee481aca18c81bd710a8c15cbdf75d3d599bea1/docs/content/assets/img/before-traefik-mesh-graphic.png -------------------------------------------------------------------------------- /docs/content/assets/img/smi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/traefik/mesh/5ee481aca18c81bd710a8c15cbdf75d3d599bea1/docs/content/assets/img/smi.png -------------------------------------------------------------------------------- /docs/content/assets/img/traefik-mesh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/traefik/mesh/5ee481aca18c81bd710a8c15cbdf75d3d599bea1/docs/content/assets/img/traefik-mesh.png -------------------------------------------------------------------------------- /docs/content/assets/js/extra.js: -------------------------------------------------------------------------------- 1 | /* Highlight */ 2 | (function(hljs) { 3 | hljs.initHighlightingOnLoad(); 4 | })(hljs); -------------------------------------------------------------------------------- /docs/content/assets/js/hljs/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2006, Ivan Sagalaev 2 | All rights reserved. 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of highlight.js nor the names of its contributors 12 | may be used to endorse or promote products derived from this software 13 | without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY 16 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /docs/content/compatibility.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Traefik Mesh Compatibility" 3 | description: "Traefik Mesh supports, similar to Kubernetes, at least the latest three minor versions of Kubernetes. Read the documentation to learn more." 4 | --- 5 | 6 | # Compatibility 7 | 8 | Traefik Mesh supports, [similar to Kubernetes](https://kubernetes.io/docs/setup/release/version-skew-policy/#supported-versions), at least the latest three minor versions of Kubernetes, therefore currently: 9 | 10 | * 1.21 11 | * 1.22 12 | * 1.23 13 | 14 | General functionality cannot be guaranted for versions older than that. However, we expect it to work with Kubernetes down to 1.11 currently. 15 | 16 | ## Compatibility by Features 17 | 18 | Some of Traefik Mesh's features are only supported on certain Kubernetes versions. 19 | Please see the table below. 20 | 21 | | Features | K8s 1.21 | K8s 1.22 | K8s 1.23 | 22 | |-----------------------|----------|----------|----------| 23 | | General functionality | ✔ | ✔ | ✔ | 24 | | Service Topology | ✔ | - | - | 25 | 26 | !!! warning "Service Topology" 27 | 28 | In Kubernetes `v1.22`, the experimental Service Topology feature was removed. 29 | Therefore, starting from Traefik Mesh `v1.4.5`, the support of this feature has been removed. 30 | 31 | ## SMI Specification support 32 | 33 | Traefik Mesh is based on the latest version of the SMI specification: 34 | 35 | | API Group | API Version | 36 | |--------------------|-------------------------------------------------------------------------------------------------------------------------| 37 | | access.smi-spec.io | [v1alpha2](https://github.com/servicemeshinterface/smi-spec/blob/master/apis/traffic-access/v1alpha2/traffic-access.md) | 38 | | specs.smi-spec.io | [v1alpha3](https://github.com/servicemeshinterface/smi-spec/blob/master/apis/traffic-specs/v1alpha3/traffic-specs.md) | 39 | | split.smi-spec.io | [v1alpha3](https://github.com/servicemeshinterface/smi-spec/blob/master/apis/traffic-split/v1alpha3/traffic-split.md) | 40 | -------------------------------------------------------------------------------- /docs/content/contributing/documentation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Traefik Mesh Documentation" 3 | description: "You've found something unclear in the documentation and want to give a try at explaining it better? Learn how you can do so in this article." 4 | --- 5 | 6 | # Documentation 7 | 8 | You've found something unclear in the documentation and want to give a try at explaining it better? 9 | Let's see how. 10 | 11 | ## Building 12 | 13 | This [documentation](https://doc.traefik.io/traefik-mesh/) is built with [MkDocs](https://mkdocs.org/). 14 | 15 | ### With `Docker` and `make` 16 | 17 | You can build the documentation and test it locally (with live reloading), using the `serve` target: 18 | 19 | ```bash 20 | $ make serve 21 | docker build -t traefik-mesh-docs -f docs.Dockerfile ./ 22 | # […] 23 | docker run --rm -v /home/user/traefik-mesh/docs:/mkdocs -p 8000:8000 traefik-mesh-docs mkdocs serve 24 | # […] 25 | INFO - Building documentation... 26 | INFO - Cleaning site directory 27 | [I 200408 14:36:33 server:296] Serving on http://0.0.0.0:8000 28 | [I 200408 14:36:33 handlers:62] Start watching changes 29 | [I 200408 14:36:33 handlers:64] Start detecting changes 30 | ``` 31 | 32 | !!! Note 33 | By default, the local documentation server listens on [http://127.0.0.1:8000](http://127.0.0.1:8000). 34 | To build the documentation without serving it locally, use the `build` target. 35 | 36 | ### With `MkDocs` 37 | 38 | First, make sure you have `python` and `pip` installed. MkDocs supports `python` versions `2.7.9+`, `3.4`, `3.5`, `3.6` 39 | and `3.7`. 40 | 41 | ```bash 42 | $ python --version 43 | Python 2.7.14 44 | 45 | $ pip --version 46 | pip 19.3.1 from /usr/local/lib/python2.7/site-packages/pip (python 2.7) 47 | ``` 48 | 49 | Then, install MkDocs with `pip`. 50 | 51 | ```bash 52 | pip install --user -r requirements.txt 53 | ``` 54 | 55 | To build the documentation and serve it locally, run `mkdocs serve` from the root directory. 56 | This starts a local server, and exposes the documentation on `http://127.0.0.1:8000`: 57 | 58 | ```bash 59 | $ mkdocs serve 60 | INFO - Building documentation... 61 | INFO - Cleaning site directory 62 | [I 160505 22:31:24 server:281] Serving on http://127.0.0.1:8000 63 | [I 160505 22:31:24 handlers:59] Start watching changes 64 | [I 160505 22:31:24 handlers:61] Start detecting changes 65 | ``` 66 | 67 | ## Checking 68 | 69 | To check that the documentation meets standard expectations (no dead links, html markup validity, ...), use the `verify` target. 70 | If you've made changes to the documentation, it's safer to clean it before verifying it. 71 | 72 | ```bash 73 | $ make clean verify 74 | docker build -t traefik-mesh-docs -f docs.Dockerfile ./ 75 | # […] 76 | docker run --rm -v /home/user/traefik-mesh/docs:/mkdocs -p 8000:8000 traefik-mesh-docs sh -c "mkdocs build && chown -R 501:20 ./site" 77 | === Checking HTML content... 78 | # […] 79 | ``` 80 | 81 | !!! Note "Disabling Verification" 82 | Verification can be disabled by setting the environment variable `DOCS_VERIFY_SKIP` to `true`: 83 | 84 | ```bash 85 | $ DOCS_VERIFY_SKIP=true make verify 86 | # […] 87 | DOCS_VERIFY_SKIP is true: no verification done. 88 | ``` 89 | -------------------------------------------------------------------------------- /docs/content/contributing/maintainers.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Traefik Mesh Maintainers" 3 | description: "In this article, you can find the list of Traefik Mesh maintainers." 4 | --- 5 | 6 | # Maintainers 7 | 8 | - Daniel Tomcej [@dtomcej](https://github.com/dtomcej) 9 | - Manuel Zapf [@SantoDE](https://github.com/SantoDE) 10 | - Michaël Matur [@mmatur](https://github.com/mmatur) 11 | - Landry Benguigui [@LandryBe](https://github.com/LandryBe) 12 | - Harold Ozouf [@jspdown](https://github.com/jspdown) 13 | - Julien Levesy [@jlevesy](https://github.com/jlevesy) 14 | - Brendan Le Glaunec [@Ullaakut](https://github.com/Ullaakut) 15 | - Kevin Pollet [@kevinpollet](https://github.com/kevinpollet) 16 | -------------------------------------------------------------------------------- /docs/content/contributing/submitting-issues.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Submitting Issues in Traefik Mesh" 3 | description: "This documentation article describes the process of sorting and checking issues in Traefik Mesh." 4 | --- 5 | 6 | # Submitting Issues 7 | 8 | We use the [GitHub issue tracker](https://github.com/traefik/mesh/issues) to keep track of Traefik Mesh issues. 9 | 10 | The process of sorting and checking the issues requires a lot of work. To save us some time and get quicker feedback, 11 | be sure to follow the guidelines below. 12 | 13 | !!! Important "Getting Help" 14 | The issue tracker is not a general support forum, but a place to report bugs and asks for new features. 15 | For end-user related support questions, use the [Traefik Mesh community forum](https://community.traefik.io/c/traefik-mesh). 16 | 17 | ## Issue Title 18 | 19 | The title must be short and descriptive. (~60 characters) 20 | 21 | ## Description 22 | 23 | Follow the [issue template](https://github.com/traefik/mesh/blob/master/.github/ISSUE_TEMPLATE/) as much as possible. 24 | 25 | Explain in which context you encountered the issue. 26 | 27 | Remain clear and concise. 28 | 29 | Take time to polish the format of your message so we'll enjoy reading it and working on it. Help the readers focus on 30 | what matters, and help them understand the structure of your message (see the [GitHub Markdown Syntax](https://docs.github.com/en/get-started/writing-on-github)). 31 | 32 | ## Feature Request 33 | 34 | Remember, when asking for new features, they must be useful to the majority (and not only useful in edge case scenarios, or hack-like setups). 35 | 36 | Do your best to explain what you're looking for, and why it would improve Traefik Mesh for everyone. 37 | 38 | ## International English 39 | 40 | Traefik Mesh maintainers/users are not all native English speakers, so if you sometimes feel that some messages sound rude, 41 | remember that it's probably a language barrier problem from someone willing to help you. 42 | -------------------------------------------------------------------------------- /docs/content/contributing/submitting-pull-requests.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Submitting Pull Requests for Traefik Mesh" 3 | description: "This documentation article describes the process of submitting a pull request for Traefik Mesh." 4 | --- 5 | 6 | # Submitting Pull Requests 7 | 8 | So you've decided to improve Traefik Mesh? Thank You! Now the last step is to submit your Pull Request in a way that makes sure 9 | it gets the attention it deserves. 10 | 11 | Let's go through the classic pitfalls to make sure everything is right. 12 | 13 | ## Title 14 | 15 | The title must be short and descriptive. (~60 characters) 16 | 17 | ## Description 18 | 19 | Follow the [pull request template](https://github.com/traefik/mesh/blob/master/.github/PULL_REQUEST_TEMPLATE.md) 20 | as much as possible. 21 | 22 | Explain the conditions which led you to write this PR: give us context. The context should lead to something, an idea or 23 | a problem that you’re facing. 24 | 25 | Remain clear and concise. 26 | 27 | Take time to polish the format of your message so we'll enjoy reading it and working on it. Help the readers focus on 28 | what matters, and help them understand the structure of your message (see the [GitHub Markdown Syntax](https://docs.github.com/en/get-started/writing-on-github)). 29 | 30 | ## Content 31 | 32 | - Make it small. 33 | - Each PR should be linked to an issue. 34 | - One feature per PR. 35 | - PRs should be standalone (they should not depend on an upcoming PR). 36 | - Write useful descriptions and titles. 37 | - Avoid re-formatting code that is not on the path of your PR. 38 | - Commits should be split properly (in order to guide reviewers through the code). 39 | - Make sure the [code builds](building-testing.md). 40 | - Make sure [all tests pass](building-testing.md). 41 | - Add tests. 42 | - Address review comments in terms of additional commits (don't amend/squash existing ones unless the PR is trivial). 43 | -------------------------------------------------------------------------------- /docs/content/contributing/thank-you.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Traefik Mesh Feedback and Contributions" 3 | description: "Traefik Mesh is an open-source project, your feedback and contributions are needed and always welcome! Read the docs to learn how you can contribute." 4 | --- 5 | 6 | # Thank You! 7 | 8 | Traefik Mesh is an [open-source project](https://github.com/traefik/mesh/), your feedback and contributions are needed and 9 | always welcome! 10 | 11 | !!! Question "Where to Go Next?" 12 | If you want to: 13 | 14 | - Propose and idea, request a feature a report a bug, 15 | read the page [Submitting Issues](./submitting-issues.md). 16 | - Discover how to make an efficient contribution, 17 | read the page [Submitting Pull Requests](./submitting-pull-requests.md). 18 | - Learn how to build and test Traefik Mesh, 19 | the page [Building and Testing](./building-testing.md) is for you. 20 | - Contribute to the documentation, 21 | read the related page [Documentation](./documentation.md). 22 | -------------------------------------------------------------------------------- /docs/content/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Traefik Mesh: Simpler Service Mesh" 3 | description: "Traefik Mesh is a lightweight and simpler service mesh designed from the ground up to be straightforward, easy to install and use. Read the docs to learn more." 4 | --- 5 | 6 | # Traefik Mesh: Simpler Service Mesh 7 | 8 |

9 | Traefik Mesh 10 |

11 | 12 | Traefik Mesh is a lightweight and simpler service mesh designed from the ground up to be straightforward, easy to install and easy to use. 13 | 14 | Built on top of Traefik, Traefik Mesh fits as your de-facto service mesh in your Kubernetes cluster supporting the latest Service Mesh Interface specification (SMI). 15 | 16 | Moreover, Traefik Mesh is opt-in by default, which means that your existing services are unaffected until you decide to add them to the mesh. 17 | 18 |

19 | SMI 20 |

21 | 22 | ## Non-Invasive Service Mesh 23 | 24 | Traefik Mesh does not use any sidecar container but handles routing through proxy endpoints running on each node. 25 | The mesh controller runs in a dedicated pod and handles all the configuration parsing and deployment to the proxy nodes. 26 | Traefik Mesh supports multiple configuration options: annotations on user service objects, and SMI objects. 27 | Not using sidecars means that Traefik Mesh does not modify your Kubernetes objects and does not modify your traffic without your knowledge. 28 | Using the Traefik Mesh endpoints is all that is required. 29 | 30 |

31 | Traefik Mesh 32 | Traefik Mesh 33 |

34 | 35 | ## Prerequisites 36 | 37 | To run this app, you require the following: 38 | 39 | - Kubernetes 1.11+ 40 | - CoreDNS/KubeDNS installed as [Cluster DNS Provider](https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/) (versions 1.3+ supported) 41 | - Helm v3 42 | -------------------------------------------------------------------------------- /docs/content/migration/helm-chart.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Traefik Helm Chart Documentation" 3 | description: "Migrate to the latest version of Traefik Mesh, a simple and lightweight service mesh, in your cluster's Helm chart. Read the technical documentation." 4 | --- 5 | 6 | # Migrations 7 | 8 | Helm Chart 9 | {: .subtitle } 10 | 11 | ## v2.1 to v3.0 12 | 13 | ### Traefik Mesh renaming 14 | 15 | All existing resources have been renamed and prefixed by `traefik-mesh`. 16 | For example, the `maesh-controller`resource has been renamed to `traefik-mesh-controller`. 17 | 18 | ### Mesh Property Name 19 | 20 | Inside the Traefik Mesh helm chart, the `mesh` property has been renamed to `proxy`. 21 | 22 | ### Image version 23 | 24 | Since version `v1.4`, Traefik Mesh uses plain [Traefik](https://github.com/traefik/traefik/) Docker image for proxies. 25 | Therefore, to change the image version used for the controller, or the proxies you should use the `controller.image` and `proxy.image` options. 26 | 27 | ### Default Mode 28 | 29 | The `mesh.defaultMode` option has been removed. 30 | You should use the new `defaultMode` option to configure the default traffic mode. 31 | 32 | ## v2.0 to v2.1 33 | 34 | ### Default Mode 35 | 36 | The `mesh.defaultMode` option has been deprecated and will be removed in a future major release. 37 | You should use the new `defaultMode` option to configure the default traffic mode. 38 | 39 | ### Prometheus and Grafana services 40 | 41 | Prior to version `v2.1`, when the Metrics chart is deployed, Prometheus and Grafana services are exposed by default through a `NodePort`. 42 | For security reasons, those services are not exposed by default anymore. 43 | To expose them you should use the new `prometheus.service` and `grafana.service` options, more details in the corresponding [values.yaml](https://github.com/traefik/mesh-helm-chart/blob/8a7a193a1718129ad6e02ff313b219029d6daffe/mesh/charts/metrics/values.yaml). 44 | 45 | ## v1.x to v2.0 46 | 47 | ### Image version 48 | 49 | Since version `v1.2`, [Traefik](https://github.com/traefik/traefik/) is used as a library. 50 | Therefore, the `controller.image` and `mesh.image` options have been removed. 51 | You should use the new `image` option as described in the [documentation](../install.md#deploy-helm-chart). 52 | 53 | ### Log Level 54 | 55 | The `controller.logging.debug` and `mesh.logging` options have been removed. 56 | You should use the new `controller.logLevel` and `mesh.logLevel` options to configure the logging level for the controller and proxies. 57 | 58 | ### SMI Mode 59 | 60 | The `smi.enable` option has been deprecated and removed. 61 | You should use the new and backward compatible ACL mode option as described in the [documentation](../install.md#access-control-list). 62 | -------------------------------------------------------------------------------- /docs/content/migration/traefik-mesh-v1.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Traefik Mesh Migrations Documentation" 3 | description: "Traefik Mesh v1.4 introduced a few key changes. Learn what those changes are and what actionable steps must be taken in the technical documentation." 4 | --- 5 | 6 | # Minor Migrations 7 | 8 | Traefik Mesh v1 9 | {: .subtitle } 10 | 11 | ## Traefik Mesh v1.4 12 | 13 | Maesh has been renamed to Traefik Mesh in an effort to rename all of our products and make them look closer. 14 | Through this renaming process, a couple of things changed which might be worth mentioning for a migration process. 15 | All areas that changed are mentioned below with the appropriate actions needed to do. 16 | 17 | Everything called Maesh references to `v1.3`, while Traefik Mesh refers to `v1.4`. 18 | 19 | ### Environment Variable Prefix 20 | 21 | Prior to Traefik Mesh, environment variables were prefixed with `MAESH_`. 22 | Now they're prefixed with `TRAEFIK_MESH_` and the `MAESH_` prefix is deprecated. 23 | You need to decide on either using `MAESH_` or `TRAEFIK_MESH_` as mixing both will result in an error. 24 | 25 | ### Configuration File Name 26 | 27 | The default configuration file name is changed from `maesh` to `traefik-mesh` as well. 28 | 29 | ### DNS Name 30 | 31 | The well known internal DNS name, to opt in into the usage of Maesh was `.maesh`. 32 | Now, with the rebranding process this has been changed to `traefik.mesh` and thus, you now need to use the DNS name of `servicename.servicenamespace.traefik.maesh` to opt-in into the usage of Traefik Mesh. 33 | The old name `.maesh`, is deprecated and will be removed eventually. 34 | 35 | ### Docker Image Name 36 | 37 | As part of the process, the docker-image has been moved from `containous/maesh` to `traefik/mesh`. 38 | The old image will not be updated anymore and `traefik/mesh` starts with `v1.4.0`. 39 | 40 | ### Binary 41 | 42 | The new binary name is `traefik-mesh`, rather than `maesh` before. 43 | However, as Traefik Mesh is running inside k8s this change should not be critical as it's hidden by the docker-image name. 44 | 45 | ### Annotations 46 | 47 | As part of the rebranding process, the annotation prefix has changed. 48 | The annotation prefix `maesh.containo.us/` has been deprecated in favour of `mesh.traefik.io`. 49 | 50 | ## v1.1 to v1.2 51 | 52 | ### Debug 53 | 54 | The `--debug` CLI flag is deprecated and will be removed in a future major release. 55 | Instead, you should use the new `--logLevel` flag with `debug` as value. 56 | 57 | ### SMI Mode 58 | 59 | The `--smi` CLI flag is deprecated and will be removed in a future major release. 60 | Instead, you should use the new and backward compatible `--acl` flag. 61 | -------------------------------------------------------------------------------- /docs/docs.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.15 2 | 3 | ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/.local/bin 4 | 5 | COPY requirements.txt /mkdocs/ 6 | WORKDIR /mkdocs 7 | VOLUME /mkdocs 8 | 9 | RUN apk --no-cache --no-progress add py3-pip gcc musl-dev python3-dev \ 10 | && pip3 install --user -r requirements.txt 11 | -------------------------------------------------------------------------------- /docs/mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Traefik Mesh 2 | site_description: Traefik Mesh Documentation 3 | site_author: traefik.io 4 | site_url: https://doc.traefik.io/traefik-mesh 5 | dev_addr: 0.0.0.0:8000 6 | 7 | repo_name: 'GitHub' 8 | repo_url: 'https://github.com/traefik/mesh' 9 | 10 | docs_dir: 'content' 11 | edit_uri: 'edit/master/docs/content/' 12 | 13 | product: mesh 14 | 15 | # https://squidfunk.github.io/mkdocs-material/ 16 | theme: 17 | name: 'traefik-labs' 18 | language: en 19 | include_sidebar: true 20 | favicon: assets/img/traefik-mesh-logo.svg 21 | logo: assets/img/traefik-mesh-logo.svg 22 | feature: 23 | tabs: false 24 | palette: 25 | primary: 'white' 26 | accent: '#9D0EB0' 27 | i18n: 28 | prev: 'Previous' 29 | next: 'Next' 30 | 31 | copyright: "Copyright © 2020-2022 Traefik Labs" 32 | 33 | extra_javascript: 34 | - assets/js/hljs/highlight.pack.js # Download from https://highlightjs.org/download/ and enable YAML, TOML and Dockerfile 35 | - assets/js/extra.js 36 | 37 | plugins: 38 | - search 39 | 40 | # https://squidfunk.github.io/mkdocs-material/extensions/admonition/ 41 | # https://facelessuser.github.io/pymdown-extensions/ 42 | markdown_extensions: 43 | - meta 44 | - attr_list 45 | - admonition 46 | - footnotes 47 | - pymdownx.details 48 | - pymdownx.inlinehilite 49 | - pymdownx.highlight: 50 | use_pygments: false # hljs is used instead of pygment for TOML highlighting support 51 | - pymdownx.smartsymbols 52 | - pymdownx.superfences 53 | - pymdownx.tasklist 54 | - pymdownx.snippets: 55 | check_paths: true 56 | - markdown_include.include: 57 | base_path: content/includes/ 58 | encoding: utf-8 59 | - toc: 60 | permalink: true 61 | 62 | # Page tree 63 | nav: 64 | - 'Welcome': 'index.md' 65 | - 'Quickstart': 'quickstart.md' 66 | - 'Installation': 'install.md' 67 | - 'Configuration': 'configuration.md' 68 | - 'Compatibility': 'compatibility.md' 69 | - 'Examples': 'examples.md' 70 | - 'API': 'api.md' 71 | - 'Migration': 72 | - 'Traefik Mesh v1': 'migration/traefik-mesh-v1.md' 73 | - 'Helm Chart': 'migration/helm-chart.md' 74 | - 'Contributing': 75 | - 'Thank You!': 'contributing/thank-you.md' 76 | - 'Submitting Issues': 'contributing/submitting-issues.md' 77 | - 'Submitting Pull Requests': 'contributing/submitting-pull-requests.md' 78 | - 'Building and Testing': 'contributing/building-testing.md' 79 | - 'Documentation': 'contributing/documentation.md' 80 | - 'Maintainers': 'contributing/maintainers.md' 81 | -------------------------------------------------------------------------------- /docs/readme.md: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | ## Tooling 4 | 5 | | Tool | Documentation | Sources | 6 | |-------------------|-------------------------------------|-----------------------------------| 7 | | mkdocs | [documentation][mkdocs] | [Sources][mkdocs-src] | 8 | | mkdocs-material | [documentation][mkdocs-material] | [Sources][mkdocs-material-src] | 9 | | pymdown-extensions| [documentation][pymdown-extensions] | [Sources][pymdown-extensions-src] | 10 | 11 | 12 | [mkdocs]: https://www.mkdocs.org "Mkdocs" 13 | [mkdocs-src]: https://github.com/mkdocs/mkdocs "Mkdocs - Sources" 14 | 15 | [mkdocs-material]: https://squidfunk.github.io/mkdocs-material/ "Material for MkDocs" 16 | [mkdocs-material-src]: https://github.com/squidfunk/mkdocs-material "Material for MkDocs - Sources" 17 | 18 | [pymdown-extensions]: https://facelessuser.github.io/pymdown-extensions/extensions "PyMdown Extensions" 19 | [pymdown-extensions-src]: https://github.com/facelessuser/pymdown-extensions "PyMdown Extensions - Sources" 20 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | appdirs==1.4.4 2 | CacheControl==0.12.10 3 | certifi==2020.12.5 4 | charset-normalizer==2.0.7 5 | click==8.1.2 6 | colorama==0.4.4 7 | contextlib2==21.6.0 8 | distlib==0.3.3 9 | distro==1.6.0 10 | ghp-import==2.0.2 11 | html5lib==1.1 12 | idna==3.3 13 | importlib-metadata==4.11.3 14 | Jinja2==3.0.0 15 | lockfile==0.12.2 16 | Markdown==3.3.6 17 | markdown-include==0.5.1 18 | MarkupSafe==2.1.1 19 | mergedeep==1.3.4 20 | mkdocs==1.2.2 21 | mkdocs-bootswatch==1.0 22 | mkdocs-material-extensions==1.0.3 23 | mkdocs-traefiklabs==100.0.10 24 | msgpack==1.0.2 25 | ordered-set==4.0.2 26 | packaging==20.9 27 | pep517==0.12.0 28 | progress==1.6 29 | Pygments==2.11.2 30 | pymdown-extensions==7.0 31 | pyparsing==2.4.7 32 | python-dateutil==2.8.2 33 | PyYAML==6.0 34 | pyyaml-env-tag==0.1 35 | requests==2.26.0 36 | retrying==1.3.3 37 | six==1.16.0 38 | toml==0.10.2 39 | tomli==1.2.2 40 | urllib3==1.26.7 41 | watchdog==2.1.7 42 | webencodings==0.5.1 43 | zipp==3.8.0 44 | -------------------------------------------------------------------------------- /docs/runtime.txt: -------------------------------------------------------------------------------- 1 | 3.7 2 | -------------------------------------------------------------------------------- /docs/scripts/lint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This script will run a couple of linter on the documentation 3 | 4 | set -eu 5 | 6 | # We want to run all linters before returning success (exit 0) or failure (exit 1) 7 | # So this variable holds the global exit code 8 | EXIT_CODE=0 9 | readonly BASE_DIR=/app 10 | 11 | echo "== Linting Markdown" 12 | # Uses the file ".markdownlint.json" for setup 13 | cd "${BASE_DIR}" || exit 1 14 | 15 | LINTER_EXCLUSIONS="$(find "${BASE_DIR}/content" -type f -name '.markdownlint.json')" \ 16 | GLOBAL_LINT_OPTIONS="--config ${BASE_DIR}/.markdownlint.json" 17 | 18 | # Lint the specific folders (containing linter specific rulesets) 19 | for LINTER_EXCLUSION in ${LINTER_EXCLUSIONS} 20 | do 21 | markdownlint --config "${LINTER_EXCLUSION}" "$(dirname "${LINTER_EXCLUSION}")" || EXIT_CODE=1 22 | # Add folder to the ignore list for global lint 23 | GLOBAL_LINT_OPTIONS="${GLOBAL_LINT_OPTIONS} --ignore=$(dirname "${LINTER_EXCLUSION}")" 24 | done 25 | 26 | # Lint all the content, excluding the previously done` 27 | eval markdownlint "${GLOBAL_LINT_OPTIONS}" "${BASE_DIR}/content/**/*.md" || EXIT_CODE=1 28 | 29 | exit "${EXIT_CODE}" 30 | -------------------------------------------------------------------------------- /docs/scripts/netlify-run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This script is run in netlify environment to build and validate 4 | # the website for documentation 5 | 6 | CURRENT_DIR="$(cd "$(dirname "${0}")" && pwd -P)" 7 | 8 | #### Build website 9 | # Provide the URL for this deployment to Mkdocs 10 | echo "${DEPLOY_PRIME_URL}" > "${CURRENT_DIR}/../CNAME" 11 | sed -i "s#site_url:.*#site_url: ${DEPLOY_PRIME_URL}#" "${CURRENT_DIR}/../mkdocs.yml" 12 | 13 | # Build 14 | mkdocs build 15 | 16 | exit 0 -------------------------------------------------------------------------------- /docs/scripts/verify.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | PATH_TO_SITE="${1:-/app/site}" 4 | 5 | set -eu 6 | 7 | [ -d "${PATH_TO_SITE}" ] 8 | 9 | NUMBER_OF_CPUS="$(grep -c processor /proc/cpuinfo)" 10 | 11 | echo "=== Checking HTML content..." 12 | 13 | # Search for all HTML files except the theme's partials 14 | # and pipe this to htmlproofer with parallel threads 15 | # (one htmlproofer per vCPU) 16 | find "${PATH_TO_SITE}" -type f -not -path "/app/site/theme/*" \ 17 | -name "*.html" -print0 \ 18 | | xargs -0 -r -P "${NUMBER_OF_CPUS}" -I '{}' \ 19 | htmlproofer \ 20 | --check-html \ 21 | --check_external_hash \ 22 | --alt_ignore="/traefik-mesh-logo.svg/" \ 23 | --http_status_ignore="0,500,501,503" \ 24 | --url_ignore="/fonts.gstatic.com/,/traefik-mesh/,/github.com\/traefik\/mesh\/edit*/,/pilot.traefik.io\/profile/,/traefik.io/,/doc.traefik.io/,/www.mkdocs.org/,/squidfunk.github.io/,/ietf.org/,/docs.github.com/" \ 25 | '{}' 1>/dev/null 26 | ## HTML-proofer options at https://github.com/gjtorikian/html-proofer#configuration 27 | 28 | echo "= Documentation checked successfully." 29 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/traefik/mesh 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/cenkalti/backoff/v4 v4.1.1 7 | github.com/go-check/check v0.0.0-20180628173108-788fd7840127 8 | github.com/google/uuid v1.3.0 9 | github.com/gorilla/mux v1.8.0 10 | github.com/hashicorp/go-version v1.3.0 11 | github.com/servicemeshinterface/smi-sdk-go v0.4.1 12 | github.com/sirupsen/logrus v1.8.1 13 | github.com/stretchr/testify v1.8.0 14 | github.com/traefik/paerser v0.1.8 15 | github.com/traefik/traefik/v2 v2.8.3 16 | github.com/vdemeester/shakers v0.1.0 17 | k8s.io/api v0.22.5 18 | k8s.io/apimachinery v0.22.5 19 | k8s.io/client-go v0.22.5 20 | ) 21 | 22 | require ( 23 | github.com/BurntSushi/toml v1.1.0 // indirect 24 | github.com/Masterminds/goutils v1.1.1 // indirect 25 | github.com/Masterminds/semver/v3 v3.1.1 // indirect 26 | github.com/Masterminds/sprig/v3 v3.2.2 // indirect 27 | github.com/davecgh/go-spew v1.1.1 // indirect 28 | github.com/evanphx/json-patch v4.11.0+incompatible // indirect 29 | github.com/go-acme/lego/v4 v4.7.0 // indirect 30 | github.com/go-logr/logr v0.4.0 // indirect 31 | github.com/gogo/protobuf v1.3.2 // indirect 32 | github.com/golang/protobuf v1.5.2 // indirect 33 | github.com/google/go-cmp v0.5.7 // indirect 34 | github.com/google/gofuzz v1.2.0 // indirect 35 | github.com/googleapis/gnostic v0.5.5 // indirect 36 | github.com/huandu/xstrings v1.3.1 // indirect 37 | github.com/imdario/mergo v0.3.12 // indirect 38 | github.com/json-iterator/go v1.1.12 // indirect 39 | github.com/mitchellh/copystructure v1.0.0 // indirect 40 | github.com/mitchellh/reflectwalk v1.0.1 // indirect 41 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 42 | github.com/modern-go/reflect2 v1.0.2 // indirect 43 | github.com/patrickmn/go-cache v2.1.0+incompatible // indirect 44 | github.com/pkg/errors v0.9.1 // indirect 45 | github.com/pmezard/go-difflib v1.0.0 // indirect 46 | github.com/shopspring/decimal v1.2.0 // indirect 47 | github.com/spf13/cast v1.3.1 // indirect 48 | github.com/spf13/pflag v1.0.5 // indirect 49 | golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f // indirect 50 | golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e // indirect 51 | golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect 52 | golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e // indirect 53 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect 54 | golang.org/x/text v0.3.7 // indirect 55 | golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect 56 | google.golang.org/appengine v1.6.7 // indirect 57 | google.golang.org/protobuf v1.28.0 // indirect 58 | gopkg.in/inf.v0 v0.9.1 // indirect 59 | gopkg.in/square/go-jose.v2 v2.6.0 // indirect 60 | gopkg.in/yaml.v2 v2.4.0 // indirect 61 | gopkg.in/yaml.v3 v3.0.1 // indirect 62 | k8s.io/klog/v2 v2.10.0 // indirect 63 | k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c // indirect 64 | k8s.io/utils v0.0.0-20210820185131-d34e5cb4466e // indirect 65 | sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect 66 | sigs.k8s.io/yaml v1.2.0 // indirect 67 | ) 68 | 69 | // Containous forks 70 | replace ( 71 | github.com/abbot/go-http-auth => github.com/containous/go-http-auth v0.4.1-0.20200324110947-a37a7636d23e 72 | github.com/go-check/check => github.com/containous/check v0.0.0-20170915194414-ca0bf163426a 73 | github.com/gorilla/mux => github.com/containous/mux v0.0.0-20220627093034-b2dd784e613f 74 | github.com/mailgun/minheap => github.com/containous/minheap v0.0.0-20190809180810-6e71eb837595 75 | ) 76 | -------------------------------------------------------------------------------- /integration/integration_test.go: -------------------------------------------------------------------------------- 1 | package integration 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "log" 7 | "os" 8 | "os/exec" 9 | "testing" 10 | 11 | "github.com/go-check/check" 12 | "github.com/sirupsen/logrus" 13 | ) 14 | 15 | var ( 16 | integration = flag.Bool("integration", false, "run integration tests") 17 | debug = flag.Bool("debug", false, "debug log level") 18 | masterURL = "https://localhost:8443" 19 | k3dClusterName = "traefik-mesh-integration" 20 | traefikMeshNamespace = "traefik-mesh" 21 | traefikMeshBinary = "../dist/traefik-mesh" 22 | smiCRDs = "./testdata/crds/" 23 | testNamespace = "test" 24 | ) 25 | 26 | func Test(t *testing.T) { 27 | if !*integration { 28 | log.Println("Integration tests disabled") 29 | return 30 | } 31 | 32 | if *debug { 33 | logrus.SetLevel(logrus.DebugLevel) 34 | } 35 | 36 | check.Suite(&ACLDisabledSuite{}) 37 | check.Suite(&ACLEnabledSuite{}) 38 | check.Suite(&CoreDNSSuite{}) 39 | check.Suite(&KubeDNSSuite{}) 40 | 41 | check.TestingT(t) 42 | } 43 | 44 | func traefikMeshPrepare() error { 45 | args := []string{ 46 | "prepare", 47 | "--masterurl=" + masterURL, 48 | "--kubeconfig=" + os.Getenv("KUBECONFIG"), 49 | "--loglevel=debug", 50 | "--clusterdomain=cluster.local", 51 | "--namespace=" + traefikMeshNamespace, 52 | } 53 | 54 | cmd := exec.Command(traefikMeshBinary, args...) 55 | cmd.Env = os.Environ() 56 | 57 | output, err := cmd.CombinedOutput() 58 | if err != nil { 59 | return fmt.Errorf("traefik mesh prepare has failed - %s: %w", string(output), err) 60 | } 61 | 62 | return nil 63 | } 64 | -------------------------------------------------------------------------------- /integration/kubedns_test.go: -------------------------------------------------------------------------------- 1 | package integration 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/go-check/check" 7 | "github.com/sirupsen/logrus" 8 | "github.com/traefik/mesh/integration/k3d" 9 | "github.com/traefik/mesh/integration/tool" 10 | "github.com/traefik/mesh/integration/try" 11 | checker "github.com/vdemeester/shakers" 12 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 13 | ) 14 | 15 | // KubeDNSSuite. 16 | type KubeDNSSuite struct { 17 | logger logrus.FieldLogger 18 | cluster *k3d.Cluster 19 | tool *tool.Tool 20 | } 21 | 22 | func (s *KubeDNSSuite) SetUpSuite(c *check.C) { 23 | var err error 24 | 25 | requiredImages := []k3d.DockerImage{ 26 | {Name: "traefik/whoami:v1.6.0"}, 27 | {Name: "coredns/coredns:1.6.3"}, 28 | {Name: "giantswarm/tiny-tools:3.9"}, 29 | {Name: "gcr.io/google_containers/k8s-dns-kube-dns-amd64:1.14.7"}, 30 | {Name: "gcr.io/google_containers/k8s-dns-dnsmasq-nanny-amd64:1.14.7"}, 31 | {Name: "gcr.io/google_containers/k8s-dns-sidecar-amd64:1.14.7"}, 32 | } 33 | 34 | s.logger = logrus.New() 35 | s.cluster, err = k3d.NewCluster(s.logger, masterURL, k3dClusterName, 36 | k3d.WithoutTraefik(), 37 | k3d.WithoutCoreDNS(), 38 | k3d.WithImages(requiredImages...), 39 | ) 40 | c.Assert(err, checker.IsNil) 41 | 42 | c.Assert(s.cluster.CreateNamespace(s.logger, traefikMeshNamespace), checker.IsNil) 43 | c.Assert(s.cluster.CreateNamespace(s.logger, testNamespace), checker.IsNil) 44 | 45 | c.Assert(s.cluster.Apply(s.logger, smiCRDs), checker.IsNil) 46 | c.Assert(s.cluster.Apply(s.logger, "testdata/tool/tool.yaml"), checker.IsNil) 47 | c.Assert(s.cluster.Apply(s.logger, "testdata/kubedns/"), checker.IsNil) 48 | c.Assert(s.cluster.Apply(s.logger, "testdata/coredns/whoami-shadow-service.yaml"), checker.IsNil) 49 | 50 | c.Assert(s.cluster.WaitReadyPod("tool", testNamespace, 60*time.Second), checker.IsNil) 51 | c.Assert(s.cluster.WaitReadyDeployment("kube-dns", metav1.NamespaceSystem, 60*time.Second), checker.IsNil) 52 | c.Assert(s.cluster.WaitReadyDeployment("coredns", traefikMeshNamespace, 60*time.Second), checker.IsNil) 53 | 54 | s.tool = tool.New(s.logger, "tool", testNamespace) 55 | } 56 | 57 | func (s *KubeDNSSuite) TearDownSuite(c *check.C) { 58 | if s.cluster != nil { 59 | c.Assert(s.cluster.Stop(s.logger), checker.IsNil) 60 | } 61 | } 62 | 63 | func (s *KubeDNSSuite) TestKubeDNSDig(c *check.C) { 64 | s.logger.Info("Asserting KubeDNS has been patched successfully and can be dug") 65 | 66 | c.Assert(traefikMeshPrepare(), checker.IsNil) 67 | 68 | // Wait for kubeDNS, as the pods will be restarted by prepare. 69 | c.Assert(s.cluster.WaitReadyDeployment("kube-dns", metav1.NamespaceSystem, 60*time.Second), checker.IsNil) 70 | 71 | err := try.Retry(func() error { 72 | return s.tool.Dig("whoami.whoami.maesh") 73 | }, 60*time.Second) 74 | c.Assert(err, checker.IsNil) 75 | 76 | err = try.Retry(func() error { 77 | return s.tool.Dig("whoami.whoami.traefik.mesh") 78 | }, 60*time.Second) 79 | c.Assert(err, checker.IsNil) 80 | } 81 | -------------------------------------------------------------------------------- /integration/testdata/acl_disabled/http/1.server.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Pod 4 | metadata: 5 | name: server-http 6 | namespace: test 7 | labels: 8 | app: server-http 9 | spec: 10 | containers: 11 | - name: server-http 12 | image: traefik/whoami:v1.6.0 13 | imagePullPolicy: IfNotPresent 14 | readinessProbe: 15 | httpGet: 16 | path: / 17 | port: 80 18 | periodSeconds: 1 19 | 20 | --- 21 | apiVersion: v1 22 | kind: Service 23 | metadata: 24 | name: server-http 25 | namespace: test 26 | labels: 27 | app: server-http 28 | spec: 29 | type: ClusterIP 30 | ports: 31 | - port: 8080 32 | name: server-http 33 | targetPort: 80 34 | selector: 35 | app: server-http 36 | -------------------------------------------------------------------------------- /integration/testdata/acl_disabled/tcp/1.server.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Pod 4 | metadata: 5 | name: server-tcp 6 | namespace: test 7 | labels: 8 | app: server-tcp 9 | spec: 10 | containers: 11 | - name: server-tcp 12 | image: traefik/whoamitcp:v0.1.0 13 | imagePullPolicy: IfNotPresent 14 | readinessProbe: 15 | tcpSocket: 16 | port: 8080 17 | periodSeconds: 1 18 | 19 | --- 20 | apiVersion: v1 21 | kind: Service 22 | metadata: 23 | name: server-tcp 24 | namespace: test 25 | labels: 26 | app: server-tcp 27 | plop: cool 28 | annotations: 29 | mesh.traefik.io/traffic-type: tcp 30 | spec: 31 | type: ClusterIP 32 | ports: 33 | - port: 8080 34 | name: server-tcp 35 | selector: 36 | app: server-tcp 37 | -------------------------------------------------------------------------------- /integration/testdata/acl_disabled/traffic-split/1.server.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: server-split 6 | namespace: test 7 | labels: 8 | app: server-split 9 | spec: 10 | type: ClusterIP 11 | ports: 12 | - port: 8080 13 | name: server-split 14 | selector: 15 | app: server-split 16 | 17 | --- 18 | apiVersion: split.smi-spec.io/v1alpha3 19 | kind: TrafficSplit 20 | metadata: 21 | name: server-traffic-split 22 | namespace: test 23 | spec: 24 | service: server-split 25 | backends: 26 | - service: server-v1 27 | weight: 50 28 | - service: server-v2 29 | weight: 50 30 | 31 | -------------------------------------------------------------------------------- /integration/testdata/acl_disabled/traffic-split/2.server-v1.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Pod 4 | metadata: 5 | name: server-v1 6 | namespace: test 7 | labels: 8 | app: server-v1 9 | spec: 10 | containers: 11 | - name: server-v1 12 | image: traefik/whoami:v1.6.0 13 | imagePullPolicy: IfNotPresent 14 | readinessProbe: 15 | httpGet: 16 | path: / 17 | port: 80 18 | periodSeconds: 1 19 | 20 | --- 21 | apiVersion: v1 22 | kind: Service 23 | metadata: 24 | name: server-v1 25 | namespace: test 26 | labels: 27 | app: server-v1 28 | spec: 29 | type: ClusterIP 30 | ports: 31 | - port: 8080 32 | name: server-v1 33 | targetPort: 80 34 | selector: 35 | app: server-v1 36 | -------------------------------------------------------------------------------- /integration/testdata/acl_disabled/traffic-split/3.server-v2.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Pod 4 | metadata: 5 | name: server-v2 6 | namespace: test 7 | labels: 8 | app: server-v2 9 | spec: 10 | containers: 11 | - name: server-v2 12 | image: traefik/whoami:v1.6.0 13 | imagePullPolicy: IfNotPresent 14 | readinessProbe: 15 | httpGet: 16 | path: / 17 | port: 80 18 | periodSeconds: 1 19 | 20 | --- 21 | apiVersion: v1 22 | kind: Service 23 | metadata: 24 | name: server-v2 25 | namespace: test 26 | labels: 27 | app: server-v2 28 | spec: 29 | type: ClusterIP 30 | ports: 31 | - port: 8080 32 | name: server-v2 33 | targetPort: 80 34 | selector: 35 | app: server-v2 36 | -------------------------------------------------------------------------------- /integration/testdata/acl_disabled/udp/1.server.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Pod 4 | metadata: 5 | name: server-udp 6 | namespace: test 7 | labels: 8 | app: server-udp 9 | spec: 10 | containers: 11 | - name: server-udp 12 | image: traefik/whoamiudp:v0.1.0 13 | imagePullPolicy: IfNotPresent 14 | ports: 15 | - name: udp 16 | protocol: UDP 17 | containerPort: 8080 18 | --- 19 | apiVersion: v1 20 | kind: Service 21 | metadata: 22 | name: server-udp 23 | namespace: test 24 | labels: 25 | app: server-udp 26 | plop: cool 27 | annotations: 28 | mesh.traefik.io/traffic-type: udp 29 | spec: 30 | type: ClusterIP 31 | ports: 32 | - name: server-udp 33 | protocol: UDP 34 | port: 8080 35 | selector: 36 | app: server-udp 37 | -------------------------------------------------------------------------------- /integration/testdata/acl_enabled/http/0.service-account.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: server 6 | namespace: test 7 | 8 | --- 9 | apiVersion: v1 10 | kind: ServiceAccount 11 | metadata: 12 | name: server-api 13 | namespace: test 14 | 15 | --- 16 | apiVersion: v1 17 | kind: ServiceAccount 18 | metadata: 19 | name: server-header 20 | namespace: test 21 | 22 | --- 23 | apiVersion: v1 24 | kind: ServiceAccount 25 | metadata: 26 | name: server-split 27 | namespace: test 28 | -------------------------------------------------------------------------------- /integration/testdata/acl_enabled/http/1.server.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Pod 4 | metadata: 5 | name: server-http 6 | namespace: test 7 | labels: 8 | app: server-http 9 | spec: 10 | serviceAccountName: server 11 | containers: 12 | - name: server-http 13 | image: traefik/whoami:v1.6.0 14 | imagePullPolicy: IfNotPresent 15 | readinessProbe: 16 | httpGet: 17 | path: / 18 | port: 80 19 | periodSeconds: 1 20 | 21 | --- 22 | apiVersion: v1 23 | kind: Service 24 | metadata: 25 | name: server-http 26 | namespace: test 27 | labels: 28 | app: server-http 29 | spec: 30 | type: ClusterIP 31 | ports: 32 | - port: 8080 33 | name: server-http 34 | targetPort: 80 35 | selector: 36 | app: server-http 37 | -------------------------------------------------------------------------------- /integration/testdata/acl_enabled/http/3.server-api.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Pod 4 | metadata: 5 | name: server-http-api 6 | namespace: test 7 | labels: 8 | app: server-http-api 9 | spec: 10 | serviceAccountName: server-api 11 | containers: 12 | - name: server-http-api 13 | image: traefik/whoami:v1.6.0 14 | imagePullPolicy: IfNotPresent 15 | readinessProbe: 16 | httpGet: 17 | path: / 18 | port: 80 19 | periodSeconds: 1 20 | 21 | --- 22 | apiVersion: v1 23 | kind: Service 24 | metadata: 25 | name: server-http-api 26 | namespace: test 27 | labels: 28 | app: server-http-api 29 | spec: 30 | type: ClusterIP 31 | ports: 32 | - port: 8080 33 | name: server-http-api 34 | targetPort: 80 35 | selector: 36 | app: server-http-api 37 | -------------------------------------------------------------------------------- /integration/testdata/acl_enabled/http/4.server-header.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Pod 4 | metadata: 5 | name: server-http-header 6 | namespace: test 7 | labels: 8 | app: server-http-header 9 | spec: 10 | serviceAccountName: server-header 11 | containers: 12 | - name: server-http-header 13 | image: traefik/whoami:v1.6.0 14 | imagePullPolicy: IfNotPresent 15 | readinessProbe: 16 | httpGet: 17 | path: / 18 | port: 80 19 | periodSeconds: 1 20 | 21 | --- 22 | apiVersion: v1 23 | kind: Service 24 | metadata: 25 | name: server-http-header 26 | namespace: test 27 | labels: 28 | app: server-http-header 29 | spec: 30 | type: ClusterIP 31 | ports: 32 | - port: 8080 33 | name: server-http-header 34 | targetPort: 80 35 | selector: 36 | app: server-http-header 37 | -------------------------------------------------------------------------------- /integration/testdata/acl_enabled/http/6.traffic-target.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: access.smi-spec.io/v1alpha2 3 | kind: TrafficTarget 4 | metadata: 5 | name: tool-authorized-to-server-http 6 | namespace: test 7 | spec: 8 | destination: 9 | kind: ServiceAccount 10 | name: server 11 | namespace: test 12 | sources: 13 | - kind: ServiceAccount 14 | name: tool-authorized 15 | namespace: test -------------------------------------------------------------------------------- /integration/testdata/acl_enabled/http/7.traffic-target-path-filter.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: specs.smi-spec.io/v1alpha3 3 | kind: HTTPRouteGroup 4 | metadata: 5 | name: http-route-group-api 6 | namespace: test 7 | spec: 8 | matches: 9 | - name: api 10 | pathRegex: /api 11 | methods: ["GET"] 12 | 13 | --- 14 | apiVersion: access.smi-spec.io/v1alpha2 15 | kind: TrafficTarget 16 | metadata: 17 | name: traffic-target-path-filter 18 | namespace: test 19 | spec: 20 | destination: 21 | kind: ServiceAccount 22 | name: server-api 23 | namespace: test 24 | rules: 25 | - kind: HTTPRouteGroup 26 | name: http-route-group-api 27 | sources: 28 | - kind: ServiceAccount 29 | name: tool-authorized 30 | namespace: test -------------------------------------------------------------------------------- /integration/testdata/acl_enabled/http/8.traffic-target-header-filter.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: specs.smi-spec.io/v1alpha3 3 | kind: HTTPRouteGroup 4 | metadata: 5 | name: http-route-group-header 6 | namespace: test 7 | spec: 8 | matches: 9 | - name: header 10 | headers: 11 | - Authorized: "t|True" 12 | 13 | --- 14 | apiVersion: access.smi-spec.io/v1alpha2 15 | kind: TrafficTarget 16 | metadata: 17 | name: traffic-target-header-filter 18 | namespace: test 19 | spec: 20 | destination: 21 | kind: ServiceAccount 22 | name: server-header 23 | namespace: test 24 | rules: 25 | - kind: HTTPRouteGroup 26 | name: http-route-group-header 27 | sources: 28 | - kind: ServiceAccount 29 | name: tool-authorized 30 | namespace: test -------------------------------------------------------------------------------- /integration/testdata/acl_enabled/traffic-split/0.service-account.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: server-split 6 | namespace: test -------------------------------------------------------------------------------- /integration/testdata/acl_enabled/traffic-split/1.server-split.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: server-http-split 6 | namespace: test 7 | labels: 8 | app: server-http-split 9 | spec: 10 | type: ClusterIP 11 | ports: 12 | - port: 8080 13 | name: server-http-split 14 | selector: 15 | app: server-http-split 16 | 17 | --- 18 | apiVersion: v1 19 | kind: Pod 20 | metadata: 21 | name: server-http-split-v1 22 | namespace: test 23 | labels: 24 | app: server-http-split-v1 25 | spec: 26 | serviceAccountName: server-split 27 | containers: 28 | - name: server-http-split-v1 29 | image: traefik/whoami:v1.6.0 30 | imagePullPolicy: IfNotPresent 31 | readinessProbe: 32 | httpGet: 33 | path: / 34 | port: 80 35 | periodSeconds: 1 36 | 37 | --- 38 | apiVersion: v1 39 | kind: Service 40 | metadata: 41 | name: server-http-split-v1 42 | namespace: test 43 | labels: 44 | app: server-http-split-v1 45 | spec: 46 | type: ClusterIP 47 | ports: 48 | - port: 8080 49 | name: server-http-split-v1 50 | targetPort: 80 51 | selector: 52 | app: server-http-split-v1 53 | 54 | --- 55 | apiVersion: v1 56 | kind: Pod 57 | metadata: 58 | name: server-http-split-v2 59 | namespace: test 60 | labels: 61 | app: server-http-split-v2 62 | spec: 63 | serviceAccountName: server-split 64 | containers: 65 | - name: server-http-split-v2 66 | image: traefik/whoami:v1.6.0 67 | imagePullPolicy: IfNotPresent 68 | readinessProbe: 69 | httpGet: 70 | path: / 71 | port: 80 72 | periodSeconds: 1 73 | 74 | --- 75 | apiVersion: v1 76 | kind: Service 77 | metadata: 78 | name: server-http-split-v2 79 | namespace: test 80 | labels: 81 | app: server-http-split-v2 82 | spec: 83 | type: ClusterIP 84 | ports: 85 | - port: 8080 86 | name: server-http-split-v2 87 | targetPort: 80 88 | selector: 89 | app: server-http-split-v2 90 | 91 | --- 92 | apiVersion: split.smi-spec.io/v1alpha3 93 | kind: TrafficSplit 94 | metadata: 95 | name: server-http-split 96 | namespace: test 97 | spec: 98 | service: server-http-split 99 | backends: 100 | - service: server-http-split-v1 101 | weight: 50 102 | - service: server-http-split-v2 103 | weight: 50 104 | 105 | --- 106 | apiVersion: access.smi-spec.io/v1alpha2 107 | kind: TrafficTarget 108 | metadata: 109 | name: tool-authorized-to-server-http-split 110 | namespace: test 111 | spec: 112 | destination: 113 | kind: ServiceAccount 114 | name: server-split 115 | namespace: test 116 | sources: 117 | - kind: ServiceAccount 118 | name: tool-authorized 119 | namespace: test 120 | -------------------------------------------------------------------------------- /integration/testdata/coredns/whoami-shadow-service.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: traefik-mesh-whoami-6d61657368-whoami 6 | namespace: traefik-mesh 7 | labels: 8 | app: maesh 9 | type: shadow 10 | spec: 11 | type: ClusterIP 12 | ports: 13 | - port: 8080 14 | name: whoami 15 | targetPort: 80 16 | selector: 17 | app: traefik-mesh-proxy 18 | -------------------------------------------------------------------------------- /integration/testdata/crds/smi-access.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1beta1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: traffictargets.access.smi-spec.io 6 | spec: 7 | group: access.smi-spec.io 8 | scope: Namespaced 9 | names: 10 | kind: TrafficTarget 11 | shortNames: 12 | - tt 13 | plural: traffictargets 14 | singular: traffictarget 15 | version: v1alpha2 16 | versions: 17 | - name: v1alpha2 18 | served: true 19 | storage: true 20 | - name: v1alpha1 21 | served: false 22 | storage: false 23 | validation: 24 | openAPIV3Schema: 25 | properties: 26 | spec: 27 | required: 28 | - destination 29 | properties: 30 | destination: 31 | description: The destination of this traffic target. 32 | type: object 33 | required: 34 | - name 35 | - kind 36 | properties: 37 | kind: 38 | description: Kind of the destination. 39 | type: string 40 | name: 41 | description: Name of the destination. 42 | type: string 43 | namespace: 44 | description: Namespace of the destination. 45 | type: string 46 | port: 47 | description: Port number of the destination. 48 | type: number 49 | rules: 50 | description: Specifications of this traffic target. 51 | type: array 52 | items: 53 | type: object 54 | required: 55 | - name 56 | - kind 57 | properties: 58 | kind: 59 | description: Kind of this spec. 60 | type: string 61 | enum: 62 | - HTTPRouteGroup 63 | - TCPRoute 64 | name: 65 | description: Name of this spec. 66 | type: string 67 | matches: 68 | description: Match conditions of this spec. 69 | type: array 70 | items: 71 | type: string 72 | sources: 73 | description: Sources of this traffic target. 74 | type: array 75 | items: 76 | type: object 77 | required: 78 | - name 79 | - kind 80 | properties: 81 | kind: 82 | description: Kind of this source. 83 | type: string 84 | name: 85 | description: Name of this source. 86 | type: string 87 | namespace: 88 | description: Namespace of this source. 89 | type: string 90 | -------------------------------------------------------------------------------- /integration/testdata/crds/smi-specs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1beta1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: httproutegroups.specs.smi-spec.io 6 | spec: 7 | group: specs.smi-spec.io 8 | scope: Namespaced 9 | names: 10 | kind: HTTPRouteGroup 11 | shortNames: 12 | - htr 13 | plural: httproutegroups 14 | singular: httproutegroup 15 | version: v1alpha3 16 | versions: 17 | - name: v1alpha3 18 | served: true 19 | storage: true 20 | - name: v1alpha2 21 | served: false 22 | storage: false 23 | - name: v1alpha1 24 | served: false 25 | storage: false 26 | validation: 27 | openAPIV3Schema: 28 | properties: 29 | spec: 30 | required: 31 | - matches 32 | properties: 33 | matches: 34 | description: Match conditions of this route group. 35 | type: array 36 | items: 37 | type: object 38 | required: 39 | - name 40 | properties: 41 | name: 42 | description: Name of the HTTP route. 43 | type: string 44 | pathRegex: 45 | description: URI path regex of the HTTP route. 46 | type: string 47 | methods: 48 | description: The HTTP methods of this HTTP route. 49 | type: array 50 | items: 51 | type: string 52 | description: The HTTP method of this HTTP route. 53 | enum: 54 | - '*' 55 | - GET 56 | - HEAD 57 | - PUT 58 | - POST 59 | - DELETE 60 | - CONNECT 61 | - OPTIONS 62 | - TRACE 63 | - PATCH 64 | headers: 65 | description: Header match conditions of this route. 66 | type: array 67 | items: 68 | description: Header match condition of this route. 69 | type: object 70 | additionalProperties: 71 | type: string 72 | --- 73 | apiVersion: apiextensions.k8s.io/v1beta1 74 | kind: CustomResourceDefinition 75 | metadata: 76 | name: tcproutes.specs.smi-spec.io 77 | spec: 78 | group: specs.smi-spec.io 79 | scope: Namespaced 80 | names: 81 | kind: TCPRoute 82 | shortNames: 83 | - tr 84 | plural: tcproutes 85 | singular: tcproute 86 | version: v1alpha3 87 | versions: 88 | - name: v1alpha3 89 | served: true 90 | storage: true 91 | - name: v1alpha2 92 | served: false 93 | storage: false 94 | - name: v1alpha1 95 | served: false 96 | storage: false 97 | -------------------------------------------------------------------------------- /integration/testdata/crds/smi-split.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1beta1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: trafficsplits.split.smi-spec.io 6 | spec: 7 | group: split.smi-spec.io 8 | scope: Namespaced 9 | names: 10 | kind: TrafficSplit 11 | listKind: TrafficSplitList 12 | shortNames: 13 | - ts 14 | plural: trafficsplits 15 | singular: trafficsplit 16 | version: v1alpha3 17 | versions: 18 | - name: v1alpha3 19 | served: true 20 | storage: true 21 | - name: v1alpha2 22 | served: false 23 | storage: false 24 | - name: v1alpha1 25 | served: false 26 | storage: false 27 | additionalPrinterColumns: 28 | - name: Service 29 | type: string 30 | description: The apex service of this split. 31 | JSONPath: .spec.service 32 | validation: 33 | openAPIV3Schema: 34 | properties: 35 | spec: 36 | type: object 37 | required: 38 | - service 39 | - backends 40 | properties: 41 | service: 42 | description: The apex service of this split. 43 | type: string 44 | matches: 45 | description: The HTTP route groups that this traffic split should match. 46 | type: array 47 | items: 48 | type: object 49 | required: ['kind', 'name'] 50 | properties: 51 | kind: 52 | description: Kind of the matching group. 53 | type: string 54 | enum: 55 | - HTTPRouteGroup 56 | name: 57 | description: Name of the matching group. 58 | type: string 59 | backends: 60 | description: The backend services of this split. 61 | type: array 62 | items: 63 | type: object 64 | required: ['service', 'weight'] 65 | properties: 66 | service: 67 | description: Name of the Kubernetes service. 68 | type: string 69 | weight: 70 | description: Traffic weight value of this backend. 71 | type: number 72 | -------------------------------------------------------------------------------- /integration/testdata/tool/tool-authorized.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: tool-authorized 6 | namespace: test 7 | 8 | --- 9 | apiVersion: v1 10 | kind: Pod 11 | metadata: 12 | name: tool-authorized 13 | namespace: test 14 | spec: 15 | serviceAccountName: tool-authorized 16 | containers: 17 | - name: tool-authorized 18 | image: giantswarm/tiny-tools:3.9 19 | command: 20 | - "sleep" 21 | - "36000" -------------------------------------------------------------------------------- /integration/testdata/tool/tool-forbidden.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: tool-forbidden 6 | namespace: test 7 | 8 | --- 9 | apiVersion: v1 10 | kind: Pod 11 | metadata: 12 | name: tool-forbidden 13 | namespace: test 14 | spec: 15 | serviceAccountName: tool-forbidden 16 | containers: 17 | - name: tool-forbidden 18 | image: giantswarm/tiny-tools:3.9 19 | command: 20 | - "sleep" 21 | - "36000" -------------------------------------------------------------------------------- /integration/testdata/tool/tool.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: tool 6 | namespace: test 7 | 8 | --- 9 | apiVersion: v1 10 | kind: Pod 11 | metadata: 12 | name: tool 13 | namespace: test 14 | spec: 15 | serviceAccountName: tool 16 | containers: 17 | - name: tool 18 | image: giantswarm/tiny-tools:3.9 19 | command: 20 | - "sleep" 21 | - "36000" -------------------------------------------------------------------------------- /integration/testdata/traefik-mesh/values.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Traefik Mesh configuration. 3 | # 4 | controller: 5 | image: 6 | pullPolicy: IfNotPresent 7 | tag: latest 8 | 9 | proxy: 10 | image: 11 | pullPolicy: IfNotPresent 12 | tag: v2.3 13 | 14 | pollInterval: 100ms 15 | pollTimeout: 100ms 16 | 17 | # 18 | # Tracing configuration. 19 | # 20 | tracing: 21 | deploy: false 22 | jaeger: 23 | enabled: false 24 | 25 | # 26 | # Metrics configuration. 27 | # 28 | metrics: 29 | deploy: false 30 | prometheus: 31 | enabled: false 32 | -------------------------------------------------------------------------------- /integration/tool/tool.go: -------------------------------------------------------------------------------- 1 | package tool 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "fmt" 7 | "net" 8 | "net/http" 9 | "os" 10 | "os/exec" 11 | "strings" 12 | "time" 13 | 14 | "github.com/sirupsen/logrus" 15 | "github.com/traefik/mesh/integration/try" 16 | ) 17 | 18 | // Tool is pod capable of running operations from within the cluster. 19 | type Tool struct { 20 | logger logrus.FieldLogger 21 | name string 22 | namespace string 23 | kubectlTimeout time.Duration 24 | } 25 | 26 | // New creates a new Tool. 27 | func New(logger logrus.FieldLogger, name, namespace string) *Tool { 28 | return &Tool{ 29 | name: name, 30 | namespace: namespace, 31 | logger: logger, 32 | kubectlTimeout: 3 * time.Second, 33 | } 34 | } 35 | 36 | // Dig digs the given url and make sure there is an A record. 37 | func (t *Tool) Dig(url string) error { 38 | output, err := t.exec([]string{"dig", url, "+short", "+timeout=3"}) 39 | if err != nil { 40 | t.logger.WithError(err).Debug("Dig command has failed") 41 | return err 42 | } 43 | 44 | IP := net.ParseIP(strings.TrimSpace(string(output))) 45 | if IP == nil { 46 | return fmt.Errorf("could not parse an IP from dig: %s", string(output)) 47 | } 48 | 49 | t.logger.Debugf("Dig %q: %s", url, IP.String()) 50 | 51 | return nil 52 | } 53 | 54 | // Curl curls the given url and checks if it matches the given conditions. 55 | func (t *Tool) Curl(url string, headers map[string]string, conditions ...try.ResponseCondition) error { 56 | args := []string{"curl", "-s", "-D-", "-m", "2"} 57 | 58 | for key, value := range headers { 59 | args = append(args, "-H", key+": "+value) 60 | } 61 | 62 | output, err := t.exec(append(args, url)) 63 | if err != nil { 64 | t.logger.WithError(err).Debug("Curl command has failed") 65 | return err 66 | } 67 | 68 | reader := bufio.NewReader(bytes.NewReader(output)) 69 | 70 | resp, err := http.ReadResponse(reader, nil) 71 | if err != nil { 72 | t.logger.WithError(err).Debug("Curl output is not an HTTP response") 73 | return err 74 | } 75 | 76 | defer resp.Body.Close() 77 | 78 | for _, condition := range conditions { 79 | if err := condition(resp); err != nil { 80 | t.logger.Debugf("Curl condition did not match: %v", err) 81 | 82 | return err 83 | } 84 | } 85 | 86 | return nil 87 | } 88 | 89 | // Netcat netcats the given url on the given port and checks if the given conditions are fulfilled. 90 | func (t *Tool) Netcat(url string, port int, udp bool, conditions ...try.StringCondition) error { 91 | var cmd string 92 | 93 | if udp { 94 | cmd = fmt.Sprintf("echo 'WHO' | nc -u -w 1 %s %d", url, port) 95 | } else { 96 | cmd = fmt.Sprintf("echo 'WHO' | nc -q 0 %s %d", url, port) 97 | } 98 | 99 | output, err := t.exec([]string{"ash", "-c", cmd}) 100 | if err != nil { 101 | t.logger.WithError(err).Debug("Netcat command has failed") 102 | return err 103 | } 104 | 105 | for _, condition := range conditions { 106 | if err := condition(string(output)); err != nil { 107 | t.logger.Debugf("Netcat condition did not match: %v", err) 108 | 109 | return err 110 | } 111 | } 112 | 113 | return nil 114 | } 115 | 116 | func (t *Tool) exec(args []string) ([]byte, error) { 117 | args = append([]string{ 118 | "exec", "-i", t.name, 119 | "--request-timeout=3s", 120 | "-n", t.namespace, 121 | "--", 122 | }, args...) 123 | 124 | cmd := exec.Command("kubectl", args...) 125 | cmd.Env = os.Environ() 126 | 127 | output, err := cmd.CombinedOutput() 128 | if err != nil { 129 | return []byte{}, fmt.Errorf("unable execute command 'kubectl %s' - output %s: %w", strings.Join(args, " "), output, err) 130 | } 131 | 132 | return output, nil 133 | } 134 | -------------------------------------------------------------------------------- /integration/try/condition.go: -------------------------------------------------------------------------------- 1 | package try 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | "strings" 8 | ) 9 | 10 | // StringCondition is a retry condition function. 11 | // It receives a string, and returns an error 12 | // if the string failed the condition. 13 | type StringCondition func(string) error 14 | 15 | // StringContains returns a retry condition function. 16 | // The condition returns an error if the string does not contain the given values. 17 | func StringContains(values ...string) StringCondition { 18 | return func(res string) error { 19 | for _, value := range values { 20 | if !strings.Contains(res, value) { 21 | return fmt.Errorf("could not find %q in %q", value, res) 22 | } 23 | } 24 | 25 | return nil 26 | } 27 | } 28 | 29 | // ResponseCondition is a retry condition function. 30 | // It receives a response, and returns an error 31 | // if the response failed the condition. 32 | type ResponseCondition func(*http.Response) error 33 | 34 | // BodyContains returns a retry condition function. 35 | // The condition returns an error if the request body does not contain all the given 36 | // strings. 37 | func BodyContains(values ...string) ResponseCondition { 38 | return func(res *http.Response) error { 39 | body, err := io.ReadAll(res.Body) 40 | if err != nil { 41 | return fmt.Errorf("failed to read response body: %w", err) 42 | } 43 | 44 | for _, value := range values { 45 | if !strings.Contains(string(body), value) { 46 | return fmt.Errorf("could not find '%s' in body '%s'", value, string(body)) 47 | } 48 | } 49 | 50 | return nil 51 | } 52 | } 53 | 54 | // StatusCodeIs returns a retry condition function. 55 | // The condition returns an error if the given response's status code is not the 56 | // given HTTP status code. 57 | func StatusCodeIs(status int) ResponseCondition { 58 | return func(res *http.Response) error { 59 | if res.StatusCode != status { 60 | return fmt.Errorf("got status code %d, wanted %d", res.StatusCode, status) 61 | } 62 | 63 | return nil 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /integration/try/try.go: -------------------------------------------------------------------------------- 1 | package try 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strconv" 7 | "time" 8 | 9 | "github.com/cenkalti/backoff/v4" 10 | "github.com/traefik/mesh/pkg/safe" 11 | ) 12 | 13 | // CITimeoutMultiplier is the multiplier for all timeout in the CI. 14 | const CITimeoutMultiplier = 3 15 | 16 | // Retry runs a function over and over until it doesn't return an error or the given timeout duration is reached. 17 | func Retry(f func() error, timeout time.Duration) error { 18 | ebo := backoff.NewExponentialBackOff() 19 | ebo.MaxElapsedTime = applyCIMultiplier(timeout) 20 | 21 | if err := backoff.Retry(safe.OperationWithRecover(f), ebo); err != nil { 22 | return fmt.Errorf("unable execute function: %w", err) 23 | } 24 | 25 | return nil 26 | } 27 | 28 | func applyCIMultiplier(timeout time.Duration) time.Duration { 29 | if os.Getenv("CI") == "" { 30 | return timeout 31 | } 32 | 33 | ciTimeoutMultiplier := getCITimeoutMultiplier() 34 | 35 | return time.Duration(float64(timeout) * ciTimeoutMultiplier) 36 | } 37 | 38 | func getCITimeoutMultiplier() float64 { 39 | ciTimeoutMultiplier := os.Getenv("CI_TIMEOUT_MULTIPLIER") 40 | if ciTimeoutMultiplier == "" { 41 | return CITimeoutMultiplier 42 | } 43 | 44 | multiplier, err := strconv.ParseFloat(ciTimeoutMultiplier, 64) 45 | if err != nil { 46 | return CITimeoutMultiplier 47 | } 48 | 49 | return multiplier 50 | } 51 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # Path relative to the root of the repository 3 | publish = "site" 4 | base = "docs" 5 | 6 | # Path relative to the "base" directory 7 | command = "sh -x scripts/netlify-run.sh" 8 | 9 | [[headers]] 10 | # Define which paths this specific [[headers]] block will cover. 11 | for = "/*" 12 | [headers.values] 13 | Strict-Transport-Security = "max-age=31536000; includeSubDomains; preload" 14 | -------------------------------------------------------------------------------- /pkg/annotations/middleware.go: -------------------------------------------------------------------------------- 1 | package annotations 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/traefik/traefik/v2/pkg/config/dynamic" 8 | ) 9 | 10 | type middlewareBuilder func(annotations map[string]string) (middleware *dynamic.Middleware, name string, err error) 11 | 12 | // BuildMiddlewares builds middlewares from the given annotations. 13 | func BuildMiddlewares(annotations map[string]string) (map[string]*dynamic.Middleware, error) { 14 | builders := []middlewareBuilder{ 15 | buildRetryMiddleware, 16 | buildRateLimitMiddleware, 17 | buildCircuitBreakerMiddleware, 18 | } 19 | 20 | middlewares := map[string]*dynamic.Middleware{} 21 | 22 | for _, builder := range builders { 23 | middleware, name, err := builder(annotations) 24 | if err != nil { 25 | return nil, err 26 | } 27 | 28 | if middleware != nil { 29 | middlewares[name] = middleware 30 | } 31 | } 32 | 33 | return middlewares, nil 34 | } 35 | 36 | func buildRetryMiddleware(annotations map[string]string) (middleware *dynamic.Middleware, name string, err error) { 37 | var retryAttempts int 38 | 39 | retryAttempts, err = GetRetryAttempts(annotations) 40 | if err != nil { 41 | if errors.Is(err, ErrNotFound) { 42 | return nil, "", nil 43 | } 44 | 45 | return nil, "", fmt.Errorf("unable to build retry middleware: %w", err) 46 | } 47 | 48 | name = "retry" 49 | middleware = &dynamic.Middleware{ 50 | Retry: &dynamic.Retry{Attempts: retryAttempts}, 51 | } 52 | 53 | return middleware, name, nil 54 | } 55 | 56 | func buildRateLimitMiddleware(annotations map[string]string) (middleware *dynamic.Middleware, name string, err error) { 57 | var ( 58 | rateLimitBurst int 59 | rateLimitAverage int 60 | ) 61 | 62 | rateLimitBurst, err = GetRateLimitBurst(annotations) 63 | if errors.Is(err, ErrNotFound) { 64 | return nil, "", nil 65 | } else if err != nil { 66 | return nil, "", fmt.Errorf("unable to build rate-limit middleware: %w", err) 67 | } 68 | 69 | rateLimitAverage, err = GetRateLimitAverage(annotations) 70 | if errors.Is(err, ErrNotFound) { 71 | return nil, "", nil 72 | } else if err != nil { 73 | return nil, "", fmt.Errorf("unable to build rate-limit middleware: %w", err) 74 | } 75 | 76 | if rateLimitBurst <= 0 || rateLimitAverage <= 0 { 77 | return nil, "", errors.New("unable to build rate-limit middleware: burst and average must be greater than 0") 78 | } 79 | 80 | name = "rate-limit" 81 | middleware = &dynamic.Middleware{ 82 | RateLimit: &dynamic.RateLimit{ 83 | Burst: int64(rateLimitBurst), 84 | Average: int64(rateLimitAverage), 85 | }, 86 | } 87 | 88 | return middleware, name, nil 89 | } 90 | 91 | func buildCircuitBreakerMiddleware(annotations map[string]string) (middleware *dynamic.Middleware, name string, err error) { 92 | var circuitBreakerExpression string 93 | 94 | circuitBreakerExpression, err = GetCircuitBreakerExpression(annotations) 95 | if err != nil { 96 | if errors.Is(err, ErrNotFound) { 97 | return nil, "", nil 98 | } 99 | 100 | return nil, "", fmt.Errorf("unable to build circuit-breaker middleware: %w", err) 101 | } 102 | 103 | name = "circuit-breaker" 104 | middleware = &dynamic.Middleware{ 105 | CircuitBreaker: &dynamic.CircuitBreaker{ 106 | Expression: circuitBreakerExpression, 107 | }, 108 | } 109 | 110 | return middleware, name, nil 111 | } 112 | -------------------------------------------------------------------------------- /pkg/api/testdata/getmeshnodeconfiguration_empty.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/traefik/mesh/5ee481aca18c81bd710a8c15cbdf75d3d599bea1/pkg/api/testdata/getmeshnodeconfiguration_empty.yaml -------------------------------------------------------------------------------- /pkg/api/testdata/getmeshnodeconfiguration_simple.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: mesh-pod-1 5 | namespace: foo 6 | labels: 7 | component: maesh-mesh 8 | spec: 9 | containers: 10 | - name: example 11 | image: busybox 12 | status: 13 | podIP: "127.0.0.1" 14 | -------------------------------------------------------------------------------- /pkg/api/testdata/getmeshnodes_empty.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/traefik/mesh/5ee481aca18c81bd710a8c15cbdf75d3d599bea1/pkg/api/testdata/getmeshnodes_empty.yaml -------------------------------------------------------------------------------- /pkg/api/testdata/getmeshnodes_one_mesh_pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: mesh-pod-1 5 | namespace: foo 6 | labels: 7 | component: maesh-mesh 8 | spec: 9 | containers: 10 | - name: example 11 | image: busybox 12 | status: 13 | podIP: "10.4.3.2" 14 | -------------------------------------------------------------------------------- /pkg/api/testdata/getmeshnodes_one_nonready_mesh_pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: mesh-pod-1 5 | namespace: foo 6 | labels: 7 | component: maesh-mesh 8 | spec: 9 | containers: 10 | - name: example 11 | image: busybox 12 | status: 13 | podIP: "10.4.19.1" 14 | containerStatuses: 15 | - name: example 16 | ready: false 17 | -------------------------------------------------------------------------------- /pkg/cleanup/cleanup.go: -------------------------------------------------------------------------------- 1 | package cleanup 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/sirupsen/logrus" 8 | "github.com/traefik/mesh/pkg/dns" 9 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | "k8s.io/client-go/kubernetes" 11 | ) 12 | 13 | // Cleanup holds the clients for the various resource controllers. 14 | type Cleanup struct { 15 | namespace string 16 | kubeClient kubernetes.Interface 17 | dnsClient *dns.Client 18 | logger logrus.FieldLogger 19 | } 20 | 21 | // NewCleanup returns an initialized cleanup object. 22 | func NewCleanup(logger logrus.FieldLogger, kubeClient kubernetes.Interface, namespace string) *Cleanup { 23 | dnsClient := dns.NewClient(logger, kubeClient) 24 | 25 | return &Cleanup{ 26 | kubeClient: kubeClient, 27 | logger: logger, 28 | namespace: namespace, 29 | dnsClient: dnsClient, 30 | } 31 | } 32 | 33 | // CleanShadowServices deletes all shadow services from the cluster. 34 | func (c *Cleanup) CleanShadowServices(ctx context.Context) error { 35 | serviceList, err := c.kubeClient.CoreV1().Services(c.namespace).List(ctx, metav1.ListOptions{ 36 | LabelSelector: "app=maesh,type=shadow", 37 | }) 38 | if err != nil { 39 | return err 40 | } 41 | 42 | for _, s := range serviceList.Items { 43 | if err := c.kubeClient.CoreV1().Services(s.Namespace).Delete(ctx, s.Name, metav1.DeleteOptions{}); err != nil { 44 | return err 45 | } 46 | } 47 | 48 | return nil 49 | } 50 | 51 | // RestoreDNSConfig restores the configmap and restarts the DNS pods. 52 | func (c *Cleanup) RestoreDNSConfig(ctx context.Context) error { 53 | provider, err := c.dnsClient.CheckDNSProvider(ctx) 54 | if err != nil { 55 | return err 56 | } 57 | 58 | // Restore configmaps based on DNS provider. 59 | switch provider { 60 | case dns.CoreDNS: 61 | if err := c.dnsClient.RestoreCoreDNS(ctx); err != nil { 62 | return fmt.Errorf("unable to restore CoreDNS: %w", err) 63 | } 64 | case dns.KubeDNS: 65 | if err := c.dnsClient.RestoreKubeDNS(ctx); err != nil { 66 | return fmt.Errorf("unable to restore KubeDNS: %w", err) 67 | } 68 | } 69 | 70 | return nil 71 | } 72 | -------------------------------------------------------------------------------- /pkg/cleanup/cleanup_test.go: -------------------------------------------------------------------------------- 1 | package cleanup 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "testing" 7 | 8 | "github.com/sirupsen/logrus" 9 | "github.com/stretchr/testify/assert" 10 | "github.com/stretchr/testify/require" 11 | "github.com/traefik/mesh/pkg/k8s" 12 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 13 | ) 14 | 15 | func TestCleanup_New(t *testing.T) { 16 | clientMock := k8s.NewClientMock("mock.yaml") 17 | logger := logrus.New() 18 | 19 | logger.SetOutput(os.Stdout) 20 | logger.SetLevel(logrus.DebugLevel) 21 | 22 | cleanup := NewCleanup(logger, clientMock.KubernetesClient(), metav1.NamespaceDefault) 23 | require.NotNil(t, cleanup) 24 | } 25 | 26 | func TestCleanup_CleanShadowServices(t *testing.T) { 27 | clientMock := k8s.NewClientMock("mock.yaml") 28 | logger := logrus.New() 29 | 30 | logger.SetOutput(os.Stdout) 31 | logger.SetLevel(logrus.DebugLevel) 32 | 33 | cleanup := NewCleanup(logger, clientMock.KubernetesClient(), "traefik-mesh") 34 | require.NotNil(t, cleanup) 35 | 36 | err := cleanup.CleanShadowServices(context.Background()) 37 | require.NoError(t, err) 38 | 39 | serviceList, err := clientMock.KubernetesClient().CoreV1().Services(metav1.NamespaceAll).List(context.Background(), metav1.ListOptions{ 40 | LabelSelector: "app=maesh,type=shadow", 41 | }) 42 | require.NoError(t, err) 43 | assert.Len(t, serviceList.Items, 0) 44 | 45 | serviceList, err = clientMock.KubernetesClient().CoreV1().Services(metav1.NamespaceAll).List(context.Background(), metav1.ListOptions{}) 46 | require.NoError(t, err) 47 | assert.Len(t, serviceList.Items, 2) 48 | } 49 | -------------------------------------------------------------------------------- /pkg/cleanup/testdata/mock.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: test1 6 | namespace: traefik-mesh 7 | labels: 8 | app: maesh 9 | type: shadow 10 | spec: 11 | selector: 12 | app: test 13 | ports: 14 | - protocol: TCP 15 | port: 80 16 | targetPort: 8080 17 | 18 | --- 19 | apiVersion: v1 20 | kind: Service 21 | metadata: 22 | name: test2 23 | namespace: traefik-mesh 24 | labels: 25 | app: maesh 26 | type: shadow 27 | spec: 28 | selector: 29 | app: test 30 | ports: 31 | - protocol: TCP 32 | port: 80 33 | targetPort: 8080 34 | 35 | --- 36 | apiVersion: v1 37 | kind: Service 38 | metadata: 39 | name: test3 40 | namespace: traefik-mesh 41 | spec: 42 | selector: 43 | app: test 44 | ports: 45 | - protocol: TCP 46 | port: 80 47 | targetPort: 8080 48 | 49 | --- 50 | apiVersion: v1 51 | kind: Service 52 | metadata: 53 | name: test4 54 | namespace: default 55 | spec: 56 | selector: 57 | app: test 58 | ports: 59 | - protocol: TCP 60 | port: 80 61 | targetPort: 8080 62 | -------------------------------------------------------------------------------- /pkg/controller/controller_test.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/sirupsen/logrus" 8 | "github.com/stretchr/testify/assert" 9 | "github.com/traefik/mesh/pkg/k8s" 10 | "github.com/traefik/mesh/pkg/topology" 11 | "github.com/traefik/traefik/v2/pkg/config/dynamic" 12 | ) 13 | 14 | const ( 15 | traefikMeshNamespace string = "traefik-mesh" 16 | minHTTPPort = int32(5000) 17 | maxHTTPPort = int32(5005) 18 | minTCPPort = int32(10000) 19 | maxTCPPort = int32(10005) 20 | minUDPPort = int32(15000) 21 | maxUDPPort = int32(15005) 22 | ) 23 | 24 | type storeMock struct{} 25 | 26 | func (a *storeMock) SetConfig(cfg *dynamic.Configuration) {} 27 | func (a *storeMock) SetTopology(topo *topology.Topology) {} 28 | func (a *storeMock) SetReadiness(isReady bool) {} 29 | 30 | func TestController_NewMeshController(t *testing.T) { 31 | store := &storeMock{} 32 | clientMock := k8s.NewClientMock("mock.yaml") 33 | 34 | log := logrus.New() 35 | log.SetOutput(os.Stdout) 36 | log.SetLevel(logrus.DebugLevel) 37 | 38 | // Create a new controller with base HTTP mode. 39 | controller := NewMeshController(clientMock, Config{ 40 | ACLEnabled: false, 41 | DefaultMode: "http", 42 | Namespace: traefikMeshNamespace, 43 | IgnoreNamespaces: []string{}, 44 | MinHTTPPort: minHTTPPort, 45 | MaxHTTPPort: maxHTTPPort, 46 | MinTCPPort: minTCPPort, 47 | MaxTCPPort: maxTCPPort, 48 | MinUDPPort: minUDPPort, 49 | MaxUDPPort: maxUDPPort, 50 | }, store, log) 51 | 52 | assert.NotNil(t, controller) 53 | } 54 | 55 | func TestController_NewMeshControllerWithSMI(t *testing.T) { 56 | store := &storeMock{} 57 | clientMock := k8s.NewClientMock("mock.yaml") 58 | 59 | log := logrus.New() 60 | log.SetOutput(os.Stdout) 61 | log.SetLevel(logrus.DebugLevel) 62 | 63 | // Create a new controller with base HTTP mode, in SMI mode. 64 | controller := NewMeshController(clientMock, Config{ 65 | ACLEnabled: true, 66 | DefaultMode: "http", 67 | Namespace: traefikMeshNamespace, 68 | IgnoreNamespaces: []string{}, 69 | MinHTTPPort: minHTTPPort, 70 | MaxHTTPPort: maxHTTPPort, 71 | MinTCPPort: minTCPPort, 72 | MaxTCPPort: maxTCPPort, 73 | MinUDPPort: minUDPPort, 74 | MaxUDPPort: maxUDPPort, 75 | }, store, log) 76 | 77 | assert.NotNil(t, controller) 78 | } 79 | -------------------------------------------------------------------------------- /pkg/controller/handler.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "github.com/sirupsen/logrus" 5 | corev1 "k8s.io/api/core/v1" 6 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 7 | "k8s.io/client-go/tools/cache" 8 | "k8s.io/client-go/util/workqueue" 9 | ) 10 | 11 | type enqueueWorkHandler struct { 12 | logger logrus.FieldLogger 13 | workQueue workqueue.RateLimitingInterface 14 | } 15 | 16 | // OnAdd is called when an object is added to the informers cache. 17 | func (h *enqueueWorkHandler) OnAdd(obj interface{}) { 18 | h.enqueueWork(obj) 19 | } 20 | 21 | // OnUpdate is called when an object is updated in the informers cache. 22 | func (h *enqueueWorkHandler) OnUpdate(oldObj interface{}, newObj interface{}) { 23 | oldObjMeta, okOld := oldObj.(metav1.Object) 24 | newObjMeta, okNew := newObj.(metav1.Object) 25 | 26 | // This is a resync event, no extra work is needed. 27 | if okOld && okNew && oldObjMeta.GetResourceVersion() == newObjMeta.GetResourceVersion() { 28 | return 29 | } 30 | 31 | h.enqueueWork(newObj) 32 | } 33 | 34 | // OnDelete is called when an object is removed from the informers cache. 35 | func (h *enqueueWorkHandler) OnDelete(obj interface{}) { 36 | h.enqueueWork(obj) 37 | } 38 | 39 | func (h *enqueueWorkHandler) enqueueWork(obj interface{}) { 40 | if _, isService := obj.(*corev1.Service); !isService { 41 | h.workQueue.Add(configRefreshKey) 42 | return 43 | } 44 | 45 | key, err := cache.MetaNamespaceKeyFunc(obj) 46 | if err != nil { 47 | h.logger.Errorf("Unable to create a work key for resource %#v", obj) 48 | return 49 | } 50 | 51 | h.workQueue.Add(key) 52 | } 53 | -------------------------------------------------------------------------------- /pkg/controller/testdata/getmeshnodeconfiguration_empty.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/traefik/mesh/5ee481aca18c81bd710a8c15cbdf75d3d599bea1/pkg/controller/testdata/getmeshnodeconfiguration_empty.yaml -------------------------------------------------------------------------------- /pkg/controller/testdata/getmeshnodeconfiguration_simple.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: mesh-pod-1 5 | namespace: foo 6 | labels: 7 | component: maesh-mesh 8 | spec: 9 | containers: 10 | - name: example 11 | image: busybox 12 | status: 13 | podIP: "127.0.0.1" 14 | -------------------------------------------------------------------------------- /pkg/controller/testdata/getmeshnodes_empty.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/traefik/mesh/5ee481aca18c81bd710a8c15cbdf75d3d599bea1/pkg/controller/testdata/getmeshnodes_empty.yaml -------------------------------------------------------------------------------- /pkg/controller/testdata/getmeshnodes_one_mesh_pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: mesh-pod-1 5 | namespace: foo 6 | labels: 7 | component: maesh-mesh 8 | spec: 9 | containers: 10 | - name: example 11 | image: busybox 12 | status: 13 | podIP: "10.4.3.2" 14 | -------------------------------------------------------------------------------- /pkg/controller/testdata/getmeshnodes_one_nonready_mesh_pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: mesh-pod-1 5 | namespace: foo 6 | labels: 7 | component: maesh-mesh 8 | spec: 9 | containers: 10 | - name: example 11 | image: busybox 12 | status: 13 | podIP: "10.4.19.1" 14 | containerStatuses: 15 | - name: example 16 | ready: false 17 | -------------------------------------------------------------------------------- /pkg/controller/testdata/mock.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: foo 5 | --- 6 | apiVersion: v1 7 | kind: Service 8 | metadata: 9 | name: test 10 | namespace: foo 11 | spec: 12 | clusterIP: 10.1.0.1 13 | selector: 14 | app: test 15 | ports: 16 | - protocol: TCP 17 | port: 80 18 | targetPort: 80 19 | --- 20 | apiVersion: v1 21 | kind: Endpoints 22 | metadata: 23 | name: test 24 | namespace: foo 25 | subsets: 26 | - addresses: 27 | - ip: 10.0.0.1 28 | ports: 29 | - port: 80 30 | - addresses: 31 | - ip: 10.0.0.2 32 | ports: 33 | - port: 80 34 | -------------------------------------------------------------------------------- /pkg/dns/testdata/checkdnsprovider_coredns_using_label.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: rke2-coredns-rke2-coredns 5 | namespace: kube-system 6 | labels: 7 | kubernetes.io/name: CoreDNS 8 | spec: 9 | template: 10 | spec: 11 | containers: 12 | - name: coredns 13 | image: image-registry.dkr.ecr.eu-west-1.amazonaws.com/eks/coredns:v1.8.4 14 | - name: titi 15 | image: titi/toto:latest 16 | -------------------------------------------------------------------------------- /pkg/dns/testdata/checkdnsprovider_kubedns.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: kube-dns 5 | namespace: kube-system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: titi 11 | image: titi/toto:latest 12 | -------------------------------------------------------------------------------- /pkg/dns/testdata/checkdnsprovider_no_provider.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: notaprovider 5 | namespace: kube-system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: titi 11 | image: titi/toto:latest 12 | -------------------------------------------------------------------------------- /pkg/dns/testdata/checkdnsprovider_supported_min_version_suffix.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: coredns 5 | namespace: kube-system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: coredns 11 | image: image-registry.dkr.ecr.eu-west-1.amazonaws.com/eks/coredns:v1.3.0-eksbuild.1 12 | - name: titi 13 | image: titi/toto:latest 14 | -------------------------------------------------------------------------------- /pkg/dns/testdata/checkdnsprovider_supported_version.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: coredns 5 | namespace: kube-system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: coredns 11 | image: image-registry.canonical.com:5000/cdk/coredns/coredns-amd64:1.6.9 12 | - name: titi 13 | image: titi/toto:latest 14 | -------------------------------------------------------------------------------- /pkg/dns/testdata/checkdnsprovider_supported_version_suffix.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: coredns 5 | namespace: kube-system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: coredns 11 | image: image-registry.dkr.ecr.eu-west-1.amazonaws.com/eks/coredns:v1.6.6-eksbuild.1 12 | - name: titi 13 | image: titi/toto:latest 14 | -------------------------------------------------------------------------------- /pkg/dns/testdata/checkdnsprovider_unsupported_version.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: coredns 5 | namespace: kube-system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: coredns 11 | image: coredns-amd64:0.4.2 12 | - name: titi 13 | image: titi/toto:latest 14 | -------------------------------------------------------------------------------- /pkg/dns/testdata/configurecoredns_17.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: coredns 5 | namespace: kube-system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: coredns 11 | image: coredns:1.7.0 12 | volumes: 13 | - configMap: 14 | name: "other-cfgmap" 15 | - configMap: 16 | name: "coredns" 17 | --- 18 | apiVersion: v1 19 | kind: ConfigMap 20 | metadata: 21 | name: other-cfgmap 22 | namespace: kube-system 23 | --- 24 | apiVersion: v1 25 | kind: ConfigMap 26 | metadata: 27 | name: coredns 28 | namespace: kube-system 29 | data: 30 | Corefile: | 31 | .:53 { 32 | errors 33 | health { 34 | lameduck 5s 35 | } 36 | ready 37 | kubernetes {{ pillar['dns_domain'] }} in-addr.arpa ip6.arpa { 38 | pods insecure 39 | fallthrough in-addr.arpa ip6.arpa 40 | ttl 30 41 | } 42 | prometheus :9153 43 | forward . /etc/resolv.conf 44 | cache 30 45 | loop 46 | reload 47 | loadbalance 48 | } 49 | -------------------------------------------------------------------------------- /pkg/dns/testdata/configurecoredns_17_already_patched.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: coredns 5 | namespace: kube-system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: coredns 11 | image: coredns:1.7.0 12 | volumes: 13 | - configMap: 14 | name: "other-cfgmap" 15 | - configMap: 16 | name: "coredns" 17 | --- 18 | apiVersion: v1 19 | kind: ConfigMap 20 | metadata: 21 | name: other-cfgmap 22 | namespace: kube-system 23 | --- 24 | apiVersion: v1 25 | kind: ConfigMap 26 | metadata: 27 | name: coredns 28 | namespace: kube-system 29 | data: 30 | Corefile: | 31 | .:53 { 32 | errors 33 | health { 34 | lameduck 5s 35 | } 36 | ready 37 | kubernetes {{ pillar['dns_domain'] }} in-addr.arpa ip6.arpa { 38 | pods insecure 39 | fallthrough in-addr.arpa ip6.arpa 40 | ttl 30 41 | } 42 | prometheus :9153 43 | forward . /etc/resolv.conf 44 | cache 30 45 | loop 46 | reload 47 | loadbalance 48 | } 49 | #### Begin Maesh Block 50 | maesh:53 { 51 | errors 52 | rewrite continue { 53 | name regex ([a-zA-Z0-9-_]*)\.([a-zv0-9-_]*)\.maesh toto-{1}-6d61657368-{2}.toto.svc.titi 54 | answer name toto-([a-zA-Z0-9-_]*)-6d61657368-([a-zA-Z0-9-_]*)\.toto\.svc\.titi {1}.{2}.maesh 55 | } 56 | kubernetes titi in-addr.arpa ip6.arpa { 57 | pods insecure 58 | upstream 59 | fallthrough in-addr.arpa ip6.arpa 60 | } 61 | forward . /etc/resolv.conf 62 | cache 30 63 | loop 64 | reload 65 | loadbalance 66 | } 67 | #### End Maesh Block -------------------------------------------------------------------------------- /pkg/dns/testdata/configurecoredns_17_custom_already_patched.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: coredns 5 | namespace: kube-system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: coredns 11 | image: coredns:1.7.0 12 | volumes: 13 | - configMap: 14 | name: "coredns" 15 | - configMap: 16 | name: "coredns-custom" 17 | --- 18 | apiVersion: v1 19 | kind: ConfigMap 20 | metadata: 21 | name: coredns-custom 22 | namespace: kube-system 23 | data: 24 | maesh.server: | 25 | #### Begin Maesh Block 26 | maesh:53 { 27 | errors 28 | rewrite continue { 29 | name regex ([a-zA-Z0-9-_]*)\.([a-zv0-9-_]*)\.maesh toto-{1}-6d61657368-{2}.toto.svc.titi 30 | answer name toto-([a-zA-Z0-9-_]*)-6d61657368-([a-zA-Z0-9-_]*)\.toto\.svc\.titi {1}.{2}.maesh 31 | } 32 | kubernetes titi in-addr.arpa ip6.arpa { 33 | pods insecure 34 | upstream 35 | fallthrough in-addr.arpa ip6.arpa 36 | } 37 | forward . /etc/resolv.conf 38 | cache 30 39 | loop 40 | reload 41 | loadbalance 42 | } 43 | #### End Maesh Block 44 | --- 45 | apiVersion: v1 46 | kind: ConfigMap 47 | metadata: 48 | name: coredns 49 | namespace: kube-system 50 | data: 51 | Corefile: | 52 | .:53 { 53 | errors 54 | health { 55 | lameduck 5s 56 | } 57 | ready 58 | kubernetes {{ pillar['dns_domain'] }} in-addr.arpa ip6.arpa { 59 | pods insecure 60 | fallthrough in-addr.arpa ip6.arpa 61 | ttl 30 62 | } 63 | prometheus :9153 64 | forward . /etc/resolv.conf 65 | cache 30 66 | loop 67 | reload 68 | loadbalance 69 | } 70 | -------------------------------------------------------------------------------- /pkg/dns/testdata/configurecoredns_17_suffix.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: coredns 5 | namespace: kube-system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: coredns 11 | image: image-registry.dkr.ecr.eu-west-1.amazonaws.com/eks/coredns:v1.7.0-eksbuild.1 12 | volumes: 13 | - configMap: 14 | name: "other-cfgmap" 15 | - configMap: 16 | name: "coredns" 17 | --- 18 | apiVersion: v1 19 | kind: ConfigMap 20 | metadata: 21 | name: other-cfgmap 22 | namespace: kube-system 23 | --- 24 | apiVersion: v1 25 | kind: ConfigMap 26 | metadata: 27 | name: coredns 28 | namespace: kube-system 29 | data: 30 | Corefile: | 31 | .:53 { 32 | errors 33 | health { 34 | lameduck 5s 35 | } 36 | ready 37 | kubernetes {{ pillar['dns_domain'] }} in-addr.arpa ip6.arpa { 38 | pods insecure 39 | fallthrough in-addr.arpa ip6.arpa 40 | ttl 30 41 | } 42 | prometheus :9153 43 | forward . /etc/resolv.conf 44 | cache 30 45 | loop 46 | reload 47 | loadbalance 48 | } 49 | -------------------------------------------------------------------------------- /pkg/dns/testdata/configurecoredns_already_patched.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: coredns 5 | namespace: kube-system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: coredns 11 | image: coredns:1.6.0 12 | volumes: 13 | - configMap: 14 | name: "other-cfgmap" 15 | - configMap: 16 | name: "coredns" 17 | --- 18 | apiVersion: v1 19 | kind: ConfigMap 20 | metadata: 21 | name: other-cfgmap 22 | namespace: kube-system 23 | --- 24 | apiVersion: v1 25 | kind: ConfigMap 26 | metadata: 27 | name: coredns 28 | namespace: kube-system 29 | data: 30 | Corefile: | 31 | .:53 { 32 | errors 33 | health { 34 | lameduck 5s 35 | } 36 | ready 37 | kubernetes {{ pillar['dns_domain'] }} in-addr.arpa ip6.arpa { 38 | pods insecure 39 | fallthrough in-addr.arpa ip6.arpa 40 | ttl 30 41 | } 42 | prometheus :9153 43 | forward . /etc/resolv.conf 44 | cache 30 45 | loop 46 | reload 47 | loadbalance 48 | } 49 | 50 | #### Begin Maesh Block 51 | maesh:53 { 52 | errors 53 | rewrite continue { 54 | name regex ([a-zA-Z0-9-_]*)\.([a-zv0-9-_]*)\.maesh toto-{1}-6d61657368-{2}.toto.svc.titi 55 | answer name toto-([a-zA-Z0-9-_]*)-6d61657368-([a-zA-Z0-9-_]*)\.toto\.svc\.titi {1}.{2}.maesh 56 | } 57 | kubernetes titi in-addr.arpa ip6.arpa { 58 | pods insecure 59 | upstream 60 | fallthrough in-addr.arpa ip6.arpa 61 | } 62 | forward . /etc/resolv.conf 63 | cache 30 64 | loop 65 | reload 66 | loadbalance 67 | } 68 | #### End Maesh Block 69 | 70 | #### Begin Traefik Mesh Block 71 | traefik.mesh:53 { 72 | errors 73 | rewrite continue { 74 | name regex ([a-zA-Z0-9-_]*)\.([a-zv0-9-_]*)\.traefik.mesh toto-{1}-6d61657368-{2}.toto.svc.titi 75 | answer name toto-([a-zA-Z0-9-_]*)-6d61657368-([a-zA-Z0-9-_]*)\.toto\.svc\.titi {1}.{2}.traefik.mesh 76 | } 77 | kubernetes titi in-addr.arpa ip6.arpa { 78 | pods insecure 79 | upstream 80 | fallthrough in-addr.arpa ip6.arpa 81 | } 82 | forward . /etc/resolv.conf 83 | cache 30 84 | loop 85 | reload 86 | loadbalance 87 | } 88 | #### End Traefik Mesh Block 89 | -------------------------------------------------------------------------------- /pkg/dns/testdata/configurecoredns_custom_already_patched.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: coredns 5 | namespace: kube-system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: coredns 11 | image: coredns:1.6.0 12 | volumes: 13 | - configMap: 14 | name: "coredns" 15 | - configMap: 16 | name: "coredns-custom" 17 | --- 18 | apiVersion: v1 19 | kind: ConfigMap 20 | metadata: 21 | name: coredns-custom 22 | namespace: kube-system 23 | data: 24 | maesh.server: | 25 | #### Begin Maesh Block 26 | maesh:53 { 27 | errors 28 | rewrite continue { 29 | name regex ([a-zA-Z0-9-_]*)\.([a-zv0-9-_]*)\.maesh toto-{1}-6d61657368-{2}.toto.svc.titi 30 | answer name toto-([a-zA-Z0-9-_]*)-6d61657368-([a-zA-Z0-9-_]*)\.toto\.svc\.titi {1}.{2}.maesh 31 | } 32 | kubernetes titi in-addr.arpa ip6.arpa { 33 | pods insecure 34 | upstream 35 | fallthrough in-addr.arpa ip6.arpa 36 | } 37 | forward . /etc/resolv.conf 38 | cache 30 39 | loop 40 | reload 41 | loadbalance 42 | } 43 | #### End Maesh Block 44 | 45 | traefik.mesh.server: | 46 | #### Begin Traefik Mesh Block 47 | traefik.mesh:53 { 48 | errors 49 | rewrite continue { 50 | name regex ([a-zA-Z0-9-_]*)\.([a-zv0-9-_]*)\.traefik.mesh toto-{1}-6d61657368-{2}.toto.svc.titi 51 | answer name toto-([a-zA-Z0-9-_]*)-6d61657368-([a-zA-Z0-9-_]*)\.toto\.svc\.titi {1}.{2}.traefik.mesh 52 | } 53 | kubernetes titi in-addr.arpa ip6.arpa { 54 | pods insecure 55 | upstream 56 | fallthrough in-addr.arpa ip6.arpa 57 | } 58 | forward . /etc/resolv.conf 59 | cache 30 60 | loop 61 | reload 62 | loadbalance 63 | } 64 | #### End Traefik Mesh Block 65 | 66 | --- 67 | apiVersion: v1 68 | kind: ConfigMap 69 | metadata: 70 | name: coredns 71 | namespace: kube-system 72 | data: 73 | Corefile: | 74 | .:53 { 75 | errors 76 | health { 77 | lameduck 5s 78 | } 79 | ready 80 | kubernetes {{ pillar['dns_domain'] }} in-addr.arpa ip6.arpa { 81 | pods insecure 82 | fallthrough in-addr.arpa ip6.arpa 83 | ttl 30 84 | } 85 | prometheus :9153 86 | forward . /etc/resolv.conf 87 | cache 30 88 | loop 89 | reload 90 | loadbalance 91 | } 92 | -------------------------------------------------------------------------------- /pkg/dns/testdata/configurecoredns_custom_not_patched.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: coredns 5 | namespace: kube-system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: coredns 11 | image: coredns:1.6.0 12 | volumes: 13 | - configMap: 14 | name: "coredns" 15 | - configMap: 16 | name: "coredns-custom" 17 | --- 18 | apiVersion: v1 19 | kind: ConfigMap 20 | metadata: 21 | name: coredns-custom 22 | namespace: kube-system 23 | --- 24 | apiVersion: v1 25 | kind: ConfigMap 26 | metadata: 27 | name: coredns 28 | namespace: kube-system 29 | data: 30 | Corefile: | 31 | .:53 { 32 | errors 33 | health { 34 | lameduck 5s 35 | } 36 | ready 37 | kubernetes {{ pillar['dns_domain'] }} in-addr.arpa ip6.arpa { 38 | pods insecure 39 | fallthrough in-addr.arpa ip6.arpa 40 | ttl 30 41 | } 42 | prometheus :9153 43 | forward . /etc/resolv.conf 44 | cache 30 45 | loop 46 | reload 47 | loadbalance 48 | } 49 | -------------------------------------------------------------------------------- /pkg/dns/testdata/configurecoredns_missing_configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: coredns 5 | namespace: kube-system 6 | -------------------------------------------------------------------------------- /pkg/dns/testdata/configurecoredns_missing_deployment.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/traefik/mesh/5ee481aca18c81bd710a8c15cbdf75d3d599bea1/pkg/dns/testdata/configurecoredns_missing_deployment.yaml -------------------------------------------------------------------------------- /pkg/dns/testdata/configurecoredns_not_patched.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: coredns 5 | namespace: kube-system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: coredns 11 | image: coredns:1.6.0 12 | volumes: 13 | - configMap: 14 | name: "other-cfgmap" 15 | - configMap: 16 | name: "coredns" 17 | --- 18 | apiVersion: v1 19 | kind: ConfigMap 20 | metadata: 21 | name: other-cfgmap 22 | namespace: kube-system 23 | --- 24 | apiVersion: v1 25 | kind: ConfigMap 26 | metadata: 27 | name: coredns 28 | namespace: kube-system 29 | data: 30 | Corefile: | 31 | .:53 { 32 | errors 33 | health { 34 | lameduck 5s 35 | } 36 | ready 37 | kubernetes {{ pillar['dns_domain'] }} in-addr.arpa ip6.arpa { 38 | pods insecure 39 | fallthrough in-addr.arpa ip6.arpa 40 | ttl 30 41 | } 42 | prometheus :9153 43 | forward . /etc/resolv.conf 44 | cache 30 45 | loop 46 | reload 47 | loadbalance 48 | } 49 | -------------------------------------------------------------------------------- /pkg/dns/testdata/configurekubedns_already_patched.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: kube-dns 5 | namespace: kube-system 6 | spec: 7 | template: 8 | spec: 9 | volumes: 10 | - configMap: 11 | name: "kube-dns" 12 | --- 13 | apiVersion: v1 14 | kind: ConfigMap 15 | metadata: 16 | name: kube-dns 17 | namespace: kube-system 18 | --- 19 | apiVersion: v1 20 | kind: Service 21 | metadata: 22 | name: coredns 23 | namespace: traefik-mesh 24 | spec: 25 | clusterIP: "1.2.3.4" 26 | --- 27 | apiVersion: apps/v1 28 | kind: Deployment 29 | metadata: 30 | name: coredns 31 | namespace: traefik-mesh 32 | spec: 33 | template: 34 | spec: 35 | containers: 36 | - name: coredns 37 | image: coredns:1.6.0 38 | volumes: 39 | - configMap: 40 | name: "other-cfgmap" 41 | - configMap: 42 | name: "coredns" 43 | --- 44 | apiVersion: v1 45 | kind: ConfigMap 46 | metadata: 47 | name: other-cfgmap 48 | namespace: traefik-mesh 49 | --- 50 | apiVersion: v1 51 | kind: ConfigMap 52 | metadata: 53 | name: coredns 54 | namespace: traefik-mesh 55 | data: 56 | Corefile: | 57 | .:53 { 58 | errors 59 | health { 60 | lameduck 5s 61 | } 62 | ready 63 | kubernetes {{ pillar['dns_domain'] }} in-addr.arpa ip6.arpa { 64 | pods insecure 65 | fallthrough in-addr.arpa ip6.arpa 66 | ttl 30 67 | } 68 | prometheus :9153 69 | forward . /etc/resolv.conf 70 | cache 30 71 | loop 72 | reload 73 | loadbalance 74 | } 75 | -------------------------------------------------------------------------------- /pkg/dns/testdata/configurekubedns_missing_deployment.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/traefik/mesh/5ee481aca18c81bd710a8c15cbdf75d3d599bea1/pkg/dns/testdata/configurekubedns_missing_deployment.yaml -------------------------------------------------------------------------------- /pkg/dns/testdata/configurekubedns_not_patched.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: kube-dns 5 | namespace: kube-system 6 | spec: 7 | template: 8 | spec: 9 | volumes: 10 | - configMap: 11 | name: "kube-dns" 12 | --- 13 | apiVersion: v1 14 | kind: ConfigMap 15 | metadata: 16 | name: kube-dns 17 | namespace: kube-system 18 | --- 19 | apiVersion: v1 20 | kind: Service 21 | metadata: 22 | name: coredns 23 | namespace: traefik-mesh 24 | spec: 25 | clusterIP: "1.2.3.4" 26 | --- 27 | apiVersion: apps/v1 28 | kind: Deployment 29 | metadata: 30 | name: coredns 31 | namespace: traefik-mesh 32 | spec: 33 | template: 34 | spec: 35 | containers: 36 | - name: coredns 37 | image: coredns:1.6.0 38 | volumes: 39 | - configMap: 40 | name: "other-cfgmap" 41 | - configMap: 42 | name: "coredns" 43 | --- 44 | apiVersion: v1 45 | kind: ConfigMap 46 | metadata: 47 | name: other-cfgmap 48 | namespace: traefik-mesh 49 | --- 50 | apiVersion: v1 51 | kind: ConfigMap 52 | metadata: 53 | name: coredns 54 | namespace: traefik-mesh 55 | data: 56 | Corefile: | 57 | .:53 { 58 | errors 59 | health { 60 | lameduck 5s 61 | } 62 | ready 63 | kubernetes {{ pillar['dns_domain'] }} in-addr.arpa ip6.arpa { 64 | pods insecure 65 | fallthrough in-addr.arpa ip6.arpa 66 | ttl 30 67 | } 68 | prometheus :9153 69 | forward . /etc/resolv.conf 70 | cache 30 71 | loop 72 | reload 73 | loadbalance 74 | } 75 | -------------------------------------------------------------------------------- /pkg/dns/testdata/configurekubedns_optional_configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: kube-dns 5 | namespace: kube-system 6 | spec: 7 | template: 8 | spec: 9 | volumes: 10 | - configMap: 11 | name: "kube-dns" 12 | optional: true 13 | --- 14 | apiVersion: v1 15 | kind: Service 16 | metadata: 17 | name: coredns 18 | namespace: traefik-mesh 19 | spec: 20 | clusterIP: "1.2.3.4" 21 | --- 22 | apiVersion: apps/v1 23 | kind: Deployment 24 | metadata: 25 | name: coredns 26 | namespace: traefik-mesh 27 | spec: 28 | template: 29 | spec: 30 | containers: 31 | - name: coredns 32 | image: coredns:1.6.0 33 | volumes: 34 | - configMap: 35 | name: "other-cfgmap" 36 | - configMap: 37 | name: "coredns" 38 | --- 39 | apiVersion: v1 40 | kind: ConfigMap 41 | metadata: 42 | name: other-cfgmap 43 | namespace: traefik-mesh 44 | --- 45 | apiVersion: v1 46 | kind: ConfigMap 47 | metadata: 48 | name: coredns 49 | namespace: traefik-mesh 50 | data: 51 | Corefile: | 52 | .:53 { 53 | errors 54 | health { 55 | lameduck 5s 56 | } 57 | ready 58 | kubernetes {{ pillar['dns_domain'] }} in-addr.arpa ip6.arpa { 59 | pods insecure 60 | fallthrough in-addr.arpa ip6.arpa 61 | ttl 30 62 | } 63 | prometheus :9153 64 | forward . /etc/resolv.conf 65 | cache 30 66 | loop 67 | reload 68 | loadbalance 69 | } 70 | -------------------------------------------------------------------------------- /pkg/dns/testdata/restorecoredns_custom_not_patched.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: coredns 5 | namespace: kube-system 6 | spec: 7 | template: 8 | spec: 9 | volumes: 10 | - configMap: 11 | name: "coredns-custom" 12 | - configMap: 13 | name: "coredns" 14 | --- 15 | apiVersion: v1 16 | kind: ConfigMap 17 | metadata: 18 | name: coredns-custom 19 | namespace: kube-system 20 | data: 21 | test.server: must be present 22 | --- 23 | apiVersion: v1 24 | kind: ConfigMap 25 | metadata: 26 | name: coredns 27 | namespace: kube-system 28 | data: 29 | Corefile: | 30 | .:53 { 31 | errors 32 | health { 33 | lameduck 5s 34 | } 35 | ready 36 | kubernetes {{ pillar['dns_domain'] }} in-addr.arpa ip6.arpa { 37 | pods insecure 38 | fallthrough in-addr.arpa ip6.arpa 39 | ttl 30 40 | } 41 | prometheus :9153 42 | forward . /etc/resolv.conf 43 | cache 30 44 | loop 45 | reload 46 | loadbalance 47 | } 48 | -------------------------------------------------------------------------------- /pkg/dns/testdata/restorecoredns_custom_patched.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: coredns 5 | namespace: kube-system 6 | spec: 7 | template: 8 | spec: 9 | volumes: 10 | - configMap: 11 | name: "coredns-custom" 12 | - configMap: 13 | name: "coredns" 14 | --- 15 | apiVersion: v1 16 | kind: ConfigMap 17 | metadata: 18 | name: coredns-custom 19 | namespace: kube-system 20 | data: 21 | test.server: must be present 22 | maesh.server: | 23 | #### Begin Maesh Block 24 | maesh:53 { 25 | errors 26 | rewrite continue { 27 | name regex ([a-zA-Z0-9-_]*)\.([a-zv0-9-_]*)\.maesh toto-{1}-6d61657368-{2}.toto.svc.titi 28 | answer name toto-([a-zA-Z0-9-_]*)-6d61657368-([a-zA-Z0-9-_]*)\.toto\.svc\.titi {1}.{2}.maesh 29 | } 30 | kubernetes titi in-addr.arpa ip6.arpa { 31 | pods insecure 32 | upstream 33 | fallthrough in-addr.arpa ip6.arpa 34 | } 35 | forward . /etc/resolv.conf 36 | cache 30 37 | loop 38 | reload 39 | loadbalance 40 | } 41 | #### End Maesh Block 42 | --- 43 | apiVersion: v1 44 | kind: ConfigMap 45 | metadata: 46 | name: coredns 47 | namespace: kube-system 48 | data: 49 | Corefile: | 50 | .:53 { 51 | errors 52 | health { 53 | lameduck 5s 54 | } 55 | ready 56 | kubernetes {{ pillar['dns_domain'] }} in-addr.arpa ip6.arpa { 57 | pods insecure 58 | fallthrough in-addr.arpa ip6.arpa 59 | ttl 30 60 | } 61 | prometheus :9153 62 | forward . /etc/resolv.conf 63 | cache 30 64 | loop 65 | reload 66 | loadbalance 67 | } 68 | -------------------------------------------------------------------------------- /pkg/dns/testdata/restorecoredns_not_patched.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: coredns 5 | namespace: kube-system 6 | spec: 7 | template: 8 | spec: 9 | volumes: 10 | - configMap: 11 | name: "other-cfgmap" 12 | - configMap: 13 | name: "coredns" 14 | --- 15 | apiVersion: v1 16 | kind: ConfigMap 17 | metadata: 18 | name: other-cfgmap 19 | namespace: kube-system 20 | --- 21 | apiVersion: v1 22 | kind: ConfigMap 23 | metadata: 24 | name: coredns 25 | namespace: kube-system 26 | data: 27 | Corefile: | 28 | .:53 { 29 | errors 30 | health { 31 | lameduck 5s 32 | } 33 | ready 34 | kubernetes {{ pillar['dns_domain'] }} in-addr.arpa ip6.arpa { 35 | pods insecure 36 | fallthrough in-addr.arpa ip6.arpa 37 | ttl 30 38 | } 39 | prometheus :9153 40 | forward . /etc/resolv.conf 41 | cache 30 42 | loop 43 | reload 44 | loadbalance 45 | } 46 | -------------------------------------------------------------------------------- /pkg/dns/testdata/restorecoredns_patched.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: coredns 5 | namespace: kube-system 6 | spec: 7 | template: 8 | spec: 9 | volumes: 10 | - configMap: 11 | name: "other-cfgmap" 12 | - configMap: 13 | name: "coredns" 14 | --- 15 | apiVersion: v1 16 | kind: ConfigMap 17 | metadata: 18 | name: other-cfgmap 19 | namespace: kube-system 20 | --- 21 | apiVersion: v1 22 | kind: ConfigMap 23 | metadata: 24 | name: coredns 25 | namespace: kube-system 26 | data: 27 | Corefile: | 28 | .:53 { 29 | errors 30 | health { 31 | lameduck 5s 32 | } 33 | ready 34 | kubernetes {{ pillar['dns_domain'] }} in-addr.arpa ip6.arpa { 35 | pods insecure 36 | fallthrough in-addr.arpa ip6.arpa 37 | ttl 30 38 | } 39 | prometheus :9153 40 | forward . /etc/resolv.conf 41 | cache 30 42 | loop 43 | reload 44 | loadbalance 45 | } 46 | 47 | #### Begin Maesh Block 48 | maesh:53 { 49 | errors 50 | rewrite continue { 51 | name regex ([a-zA-Z0-9-_]*)\.([a-zv0-9-_]*)\.maesh toto-{1}-6d61657368-{2}.toto.svc.titi 52 | answer name toto-([a-zA-Z0-9-_]*)-6d61657368-([a-zA-Z0-9-_]*)\.toto\.svc\.titi {1}.{2}.maesh 53 | } 54 | kubernetes titi in-addr.arpa ip6.arpa { 55 | pods insecure 56 | upstream 57 | fallthrough in-addr.arpa ip6.arpa 58 | } 59 | forward . /etc/resolv.conf 60 | cache 30 61 | loop 62 | reload 63 | loadbalance 64 | } 65 | #### End Maesh Block 66 | # This is test data that must be present 67 | -------------------------------------------------------------------------------- /pkg/dns/testdata/restorekubedns_already_patched.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: kube-dns 5 | namespace: kube-system 6 | spec: 7 | template: 8 | spec: 9 | volumes: 10 | - configMap: 11 | name: "kube-dns" 12 | --- 13 | apiVersion: v1 14 | kind: ConfigMap 15 | metadata: 16 | name: kube-dns 17 | namespace: kube-system 18 | data: 19 | stubDomains: | 20 | {"maesh":["1.2.3.4"], "traefik.mesh":["1.2.3.4"], "test":["5.6.7.8"]} 21 | --- 22 | apiVersion: v1 23 | kind: Service 24 | metadata: 25 | name: coredns 26 | namespace: traefik-mesh 27 | spec: 28 | clusterIP: "1.2.3.4" 29 | -------------------------------------------------------------------------------- /pkg/dns/testdata/restorekubedns_not_patched.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: kube-dns 5 | namespace: kube-system 6 | spec: 7 | template: 8 | spec: 9 | volumes: 10 | - configMap: 11 | name: "kube-dns" 12 | --- 13 | apiVersion: v1 14 | kind: ConfigMap 15 | metadata: 16 | name: kube-dns 17 | namespace: kube-system 18 | --- 19 | apiVersion: v1 20 | kind: Service 21 | metadata: 22 | name: coredns 23 | namespace: traefik-mesh 24 | spec: 25 | clusterIP: "1.2.3.4" 26 | -------------------------------------------------------------------------------- /pkg/k8s/constants.go: -------------------------------------------------------------------------------- 1 | package k8s 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | const ( 8 | // ResyncPeriod set the resync period. 9 | ResyncPeriod = 5 * time.Minute 10 | 11 | // TrafficSplitObjectKind is the name of an SMI object of kind TrafficSplit. 12 | TrafficSplitObjectKind = "TrafficSplit" 13 | // TrafficTargetObjectKind is the name of an SMI object of kind TrafficTarget. 14 | TrafficTargetObjectKind = "TrafficTarget" 15 | // HTTPRouteGroupObjectKind is the name of an SMI object of kind HTTPRouteGroup. 16 | HTTPRouteGroupObjectKind = "HTTPRouteGroup" 17 | // TCPRouteObjectKind is the name of an SMI object of kind TCPRoute. 18 | TCPRouteObjectKind = "TCPRoute" 19 | 20 | // CoreObjectKinds is a filter for objects to process by the core client. 21 | CoreObjectKinds = "Deployment|Endpoints|Service|Ingress|Secret|Namespace|Pod|ConfigMap" 22 | // AccessObjectKinds is a filter for objects to process by the access client. 23 | AccessObjectKinds = TrafficTargetObjectKind 24 | // SpecsObjectKinds is a filter for objects to process by the specs client. 25 | SpecsObjectKinds = HTTPRouteGroupObjectKind + "|" + TCPRouteObjectKind 26 | // SplitObjectKinds is a filter for objects to process by the split client. 27 | SplitObjectKinds = TrafficSplitObjectKind 28 | ) 29 | -------------------------------------------------------------------------------- /pkg/k8s/smi.go: -------------------------------------------------------------------------------- 1 | package k8s 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strings" 7 | 8 | access "github.com/servicemeshinterface/smi-sdk-go/pkg/apis/access/v1alpha2" 9 | specs "github.com/servicemeshinterface/smi-sdk-go/pkg/apis/specs/v1alpha3" 10 | split "github.com/servicemeshinterface/smi-sdk-go/pkg/apis/split/v1alpha3" 11 | "k8s.io/apimachinery/pkg/runtime/schema" 12 | "k8s.io/client-go/kubernetes" 13 | ) 14 | 15 | // CheckSMIVersion checks if the SMI CRDs versions installed match the supported versions. 16 | func CheckSMIVersion(client kubernetes.Interface, aclEnabled bool) error { 17 | serverGroups, err := client.Discovery().ServerGroups() 18 | if err != nil { 19 | return fmt.Errorf("unable to list kubernetes server groups: %w", err) 20 | } 21 | 22 | requiredGroups := []schema.GroupVersion{ 23 | split.SchemeGroupVersion, 24 | specs.SchemeGroupVersion, 25 | } 26 | 27 | if aclEnabled { 28 | requiredGroups = append(requiredGroups, access.SchemeGroupVersion) 29 | } 30 | 31 | var errs []string 32 | 33 | for _, requiredGroup := range requiredGroups { 34 | var version string 35 | 36 | for _, group := range serverGroups.Groups { 37 | if requiredGroup.Group == group.Name { 38 | version = group.PreferredVersion.Version 39 | 40 | break 41 | } 42 | } 43 | 44 | if version == "" { 45 | errs = append(errs, fmt.Sprintf("unable to find group %q version %q", requiredGroup.Group, requiredGroup.Version)) 46 | } else if version != requiredGroup.Version { 47 | errs = append(errs, fmt.Sprintf("unable to find group %q version %q, got %q", requiredGroup.Group, requiredGroup.Version, version)) 48 | } 49 | } 50 | 51 | if len(errs) > 0 { 52 | return errors.New(strings.Join(errs, "; ")) 53 | } 54 | 55 | return nil 56 | } 57 | -------------------------------------------------------------------------------- /pkg/k8s/testdata/mock.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: test 5 | spec: 6 | selector: 7 | app: test 8 | ports: 9 | - protocol: TCP 10 | port: 80 11 | targetPort: 8080 12 | -------------------------------------------------------------------------------- /pkg/provider/key.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/traefik/mesh/pkg/topology" 7 | ) 8 | 9 | const ( 10 | blockAllMiddlewareKey = "block-all-middleware" 11 | blockAllServiceKey = "block-all-service" 12 | ) 13 | 14 | func getMiddlewareKey(svc *topology.Service, name string) string { 15 | return fmt.Sprintf("%s-%s-%s", svc.Namespace, svc.Name, name) 16 | } 17 | 18 | func getServiceRouterKeyFromService(svc *topology.Service, port int32) string { 19 | return fmt.Sprintf("%s-%s-%d", svc.Namespace, svc.Name, port) 20 | } 21 | 22 | func getWhitelistMiddlewareKeyFromTrafficTargetDirect(tt *topology.ServiceTrafficTarget) string { 23 | return fmt.Sprintf("%s-%s-%s-whitelist-traffic-target-direct", tt.Service.Namespace, tt.Service.Name, tt.Name) 24 | } 25 | 26 | func getWhitelistMiddlewareKeyFromTrafficTargetIndirect(tt *topology.ServiceTrafficTarget) string { 27 | return fmt.Sprintf("%s-%s-%s-whitelist-traffic-target-indirect", tt.Service.Namespace, tt.Service.Name, tt.Name) 28 | } 29 | 30 | func getWhitelistMiddlewareKeyFromTrafficSplitDirect(ts *topology.TrafficSplit) string { 31 | return fmt.Sprintf("%s-%s-%s-whitelist-traffic-split-direct", ts.Service.Namespace, ts.Service.Name, ts.Name) 32 | } 33 | 34 | func getWhitelistMiddlewareKeyFromTrafficSplitIndirect(ts *topology.TrafficSplit) string { 35 | return fmt.Sprintf("%s-%s-%s-whitelist-traffic-split-indirect", ts.Service.Namespace, ts.Service.Name, ts.Name) 36 | } 37 | 38 | func getServiceKeyFromTrafficTarget(tt *topology.ServiceTrafficTarget, port int32) string { 39 | return fmt.Sprintf("%s-%s-%s-%d-traffic-target", tt.Service.Namespace, tt.Service.Name, tt.Name, port) 40 | } 41 | 42 | func getRouterKeyFromTrafficTargetDirect(tt *topology.ServiceTrafficTarget, port int32) string { 43 | return fmt.Sprintf("%s-%s-%s-%d-traffic-target-direct", tt.Service.Namespace, tt.Service.Name, tt.Name, port) 44 | } 45 | 46 | func getRouterKeyFromTrafficTargetIndirect(tt *topology.ServiceTrafficTarget, port int32) string { 47 | return fmt.Sprintf("%s-%s-%s-%d-traffic-target-indirect", tt.Service.Namespace, tt.Service.Name, tt.Name, port) 48 | } 49 | 50 | func getServiceKeyFromTrafficSplit(ts *topology.TrafficSplit, port int32) string { 51 | return fmt.Sprintf("%s-%s-%s-%d-traffic-split", ts.Service.Namespace, ts.Service.Name, ts.Name, port) 52 | } 53 | 54 | func getRouterKeyFromTrafficSplitDirect(ts *topology.TrafficSplit, port int32) string { 55 | return fmt.Sprintf("%s-%s-%s-%d-traffic-split-direct", ts.Service.Namespace, ts.Service.Name, ts.Name, port) 56 | } 57 | 58 | func getRouterKeyFromTrafficSplitIndirect(ts *topology.TrafficSplit, port int32) string { 59 | return fmt.Sprintf("%s-%s-%s-%d-traffic-split-indirect", ts.Service.Namespace, ts.Service.Name, ts.Name, port) 60 | } 61 | 62 | func getServiceKeyFromTrafficSplitBackend(ts *topology.TrafficSplit, port int32, backend topology.TrafficSplitBackend) string { 63 | return fmt.Sprintf("%s-%s-%s-%d-%s-traffic-split-backend", ts.Service.Namespace, ts.Service.Name, ts.Name, port, backend.Service.Name) 64 | } 65 | -------------------------------------------------------------------------------- /pkg/provider/testdata/acl-disabled-http-basic-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "http": { 3 | "routers": { 4 | "my-ns-svc-a-8080": { 5 | "entryPoints": [ 6 | "http-10000" 7 | ], 8 | "service": "my-ns-svc-a-8080", 9 | "rule": "Host(`svc-a.my-ns.traefik.mesh`) || Host(`svc-a.my-ns.maesh`) || Host(`10.10.14.1`)", 10 | "priority": 1002 11 | }, 12 | "my-ns-svc-a-8081": { 13 | "entryPoints": [ 14 | "http-10001" 15 | ], 16 | "service": "my-ns-svc-a-8081", 17 | "rule": "Host(`svc-a.my-ns.traefik.mesh`) || Host(`svc-a.my-ns.maesh`) || Host(`10.10.14.1`)", 18 | "priority": 1002 19 | }, 20 | "readiness": { 21 | "entryPoints": [ 22 | "readiness" 23 | ], 24 | "service": "readiness", 25 | "rule": "Path(`/ping`)" 26 | } 27 | }, 28 | "services": { 29 | "block-all-service": { 30 | "loadBalancer": { 31 | "passHostHeader": false 32 | } 33 | }, 34 | "my-ns-svc-a-8080": { 35 | "loadBalancer": { 36 | "servers": [ 37 | { 38 | "url": "http://10.10.2.1:8080" 39 | }, 40 | { 41 | "url": "http://10.10.2.2:8080" 42 | } 43 | ], 44 | "passHostHeader": true 45 | } 46 | }, 47 | "my-ns-svc-a-8081": { 48 | "loadBalancer": { 49 | "servers": [ 50 | { 51 | "url": "http://10.10.2.1:8080" 52 | }, 53 | { 54 | "url": "http://10.10.2.2:8081" 55 | } 56 | ], 57 | "passHostHeader": true 58 | } 59 | }, 60 | "readiness": { 61 | "loadBalancer": { 62 | "servers": [ 63 | { 64 | "url": "http://127.0.0.1:8080" 65 | } 66 | ], 67 | "passHostHeader": true 68 | } 69 | } 70 | }, 71 | "middlewares": { 72 | "block-all-middleware": { 73 | "ipWhiteList": { 74 | "sourceRange": [ 75 | "255.255.255.255" 76 | ] 77 | } 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /pkg/provider/testdata/acl-disabled-http-basic-topology.json: -------------------------------------------------------------------------------- 1 | { 2 | "services": { 3 | "svc-a@my-ns": { 4 | "name": "svc-a", 5 | "namespace": "my-ns", 6 | "selector": {}, 7 | "annotations": {}, 8 | "ports": [ 9 | { 10 | "name": "port-8080", 11 | "protocol": "TCP", 12 | "port": 8080, 13 | "targetPort": 8080 14 | }, 15 | { 16 | "name": "port-8081", 17 | "protocol": "TCP", 18 | "port": 8081, 19 | "targetPort": "web" 20 | } 21 | ], 22 | "clusterIp": "10.10.14.1", 23 | "pods": [ 24 | "pod-a1@my-ns", 25 | "pod-a2@my-ns" 26 | ] 27 | } 28 | }, 29 | "pods": { 30 | "pod-a1@my-ns": { 31 | "name": "pod-a1", 32 | "namespace": "my-ns", 33 | "serviceAccount": "default", 34 | "ip": "10.10.2.1", 35 | "containerPorts": [ 36 | { 37 | "name": "web", 38 | "protocol": "TCP", 39 | "containerPort": 8080 40 | } 41 | ] 42 | }, 43 | "pod-a2@my-ns": { 44 | "name": "pod-a2", 45 | "namespace": "my-ns", 46 | "serviceAccount": "default", 47 | "ip": "10.10.2.2", 48 | "containerPorts": [ 49 | { 50 | "name": "web", 51 | "protocol": "TCP", 52 | "containerPort": 8081 53 | } 54 | ] 55 | } 56 | }, 57 | "serviceTrafficTargets": {}, 58 | "trafficSplits": {} 59 | } 60 | -------------------------------------------------------------------------------- /pkg/provider/testdata/acl-disabled-http-traffic-split-topology.json: -------------------------------------------------------------------------------- 1 | { 2 | "services": { 3 | "svc-a@my-ns": { 4 | "name": "svc-a", 5 | "namespace": "my-ns", 6 | "selector": {}, 7 | "annotations": {}, 8 | "ports": [ 9 | { 10 | "name": "port-8080", 11 | "protocol": "TCP", 12 | "port": 8080, 13 | "targetPort": 8080 14 | } 15 | ], 16 | "clusterIp": "10.10.14.1", 17 | "pods": [], 18 | "trafficSplits": ["split@my-ns"] 19 | }, 20 | "svc-b@my-ns": { 21 | "name": "svc-b", 22 | "namespace": "my-ns", 23 | "selector": {}, 24 | "annotations": {}, 25 | "ports": [ 26 | { 27 | "name": "port-8080", 28 | "protocol": "TCP", 29 | "port": 8080, 30 | "targetPort": 80 31 | } 32 | ], 33 | "clusterIp": "10.10.15.1", 34 | "pods": [ 35 | "pod-b@my-ns" 36 | ], 37 | "backendOf": ["split@my-ns"] 38 | }, 39 | "svc-c@my-ns": { 40 | "name": "svc-c", 41 | "namespace": "my-ns", 42 | "selector": {}, 43 | "annotations": {}, 44 | "ports": [ 45 | { 46 | "name": "port-8080", 47 | "protocol": "TCP", 48 | "port": 8080, 49 | "targetPort": 80 50 | } 51 | ], 52 | "clusterIp": "10.10.16.1", 53 | "pods": [ 54 | "pod-c@my-ns" 55 | ], 56 | "backendOf": ["split@my-ns"] 57 | } 58 | }, 59 | "pods": { 60 | "pod-b@my-ns": { 61 | "name": "pod-b", 62 | "namespace": "my-ns", 63 | "serviceAccount": "default", 64 | "ip": "10.10.2.1" 65 | }, 66 | "pod-c@my-ns": { 67 | "name": "pod-c", 68 | "namespace": "my-ns", 69 | "serviceAccount": "default", 70 | "ip": "10.10.3.1" 71 | } 72 | }, 73 | "trafficSplits": { 74 | "split@my-ns": { 75 | "name": "split", 76 | "namespace": "my-ns", 77 | "service": "svc-a@my-ns", 78 | "backends": [ 79 | { 80 | "weight": 80, 81 | "service": "svc-b@my-ns" 82 | }, 83 | { 84 | "weight": 20, 85 | "service": "svc-c@my-ns" 86 | } 87 | ] 88 | } 89 | }, 90 | "serviceTrafficTargets": {} 91 | } -------------------------------------------------------------------------------- /pkg/provider/testdata/acl-disabled-tcp-basic-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "http": { 3 | "routers": { 4 | "readiness": { 5 | "entryPoints": [ 6 | "readiness" 7 | ], 8 | "service": "readiness", 9 | "rule": "Path(`/ping`)" 10 | } 11 | }, 12 | "services": { 13 | "block-all-service": { 14 | "loadBalancer": { 15 | "passHostHeader": false 16 | } 17 | }, 18 | "readiness": { 19 | "loadBalancer": { 20 | "servers": [ 21 | { 22 | "url": "http://127.0.0.1:8080" 23 | } 24 | ], 25 | "passHostHeader": true 26 | } 27 | } 28 | }, 29 | "middlewares": { 30 | "block-all-middleware": { 31 | "ipWhiteList": { 32 | "sourceRange": [ 33 | "255.255.255.255" 34 | ] 35 | } 36 | } 37 | } 38 | }, 39 | "tcp": { 40 | "routers": { 41 | "my-ns-svc-a-8080": { 42 | "entryPoints": [ 43 | "tcp-5000" 44 | ], 45 | "service": "my-ns-svc-a-8080", 46 | "rule": "HostSNI(`*`)" 47 | }, 48 | "my-ns-svc-a-8081": { 49 | "entryPoints": [ 50 | "tcp-5001" 51 | ], 52 | "service": "my-ns-svc-a-8081", 53 | "rule": "HostSNI(`*`)" 54 | } 55 | }, 56 | "services": { 57 | "my-ns-svc-a-8080": { 58 | "loadBalancer": { 59 | "servers": [ 60 | { 61 | "address": "10.10.2.1:8080" 62 | }, 63 | { 64 | "address": "10.10.2.2:8080" 65 | } 66 | ] 67 | } 68 | }, 69 | "my-ns-svc-a-8081": { 70 | "loadBalancer": { 71 | "servers": [ 72 | { 73 | "address": "10.10.2.1:8080" 74 | }, 75 | { 76 | "address": "10.10.2.2:8081" 77 | } 78 | ] 79 | } 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /pkg/provider/testdata/acl-disabled-tcp-basic-topology.json: -------------------------------------------------------------------------------- 1 | { 2 | "services": { 3 | "svc-a@my-ns": { 4 | "name": "svc-a", 5 | "namespace": "my-ns", 6 | "selector": {}, 7 | "annotations": {}, 8 | "ports": [ 9 | { 10 | "name": "port-8080", 11 | "protocol": "TCP", 12 | "port": 8080, 13 | "targetPort": 8080 14 | }, 15 | { 16 | "name": "port-8081", 17 | "protocol": "TCP", 18 | "port": 8081, 19 | "targetPort": "web" 20 | } 21 | ], 22 | "clusterIp": "10.10.14.1", 23 | "pods": [ 24 | "pod-a1@my-ns", 25 | "pod-a2@my-ns" 26 | ] 27 | } 28 | }, 29 | "pods": { 30 | "pod-a1@my-ns": { 31 | "name": "pod-a1", 32 | "namespace": "my-ns", 33 | "serviceAccount": "default", 34 | "ip": "10.10.2.1", 35 | "containerPorts": [ 36 | { 37 | "name": "web", 38 | "protocol": "TCP", 39 | "containerPort": 8080 40 | } 41 | ] 42 | }, 43 | "pod-a2@my-ns": { 44 | "name": "pod-a2", 45 | "namespace": "my-ns", 46 | "serviceAccount": "default", 47 | "ip": "10.10.2.2", 48 | "containerPorts": [ 49 | { 50 | "name": "web", 51 | "protocol": "TCP", 52 | "containerPort": 8081 53 | } 54 | ] 55 | } 56 | }, 57 | "serviceTrafficTargets": {}, 58 | "trafficSplits": {} 59 | } 60 | -------------------------------------------------------------------------------- /pkg/provider/testdata/acl-disabled-udp-basic-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "http": { 3 | "routers": { 4 | "readiness": { 5 | "entryPoints": [ 6 | "readiness" 7 | ], 8 | "service": "readiness", 9 | "rule": "Path(`/ping`)" 10 | } 11 | }, 12 | "services": { 13 | "block-all-service": { 14 | "loadBalancer": { 15 | "passHostHeader": false 16 | } 17 | }, 18 | "readiness": { 19 | "loadBalancer": { 20 | "servers": [ 21 | { 22 | "url": "http://127.0.0.1:8080" 23 | } 24 | ], 25 | "passHostHeader": true 26 | } 27 | } 28 | }, 29 | "middlewares": { 30 | "block-all-middleware": { 31 | "ipWhiteList": { 32 | "sourceRange": [ 33 | "255.255.255.255" 34 | ] 35 | } 36 | } 37 | } 38 | }, 39 | "udp": { 40 | "routers": { 41 | "my-ns-svc-a-8080": { 42 | "entryPoints": [ 43 | "udp-15000" 44 | ], 45 | "service": "my-ns-svc-a-8080" 46 | }, 47 | "my-ns-svc-a-8081": { 48 | "entryPoints": [ 49 | "udp-15001" 50 | ], 51 | "service": "my-ns-svc-a-8081" 52 | } 53 | }, 54 | "services": { 55 | "my-ns-svc-a-8080": { 56 | "loadBalancer": { 57 | "servers": [ 58 | { 59 | "address": "10.10.2.1:8080" 60 | }, 61 | { 62 | "address": "10.10.2.2:8080" 63 | } 64 | ] 65 | } 66 | }, 67 | "my-ns-svc-a-8081": { 68 | "loadBalancer": { 69 | "servers": [ 70 | { 71 | "address": "10.10.2.1:8080" 72 | }, 73 | { 74 | "address": "10.10.2.2:8081" 75 | } 76 | ] 77 | } 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /pkg/provider/testdata/acl-disabled-udp-basic-topology.json: -------------------------------------------------------------------------------- 1 | { 2 | "services": { 3 | "svc-a@my-ns": { 4 | "name": "svc-a", 5 | "namespace": "my-ns", 6 | "selector": {}, 7 | "annotations": {}, 8 | "ports": [ 9 | { 10 | "name": "port-8080", 11 | "protocol": "UDP", 12 | "port": 8080, 13 | "targetPort": 8080 14 | }, 15 | { 16 | "name": "port-8081", 17 | "protocol": "UDP", 18 | "port": 8081, 19 | "targetPort": "ping" 20 | } 21 | ], 22 | "clusterIp": "10.10.14.1", 23 | "pods": [ 24 | "pod-a1@my-ns", 25 | "pod-a2@my-ns" 26 | ] 27 | } 28 | }, 29 | "pods": { 30 | "pod-a1@my-ns": { 31 | "name": "pod-a1", 32 | "namespace": "my-ns", 33 | "serviceAccount": "default", 34 | "ip": "10.10.2.1", 35 | "containerPorts": [ 36 | { 37 | "name": "ping", 38 | "protocol": "UDP", 39 | "containerPort": 8080 40 | } 41 | ] 42 | }, 43 | "pod-a2@my-ns": { 44 | "name": "pod-a2", 45 | "namespace": "my-ns", 46 | "serviceAccount": "default", 47 | "ip": "10.10.2.2", 48 | "containerPorts": [ 49 | { 50 | "name": "ping", 51 | "protocol": "UDP", 52 | "containerPort": 8081 53 | } 54 | ] 55 | } 56 | }, 57 | "serviceTrafficTargets": {}, 58 | "trafficSplits": {} 59 | } 60 | -------------------------------------------------------------------------------- /pkg/provider/testdata/acl-enabled-http-basic-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "http": { 3 | "routers": { 4 | "my-ns-svc-b-8080": { 5 | "entryPoints": [ 6 | "http-10000" 7 | ], 8 | "middlewares": [ 9 | "block-all-middleware" 10 | ], 11 | "service": "block-all-service", 12 | "rule": "Host(`svc-b.my-ns.traefik.mesh`) || Host(`svc-b.my-ns.maesh`) || Host(`10.10.14.1`)", 13 | "priority": 1 14 | }, 15 | "my-ns-svc-b-8081": { 16 | "entryPoints": [ 17 | "http-10001" 18 | ], 19 | "middlewares": [ 20 | "block-all-middleware" 21 | ], 22 | "service": "block-all-service", 23 | "rule": "Host(`svc-b.my-ns.traefik.mesh`) || Host(`svc-b.my-ns.maesh`) || Host(`10.10.14.1`)", 24 | "priority": 1 25 | }, 26 | "my-ns-svc-b-tt-8080-traffic-target-direct": { 27 | "entryPoints": [ 28 | "http-10000" 29 | ], 30 | "middlewares": [ 31 | "my-ns-svc-b-tt-whitelist-traffic-target-direct" 32 | ], 33 | "service": "my-ns-svc-b-tt-8080-traffic-target", 34 | "rule": "Host(`svc-b.my-ns.traefik.mesh`) || Host(`svc-b.my-ns.maesh`) || Host(`10.10.14.1`)", 35 | "priority": 2002 36 | }, 37 | "my-ns-svc-b-tt-8081-traffic-target-direct": { 38 | "entryPoints": [ 39 | "http-10001" 40 | ], 41 | "middlewares": [ 42 | "my-ns-svc-b-tt-whitelist-traffic-target-direct" 43 | ], 44 | "service": "my-ns-svc-b-tt-8081-traffic-target", 45 | "rule": "Host(`svc-b.my-ns.traefik.mesh`) || Host(`svc-b.my-ns.maesh`) || Host(`10.10.14.1`)", 46 | "priority": 2002 47 | }, 48 | "readiness": { 49 | "entryPoints": [ 50 | "readiness" 51 | ], 52 | "service": "readiness", 53 | "rule": "Path(`/ping`)" 54 | } 55 | }, 56 | "services": { 57 | "block-all-service": { 58 | "loadBalancer": { 59 | "passHostHeader": false 60 | } 61 | }, 62 | "my-ns-svc-b-tt-8080-traffic-target": { 63 | "loadBalancer": { 64 | "servers": [ 65 | { 66 | "url": "http://10.10.3.1:8080" 67 | } 68 | ], 69 | "passHostHeader": true 70 | } 71 | }, 72 | "my-ns-svc-b-tt-8081-traffic-target": { 73 | "loadBalancer": { 74 | "servers": [ 75 | { 76 | "url": "http://10.10.3.1:8081" 77 | } 78 | ], 79 | "passHostHeader": true 80 | } 81 | }, 82 | "readiness": { 83 | "loadBalancer": { 84 | "servers": [ 85 | { 86 | "url": "http://127.0.0.1:8080" 87 | } 88 | ], 89 | "passHostHeader": true 90 | } 91 | } 92 | }, 93 | "middlewares": { 94 | "block-all-middleware": { 95 | "ipWhiteList": { 96 | "sourceRange": [ 97 | "255.255.255.255" 98 | ] 99 | } 100 | }, 101 | "my-ns-svc-b-tt-whitelist-traffic-target-direct": { 102 | "ipWhiteList": { 103 | "sourceRange": [ 104 | "10.10.2.1" 105 | ] 106 | } 107 | } 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /pkg/provider/testdata/acl-enabled-http-basic-topology.json: -------------------------------------------------------------------------------- 1 | { 2 | "services": { 3 | "svc-b@my-ns": { 4 | "name": "svc-b", 5 | "namespace": "my-ns", 6 | "selector": {}, 7 | "annotations": {}, 8 | "ports": [ 9 | { 10 | "name": "port-8080", 11 | "protocol": "TCP", 12 | "port": 8080, 13 | "targetPort": 8080 14 | }, 15 | { 16 | "name": "port-8081", 17 | "protocol": "TCP", 18 | "port": 8081, 19 | "targetPort": "web" 20 | } 21 | ], 22 | "clusterIp": "10.10.14.1", 23 | "pods": [ 24 | "pod-b@my-ns" 25 | ], 26 | "trafficTargets": [ 27 | "svc-b@my-ns:tt@my-ns" 28 | ] 29 | } 30 | }, 31 | "pods": { 32 | "pod-a@my-ns": { 33 | "name": "pod-a", 34 | "namespace": "my-ns", 35 | "serviceAccount": "client", 36 | "ip": "10.10.2.1" 37 | }, 38 | "pod-b@my-ns": { 39 | "name": "pod-b", 40 | "namespace": "my-ns", 41 | "serviceAccount": "server", 42 | "ip": "10.10.3.1", 43 | "containerPorts": [ 44 | { 45 | "name": "web", 46 | "protocol": "TCP", 47 | "containerPort": 8081 48 | } 49 | ] 50 | } 51 | }, 52 | "serviceTrafficTargets": { 53 | "svc-b@my-ns:tt@my-ns": { 54 | "service": "svc-b@my-ns", 55 | "name": "tt", 56 | "namespace": "my-ns", 57 | "sources": [ 58 | { 59 | "serviceAccount": "client", 60 | "namespace": "my-ns", 61 | "pods": [ 62 | "pod-a@my-ns" 63 | ] 64 | } 65 | ], 66 | "destination": { 67 | "serviceAccount": "server", 68 | "namespace": "my-ns", 69 | "ports": [ 70 | { 71 | "name": "port-8080", 72 | "protocol": "TCP", 73 | "port": 8080, 74 | "targetPort": 8080 75 | }, 76 | { 77 | "name": "port-8081", 78 | "protocol": "TCP", 79 | "port": 8081, 80 | "targetPort": "web" 81 | } 82 | ], 83 | "pods": [ 84 | "pod-b@my-ns" 85 | ] 86 | } 87 | } 88 | }, 89 | "trafficSplits": {} 90 | } 91 | -------------------------------------------------------------------------------- /pkg/provider/testdata/acl-enabled-http-route-group-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "http": { 3 | "routers": { 4 | "my-ns-svc-b-8080": { 5 | "entryPoints": [ 6 | "http-10000" 7 | ], 8 | "middlewares": [ 9 | "block-all-middleware" 10 | ], 11 | "service": "block-all-service", 12 | "rule": "Host(`svc-b.my-ns.traefik.mesh`) || Host(`svc-b.my-ns.maesh`) || Host(`10.10.14.1`)", 13 | "priority": 1 14 | }, 15 | "my-ns-svc-b-tt-8080-traffic-target-direct": { 16 | "entryPoints": [ 17 | "http-10000" 18 | ], 19 | "middlewares": [ 20 | "my-ns-svc-b-tt-whitelist-traffic-target-direct" 21 | ], 22 | "service": "my-ns-svc-b-tt-8080-traffic-target", 23 | "rule": "(Host(`svc-b.my-ns.traefik.mesh`) || Host(`svc-b.my-ns.maesh`) || Host(`10.10.14.1`)) && (PathPrefix(`/{path:app}`) || (PathPrefix(`/{path:api/notifications}`) && Method(`GET`)) || HeadersRegexp(`User-Agent`, `Mozilla/.*`))", 24 | "priority": 2006 25 | }, 26 | "readiness": { 27 | "entryPoints": [ 28 | "readiness" 29 | ], 30 | "service": "readiness", 31 | "rule": "Path(`/ping`)" 32 | } 33 | }, 34 | "services": { 35 | "block-all-service": { 36 | "loadBalancer": { 37 | "passHostHeader": false 38 | } 39 | }, 40 | "my-ns-svc-b-tt-8080-traffic-target": { 41 | "loadBalancer": { 42 | "servers": [ 43 | { 44 | "url": "http://10.10.3.1:8080" 45 | } 46 | ], 47 | "passHostHeader": true 48 | } 49 | }, 50 | "readiness": { 51 | "loadBalancer": { 52 | "servers": [ 53 | { 54 | "url": "http://127.0.0.1:8080" 55 | } 56 | ], 57 | "passHostHeader": true 58 | } 59 | } 60 | }, 61 | "middlewares": { 62 | "block-all-middleware": { 63 | "ipWhiteList": { 64 | "sourceRange": [ 65 | "255.255.255.255" 66 | ] 67 | } 68 | }, 69 | "my-ns-svc-b-tt-whitelist-traffic-target-direct": { 70 | "ipWhiteList": { 71 | "sourceRange": [ 72 | "10.10.2.1" 73 | ] 74 | } 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /pkg/provider/testdata/acl-enabled-tcp-basic-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "http": { 3 | "routers": { 4 | "readiness": { 5 | "entryPoints": [ 6 | "readiness" 7 | ], 8 | "service": "readiness", 9 | "rule": "Path(`/ping`)" 10 | } 11 | }, 12 | "services": { 13 | "block-all-service": { 14 | "loadBalancer": { 15 | "passHostHeader": false 16 | } 17 | }, 18 | "readiness": { 19 | "loadBalancer": { 20 | "servers": [ 21 | { 22 | "url": "http://127.0.0.1:8080" 23 | } 24 | ], 25 | "passHostHeader": true 26 | } 27 | } 28 | }, 29 | "middlewares": { 30 | "block-all-middleware": { 31 | "ipWhiteList": { 32 | "sourceRange": [ 33 | "255.255.255.255" 34 | ] 35 | } 36 | } 37 | } 38 | }, 39 | "tcp": { 40 | "routers": { 41 | "my-ns-svc-b-8080": { 42 | "entryPoints": [ 43 | "tcp-5000" 44 | ], 45 | "service": "my-ns-svc-b-8080", 46 | "rule": "HostSNI(`*`)" 47 | }, 48 | "my-ns-svc-b-8081": { 49 | "entryPoints": [ 50 | "tcp-5001" 51 | ], 52 | "service": "my-ns-svc-b-8081", 53 | "rule": "HostSNI(`*`)" 54 | } 55 | }, 56 | "services": { 57 | "my-ns-svc-b-8080": { 58 | "loadBalancer": { 59 | "servers": [ 60 | { 61 | "address": "10.10.3.1:8080" 62 | } 63 | ] 64 | } 65 | }, 66 | "my-ns-svc-b-8081": { 67 | "loadBalancer": { 68 | "servers": [ 69 | { 70 | "address": "10.10.3.1:8081" 71 | } 72 | ] 73 | } 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /pkg/provider/testdata/acl-enabled-tcp-basic-topology.json: -------------------------------------------------------------------------------- 1 | { 2 | "services": { 3 | "svc-b@my-ns": { 4 | "name": "svc-b", 5 | "namespace": "my-ns", 6 | "selector": {}, 7 | "annotations": {}, 8 | "ports": [ 9 | { 10 | "name": "port-8080", 11 | "protocol": "TCP", 12 | "port": 8080, 13 | "targetPort": 8080 14 | }, 15 | { 16 | "name": "port-8081", 17 | "protocol": "TCP", 18 | "port": 8081, 19 | "targetPort": "web" 20 | } 21 | ], 22 | "clusterIp": "10.10.14.1", 23 | "pods": [ 24 | "pod-b@my-ns" 25 | ], 26 | "trafficTargets": [ 27 | "svc-b@my-ns:tt@my-ns" 28 | ] 29 | } 30 | }, 31 | "pods": { 32 | "pod-a@my-ns": { 33 | "name": "pod-a", 34 | "namespace": "my-ns", 35 | "serviceAccount": "client", 36 | "ip": "10.10.2.1" 37 | }, 38 | "pod-b@my-ns": { 39 | "name": "pod-b", 40 | "namespace": "my-ns", 41 | "serviceAccount": "server", 42 | "ip": "10.10.3.1", 43 | "containerPorts": [ 44 | { 45 | "name": "web", 46 | "protocol": "TCP", 47 | "containerPort": 8081 48 | } 49 | ] 50 | } 51 | }, 52 | "serviceTrafficTargets": { 53 | "svc-b@my-ns:tt@my-ns": { 54 | "service": "svc-b@my-ns", 55 | "name": "tt", 56 | "namespace": "my-ns", 57 | "rules": [ 58 | { 59 | "tcpRoute": { 60 | "kind": "TCPRoute", 61 | "metadata": { 62 | "name": "tcp-route", 63 | "namespace": "my-ns" 64 | } 65 | } 66 | } 67 | ], 68 | "sources": [ 69 | { 70 | "serviceAccount": "client", 71 | "namespace": "my-ns", 72 | "pods": [ 73 | "pod-a@my-ns" 74 | ] 75 | } 76 | ], 77 | "destination": { 78 | "serviceAccount": "server", 79 | "namespace": "my-ns", 80 | "ports": [ 81 | { 82 | "name": "port-8080", 83 | "protocol": "TCP", 84 | "port": 8080, 85 | "targetPort": 8080 86 | }, 87 | { 88 | "name": "port-8081", 89 | "protocol": "TCP", 90 | "port": 8081, 91 | "targetPort": "web" 92 | } 93 | ], 94 | "pods": [ 95 | "pod-b@my-ns" 96 | ] 97 | } 98 | } 99 | }, 100 | "trafficSplits": {} 101 | } 102 | -------------------------------------------------------------------------------- /pkg/provider/testdata/annotations-scheme-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "http": { 3 | "routers": { 4 | "my-ns-svc-a-8080": { 5 | "entryPoints": [ 6 | "http-10000" 7 | ], 8 | "service": "my-ns-svc-a-8080", 9 | "rule": "Host(`svc-a.my-ns.traefik.mesh`) || Host(`svc-a.my-ns.maesh`) || Host(`10.10.14.1`)", 10 | "priority": 1002 11 | }, 12 | "readiness": { 13 | "entryPoints": [ 14 | "readiness" 15 | ], 16 | "service": "readiness", 17 | "rule": "Path(`/ping`)" 18 | } 19 | }, 20 | "services": { 21 | "block-all-service": { 22 | "loadBalancer": { 23 | "passHostHeader": false 24 | } 25 | }, 26 | "my-ns-svc-a-8080": { 27 | "loadBalancer": { 28 | "servers": [ 29 | { 30 | "url": "https://10.10.2.1:8080" 31 | }, 32 | { 33 | "url": "https://10.10.2.2:8080" 34 | } 35 | ], 36 | "passHostHeader": true 37 | } 38 | }, 39 | "readiness": { 40 | "loadBalancer": { 41 | "servers": [ 42 | { 43 | "url": "http://127.0.0.1:8080" 44 | } 45 | ], 46 | "passHostHeader": true 47 | } 48 | } 49 | }, 50 | "middlewares": { 51 | "block-all-middleware": { 52 | "ipWhiteList": { 53 | "sourceRange": [ 54 | "255.255.255.255" 55 | ] 56 | } 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /pkg/provider/testdata/annotations-scheme-topology.json: -------------------------------------------------------------------------------- 1 | { 2 | "services": { 3 | "svc-a@my-ns": { 4 | "name": "svc-a", 5 | "namespace": "my-ns", 6 | "selector": {}, 7 | "annotations": { 8 | "mesh.traefik.io/scheme": "https" 9 | }, 10 | "ports": [ 11 | { 12 | "name": "port-8080", 13 | "protocol": "TCP", 14 | "port": 8080, 15 | "targetPort": 8080 16 | } 17 | ], 18 | "clusterIp": "10.10.14.1", 19 | "pods": [ 20 | "pod-a1@my-ns", 21 | "pod-a2@my-ns" 22 | ] 23 | } 24 | }, 25 | "pods": { 26 | "pod-a1@my-ns": { 27 | "name": "pod-a1", 28 | "namespace": "my-ns", 29 | "serviceAccount": "default", 30 | "ip": "10.10.2.1" 31 | }, 32 | "pod-a2@my-ns": { 33 | "name": "pod-a2", 34 | "namespace": "my-ns", 35 | "serviceAccount": "default", 36 | "ip": "10.10.2.2" 37 | } 38 | }, 39 | "serviceTrafficTargets": {}, 40 | "trafficSplits": {} 41 | } 42 | -------------------------------------------------------------------------------- /pkg/provider/testdata/annotations-traffic-type-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "http": { 3 | "routers": { 4 | "readiness": { 5 | "entryPoints": [ 6 | "readiness" 7 | ], 8 | "service": "readiness", 9 | "rule": "Path(`/ping`)" 10 | } 11 | }, 12 | "services": { 13 | "block-all-service": { 14 | "loadBalancer": { 15 | "passHostHeader": false 16 | } 17 | }, 18 | "readiness": { 19 | "loadBalancer": { 20 | "servers": [ 21 | { 22 | "url": "http://127.0.0.1:8080" 23 | } 24 | ], 25 | "passHostHeader": true 26 | } 27 | } 28 | }, 29 | "middlewares": { 30 | "block-all-middleware": { 31 | "ipWhiteList": { 32 | "sourceRange": [ 33 | "255.255.255.255" 34 | ] 35 | } 36 | } 37 | } 38 | }, 39 | "tcp": { 40 | "routers": { 41 | "my-ns-svc-a-8080": { 42 | "entryPoints": [ 43 | "tcp-5000" 44 | ], 45 | "service": "my-ns-svc-a-8080", 46 | "rule": "HostSNI(`*`)" 47 | } 48 | }, 49 | "services": { 50 | "my-ns-svc-a-8080": { 51 | "loadBalancer": { 52 | "servers": [ 53 | { 54 | "address": "10.10.2.1:8080" 55 | }, 56 | { 57 | "address": "10.10.2.2:8080" 58 | } 59 | ] 60 | } 61 | } 62 | } 63 | }, 64 | "udp": { 65 | "routers": { 66 | "my-ns-svc-b-8080": { 67 | "entryPoints": [ 68 | "udp-15000" 69 | ], 70 | "service": "my-ns-svc-b-8080" 71 | } 72 | }, 73 | "services": { 74 | "my-ns-svc-b-8080": { 75 | "loadBalancer": { 76 | "servers": [ 77 | { 78 | "address": "10.10.2.1:8080" 79 | }, 80 | { 81 | "address": "10.10.2.2:8080" 82 | } 83 | ] 84 | } 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /pkg/provider/testdata/annotations-traffic-type-topology.json: -------------------------------------------------------------------------------- 1 | { 2 | "services": { 3 | "svc-a@my-ns": { 4 | "name": "svc-a", 5 | "namespace": "my-ns", 6 | "selector": {}, 7 | "annotations": { 8 | "mesh.traefik.io/traffic-type": "tcp" 9 | }, 10 | "ports": [ 11 | { 12 | "name": "port-8080", 13 | "protocol": "TCP", 14 | "port": 8080, 15 | "targetPort": 8080 16 | } 17 | ], 18 | "clusterIp": "10.10.14.1", 19 | "pods": [ 20 | "pod-a1@my-ns", 21 | "pod-a2@my-ns" 22 | ] 23 | }, 24 | "svc-b@my-ns": { 25 | "name": "svc-b", 26 | "namespace": "my-ns", 27 | "selector": {}, 28 | "annotations": { 29 | "mesh.traefik.io/traffic-type": "udp" 30 | }, 31 | "ports": [ 32 | { 33 | "name": "port-8080", 34 | "protocol": "UDP", 35 | "port": 8080, 36 | "targetPort": 8080 37 | } 38 | ], 39 | "clusterIp": "10.10.14.1", 40 | "pods": [ 41 | "pod-a1@my-ns", 42 | "pod-a2@my-ns" 43 | ] 44 | } 45 | }, 46 | "pods": { 47 | "pod-a1@my-ns": { 48 | "name": "pod-a1", 49 | "namespace": "my-ns", 50 | "serviceAccount": "default", 51 | "ip": "10.10.2.1" 52 | }, 53 | "pod-a2@my-ns": { 54 | "name": "pod-a2", 55 | "namespace": "my-ns", 56 | "serviceAccount": "default", 57 | "ip": "10.10.2.2" 58 | } 59 | }, 60 | "serviceTrafficTargets": {}, 61 | "trafficSplits": {} 62 | } 63 | -------------------------------------------------------------------------------- /pkg/safe/recover.go: -------------------------------------------------------------------------------- 1 | package safe 2 | 3 | import ( 4 | "fmt" 5 | "runtime/debug" 6 | 7 | "github.com/cenkalti/backoff/v4" 8 | "github.com/sirupsen/logrus" 9 | ) 10 | 11 | // OperationWithRecover wrap a backoff operation in a Recover. 12 | func OperationWithRecover(operation backoff.Operation) backoff.Operation { 13 | return func() (err error) { 14 | defer func() { 15 | if res := recover(); res != nil { 16 | logger := logrus.StandardLogger() 17 | 18 | logger.Errorf("Error in Go routine: %s", res) 19 | logger.Errorf("Stack: %s", debug.Stack()) 20 | 21 | err = fmt.Errorf("panic in operation: %w", err) 22 | } 23 | }() 24 | 25 | return operation() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /pkg/safe/recover_test.go: -------------------------------------------------------------------------------- 1 | package safe 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/cenkalti/backoff/v4" 8 | ) 9 | 10 | func TestOperationWithRecover(t *testing.T) { 11 | operation := func() error { 12 | return nil 13 | } 14 | 15 | err := backoff.Retry(OperationWithRecover(operation), &backoff.StopBackOff{}) 16 | if err != nil { 17 | t.Fatalf("Error in OperationWithRecover: %s", err) 18 | } 19 | } 20 | 21 | func TestOperationWithRecoverPanic(t *testing.T) { 22 | operation := func() error { 23 | panic("BOOM") 24 | } 25 | 26 | err := backoff.Retry(OperationWithRecover(operation), &backoff.StopBackOff{}) 27 | if err == nil { 28 | t.Fatalf("Error in OperationWithRecover: %s", err) 29 | } 30 | } 31 | 32 | func TestOperationWithRecoverError(t *testing.T) { 33 | operation := func() error { 34 | return fmt.Errorf("ERROR") 35 | } 36 | 37 | err := backoff.Retry(OperationWithRecover(operation), &backoff.StopBackOff{}) 38 | if err == nil { 39 | t.Fatalf("Error in OperationWithRecover: %s", err) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /pkg/safe/safe.go: -------------------------------------------------------------------------------- 1 | package safe 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | // Safe contains a thread-safe value. 8 | type Safe struct { 9 | value interface{} 10 | lock sync.RWMutex 11 | } 12 | 13 | // New create a new Safe instance given a value. 14 | func New(value interface{}) *Safe { 15 | return &Safe{value: value} 16 | } 17 | 18 | // Get returns the value. 19 | func (s *Safe) Get() interface{} { 20 | s.lock.RLock() 21 | defer s.lock.RUnlock() 22 | 23 | return s.value 24 | } 25 | 26 | // Set sets a new value. 27 | func (s *Safe) Set(value interface{}) { 28 | s.lock.Lock() 29 | defer s.lock.Unlock() 30 | 31 | s.value = value 32 | } 33 | -------------------------------------------------------------------------------- /pkg/safe/safe_test.go: -------------------------------------------------------------------------------- 1 | package safe 2 | 3 | import "testing" 4 | 5 | func TestSafe(t *testing.T) { 6 | const ts1, ts2 = "test1", "test2" 7 | 8 | s := New(ts1) 9 | 10 | result, ok := s.Get().(string) 11 | if !ok { 12 | t.Fatalf("Safe.Get() failed, got type '%T', expected string", s.Get()) 13 | } 14 | 15 | if result != ts1 { 16 | t.Errorf("Safe.Get() failed, got '%s', expected '%s'", result, ts1) 17 | } 18 | 19 | s.Set(ts2) 20 | 21 | result, ok = s.Get().(string) 22 | if !ok { 23 | t.Fatalf("Safe.Get() after Safe.Set() failed, got type '%T', expected string", s.Get()) 24 | } 25 | 26 | if result != ts2 { 27 | t.Errorf("Safe.Get() after Safe.Set() failed, got '%s', expected '%s'", result, ts2) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /pkg/topology/testdata/topology-empty-destination-port.json: -------------------------------------------------------------------------------- 1 | { 2 | "services": { 3 | "svc-b@my-ns": { 4 | "name": "svc-b", 5 | "namespace": "my-ns", 6 | "selector": { 7 | "app": "app-b" 8 | }, 9 | "annotations": { 10 | "mesh.traefik.io/ratelimit-average": "100", 11 | "mesh.traefik.io/ratelimit-burst": "200", 12 | "mesh.traefik.io/traffic-type": "http" 13 | }, 14 | "ports": [ 15 | { 16 | "name": "port-8080", 17 | "protocol": "TCP", 18 | "port": 8080, 19 | "targetPort": 8080 20 | }, 21 | { 22 | "name": "port-9090", 23 | "protocol": "TCP", 24 | "port": 9090, 25 | "targetPort": 9090 26 | } 27 | ], 28 | "clusterIp": "10.10.1.16", 29 | "pods": [ 30 | "app-b@my-ns" 31 | ], 32 | "trafficTargets": [ 33 | "svc-b@my-ns:tt@my-ns" 34 | ] 35 | } 36 | }, 37 | "pods": { 38 | "app-a@my-ns": { 39 | "name": "app-a", 40 | "namespace": "my-ns", 41 | "serviceAccount": "service-account-a", 42 | "ip": "10.10.1.1", 43 | "sourceOf": [ 44 | "svc-b@my-ns:tt@my-ns" 45 | ] 46 | }, 47 | "app-b@my-ns": { 48 | "name": "app-b", 49 | "namespace": "my-ns", 50 | "serviceAccount": "service-account-b", 51 | "ip": "10.10.2.1", 52 | "destinationOf": [ 53 | "svc-b@my-ns:tt@my-ns" 54 | ] 55 | } 56 | }, 57 | "serviceTrafficTargets": { 58 | "svc-b@my-ns:tt@my-ns": { 59 | "service": "svc-b@my-ns", 60 | "name": "tt", 61 | "namespace": "my-ns", 62 | "sources": [ 63 | { 64 | "serviceAccount": "service-account-a", 65 | "namespace": "my-ns", 66 | "pods": [ 67 | "app-a@my-ns" 68 | ] 69 | } 70 | ], 71 | "destination": { 72 | "serviceAccount": "service-account-b", 73 | "namespace": "my-ns", 74 | "ports": [ 75 | { 76 | "name": "port-8080", 77 | "protocol": "TCP", 78 | "port": 8080, 79 | "targetPort": 8080 80 | }, 81 | { 82 | "name": "port-9090", 83 | "protocol": "TCP", 84 | "port": 9090, 85 | "targetPort": 9090 86 | } 87 | ], 88 | "pods": [ 89 | "app-b@my-ns" 90 | ] 91 | } 92 | } 93 | }, 94 | "trafficSplits": {} 95 | } 96 | -------------------------------------------------------------------------------- /pkg/topology/testdata/topology-multi-sources-destinations.json: -------------------------------------------------------------------------------- 1 | { 2 | "services": { 3 | "svc-c@my-ns": { 4 | "name": "svc-c", 5 | "namespace": "my-ns", 6 | "selector": { 7 | "app": "app-c" 8 | }, 9 | "annotations": {}, 10 | "ports": [ 11 | { 12 | "name": "port-8080", 13 | "protocol": "TCP", 14 | "port": 8080, 15 | "targetPort": 8080 16 | } 17 | ], 18 | "clusterIp": "10.10.1.16", 19 | "pods": [ 20 | "app-c-1@my-ns", 21 | "app-c-2@my-ns" 22 | ], 23 | "trafficTargets": [ 24 | "svc-c@my-ns:tt@my-ns" 25 | ] 26 | } 27 | }, 28 | "pods": { 29 | "app-a@my-ns": { 30 | "name": "app-a", 31 | "namespace": "my-ns", 32 | "serviceAccount": "service-account-a", 33 | "ip": "10.10.1.1", 34 | "sourceOf": [ 35 | "svc-c@my-ns:tt@my-ns" 36 | ] 37 | }, 38 | "app-b@my-ns": { 39 | "name": "app-b", 40 | "namespace": "my-ns", 41 | "serviceAccount": "service-account-b", 42 | "ip": "10.10.2.1", 43 | "sourceOf": [ 44 | "svc-c@my-ns:tt@my-ns" 45 | ] 46 | }, 47 | "app-c-1@my-ns": { 48 | "name": "app-c-1", 49 | "namespace": "my-ns", 50 | "serviceAccount": "service-account-c", 51 | "ip": "10.10.3.1", 52 | "destinationOf": [ 53 | "svc-c@my-ns:tt@my-ns" 54 | ] 55 | }, 56 | "app-c-2@my-ns": { 57 | "name": "app-c-2", 58 | "namespace": "my-ns", 59 | "serviceAccount": "service-account-c", 60 | "ip": "10.10.3.2", 61 | "destinationOf": [ 62 | "svc-c@my-ns:tt@my-ns" 63 | ] 64 | } 65 | }, 66 | "serviceTrafficTargets": { 67 | "svc-c@my-ns:tt@my-ns": { 68 | "service": "svc-c@my-ns", 69 | "name": "tt", 70 | "namespace": "my-ns", 71 | "sources": [ 72 | { 73 | "serviceAccount": "service-account-a", 74 | "namespace": "my-ns", 75 | "pods": [ 76 | "app-a@my-ns" 77 | ] 78 | }, 79 | { 80 | "serviceAccount": "service-account-b", 81 | "namespace": "my-ns", 82 | "pods": [ 83 | "app-b@my-ns" 84 | ] 85 | } 86 | ], 87 | "destination": { 88 | "serviceAccount": "service-account-c", 89 | "namespace": "my-ns", 90 | "ports": [ 91 | { 92 | "name": "port-8080", 93 | "protocol": "TCP", 94 | "port": 8080, 95 | "targetPort": 8080 96 | } 97 | ], 98 | "pods": [ 99 | "app-c-1@my-ns", 100 | "app-c-2@my-ns" 101 | ] 102 | } 103 | } 104 | }, 105 | "trafficSplits": {} 106 | } -------------------------------------------------------------------------------- /pkg/topology/testdata/topology-service-with-pod-port-mixture.json: -------------------------------------------------------------------------------- 1 | { 2 | "services": { 3 | "svc@my-ns": { 4 | "name": "svc", 5 | "namespace": "my-ns", 6 | "selector": { 7 | "app": "my-app" 8 | }, 9 | "ports": [ 10 | { 11 | "name": "port-80", 12 | "port": 80, 13 | "targetPort": "name" 14 | }, 15 | { 16 | "name": "port-8080", 17 | "port": 8080, 18 | "targetPort": 8080 19 | } 20 | ], 21 | "clusterIp": "10.10.1.3", 22 | "pods": [ 23 | "pod-v1@my-ns", 24 | "pod-v2@my-ns" 25 | ], 26 | "trafficTargets": [] 27 | } 28 | }, 29 | "pods": { 30 | "pod-v1@my-ns": { 31 | "name": "pod-v1", 32 | "namespace": "my-ns", 33 | "serviceAccount": "service-account", 34 | "ip": "10.10.1.1", 35 | "sourceOf": [] 36 | }, 37 | "pod-v2@my-ns": { 38 | "name": "pod-v2", 39 | "namespace": "my-ns", 40 | "serviceAccount": "service-account", 41 | "ip": "10.10.1.2", 42 | "destinationOf": [] 43 | } 44 | }, 45 | "serviceTrafficTargets": {}, 46 | "trafficSplits": {} 47 | } 48 | -------------------------------------------------------------------------------- /pkg/topology/testdata/topology-spec-with-empty-match.json: -------------------------------------------------------------------------------- 1 | { 2 | "services": { 3 | "svc-b@my-ns": { 4 | "name": "svc-b", 5 | "namespace": "my-ns", 6 | "selector": { 7 | "app": "app-b" 8 | }, 9 | "annotations": {}, 10 | "ports": [ 11 | { 12 | "name": "port-8080", 13 | "protocol": "TCP", 14 | "port": 8080, 15 | "targetPort": 8080 16 | } 17 | ], 18 | "clusterIp": "10.10.1.16", 19 | "pods": [ 20 | "app-b@my-ns" 21 | ], 22 | "trafficTargets": [ 23 | "svc-b@my-ns:tt@my-ns" 24 | ] 25 | } 26 | }, 27 | "pods": { 28 | "app-a@my-ns": { 29 | "name": "app-a", 30 | "namespace": "my-ns", 31 | "serviceAccount": "service-account-a", 32 | "ip": "10.10.1.1", 33 | "sourceOf": [ 34 | "svc-b@my-ns:tt@my-ns" 35 | ] 36 | }, 37 | "app-b@my-ns": { 38 | "name": "app-b", 39 | "namespace": "my-ns", 40 | "serviceAccount": "service-account-b", 41 | "ip": "10.10.2.1", 42 | "destinationOf": [ 43 | "svc-b@my-ns:tt@my-ns" 44 | ] 45 | } 46 | }, 47 | "trafficSplits": {}, 48 | "serviceTrafficTargets": { 49 | "svc-b@my-ns:tt@my-ns": { 50 | "service": "svc-b@my-ns", 51 | "name": "tt", 52 | "namespace": "my-ns", 53 | "sources": [ 54 | { 55 | "serviceAccount": "service-account-a", 56 | "namespace": "my-ns", 57 | "pods": [ 58 | "app-a@my-ns" 59 | ] 60 | } 61 | ], 62 | "destination": { 63 | "serviceAccount": "service-account-b", 64 | "namespace": "my-ns", 65 | "ports": [ 66 | { 67 | "name": "port-8080", 68 | "protocol": "TCP", 69 | "port": 8080, 70 | "targetPort": 8080 71 | } 72 | ], 73 | "pods": [ 74 | "app-b@my-ns" 75 | ] 76 | }, 77 | "rules": [ 78 | { 79 | "httpRouteGroup": { 80 | "kind": "HTTPRouteGroup", 81 | "apiVersion": "specs.smi-spec.io/v1alpha3", 82 | "metadata": { 83 | "name": "http-rt-grp", 84 | "namespace": "my-ns", 85 | "creationTimestamp": null 86 | }, 87 | "spec": { 88 | "matches": [ 89 | { 90 | "name": "api", 91 | "methods": [ 92 | "GET", 93 | "POST" 94 | ], 95 | "pathRegex": "/api" 96 | }, 97 | { 98 | "name": "metric", 99 | "methods": [ 100 | "GET" 101 | ], 102 | "pathRegex": "/metric" 103 | } 104 | ] 105 | } 106 | }, 107 | "httpMatches": [ 108 | { 109 | "name": "api", 110 | "methods": [ 111 | "GET", 112 | "POST" 113 | ], 114 | "pathRegex": "/api" 115 | }, 116 | { 117 | "name": "metric", 118 | "methods": [ 119 | "GET" 120 | ], 121 | "pathRegex": "/metric" 122 | } 123 | ] 124 | } 125 | ] 126 | } 127 | } 128 | } -------------------------------------------------------------------------------- /pkg/topology/testdata/topology-traffic-target-service-port-mismatch.json: -------------------------------------------------------------------------------- 1 | { 2 | "services": { 3 | "svc-b1@my-ns": { 4 | "name": "svc-b1", 5 | "namespace": "my-ns", 6 | "selector": { 7 | "app": "app-b1" 8 | }, 9 | "annotations": {}, 10 | "ports": [ 11 | { 12 | "name": "port-8080", 13 | "protocol": "TCP", 14 | "port": 8080, 15 | "targetPort": 8080 16 | } 17 | ], 18 | "clusterIp": "10.10.1.16", 19 | "pods": [ 20 | "app-b1@my-ns" 21 | ] 22 | }, 23 | "svc-b2@my-ns": { 24 | "name": "svc-b2", 25 | "namespace": "my-ns", 26 | "selector": { 27 | "app": "app-b2" 28 | }, 29 | "annotations": {}, 30 | "ports": [ 31 | { 32 | "name": "port-80", 33 | "protocol": "TCP", 34 | "port": 80, 35 | "targetPort": 80 36 | } 37 | ], 38 | "clusterIp": "10.10.1.17", 39 | "pods": [ 40 | "app-b2@my-ns" 41 | ], 42 | "trafficTargets": [ 43 | "svc-b2@my-ns:tt@my-ns" 44 | ] 45 | } 46 | }, 47 | "pods": { 48 | "app-a@my-ns": { 49 | "name": "app-a", 50 | "namespace": "my-ns", 51 | "serviceAccount": "service-account-a", 52 | "ip": "10.10.1.1", 53 | "sourceOf": [ 54 | "svc-b2@my-ns:tt@my-ns" 55 | ] 56 | }, 57 | "app-b1@my-ns": { 58 | "name": "app-b1", 59 | "namespace": "my-ns", 60 | "serviceAccount": "service-account-b", 61 | "ip": "10.10.1.2" 62 | }, 63 | "app-b2@my-ns": { 64 | "name": "app-b2", 65 | "namespace": "my-ns", 66 | "serviceAccount": "service-account-b", 67 | "ip": "10.10.1.3", 68 | "destinationOf": [ 69 | "svc-b2@my-ns:tt@my-ns" 70 | ] 71 | } 72 | }, 73 | "serviceTrafficTargets": { 74 | "svc-b1@my-ns:tt@my-ns": { 75 | "service": "svc-b1@my-ns", 76 | "name": "tt", 77 | "namespace": "my-ns", 78 | "sources": [ 79 | { 80 | "serviceAccount": "service-account-a", 81 | "namespace": "my-ns", 82 | "pods": [ 83 | "app-a@my-ns" 84 | ] 85 | } 86 | ], 87 | "destination": { 88 | "serviceAccount": "service-account-b", 89 | "namespace": "my-ns", 90 | "ports": [], 91 | "pods": [ 92 | "app-b1@my-ns" 93 | ] 94 | }, 95 | "errors": [ 96 | "unable to find destination ports on Service \"svc-b1@my-ns\": destination port 80 of TrafficTarget \"tt@my-ns\" is not exposed by the service" 97 | ] 98 | }, 99 | "svc-b2@my-ns:tt@my-ns": { 100 | "service": "svc-b2@my-ns", 101 | "name": "tt", 102 | "namespace": "my-ns", 103 | "sources": [ 104 | { 105 | "serviceAccount": "service-account-a", 106 | "namespace": "my-ns", 107 | "pods": [ 108 | "app-a@my-ns" 109 | ] 110 | } 111 | ], 112 | "destination": { 113 | "serviceAccount": "service-account-b", 114 | "namespace": "my-ns", 115 | "ports": [ 116 | { 117 | "name": "port-80", 118 | "protocol": "TCP", 119 | "port": 80, 120 | "targetPort": 80 121 | } 122 | ], 123 | "pods": [ 124 | "app-b2@my-ns" 125 | ] 126 | } 127 | } 128 | }, 129 | "trafficSplits": {} 130 | } -------------------------------------------------------------------------------- /pkg/topology/testdata/topology-traffic-target.json: -------------------------------------------------------------------------------- 1 | { 2 | "services": { 3 | "svc-b@my-ns": { 4 | "name": "svc-b", 5 | "namespace": "my-ns", 6 | "selector": { 7 | "app": "app-b" 8 | }, 9 | "annotations": {}, 10 | "ports": [ 11 | { 12 | "name": "port-8080", 13 | "protocol": "TCP", 14 | "port": 8080, 15 | "targetPort": 8080 16 | } 17 | ], 18 | "clusterIp": "10.10.1.16", 19 | "pods": [ 20 | "app-b@my-ns" 21 | ], 22 | "trafficTargets": [ 23 | "svc-b@my-ns:tt@my-ns" 24 | ] 25 | } 26 | }, 27 | "pods": { 28 | "app-a@my-ns": { 29 | "name": "app-a", 30 | "namespace": "my-ns", 31 | "serviceAccount": "service-account-a", 32 | "ip": "10.10.1.1", 33 | "sourceOf": [ 34 | "svc-b@my-ns:tt@my-ns" 35 | ] 36 | }, 37 | "app-b@my-ns": { 38 | "name": "app-b", 39 | "namespace": "my-ns", 40 | "serviceAccount": "service-account-b", 41 | "ip": "10.10.2.1", 42 | "destinationOf": [ 43 | "svc-b@my-ns:tt@my-ns" 44 | ] 45 | } 46 | }, 47 | "serviceTrafficTargets": { 48 | "svc-b@my-ns:tt@my-ns": { 49 | "service": "svc-b@my-ns", 50 | "name": "tt", 51 | "namespace": "my-ns", 52 | "sources": [ 53 | { 54 | "serviceAccount": "service-account-a", 55 | "namespace": "my-ns", 56 | "pods": [ 57 | "app-a@my-ns" 58 | ] 59 | } 60 | ], 61 | "destination": { 62 | "serviceAccount": "service-account-b", 63 | "namespace": "my-ns", 64 | "ports": [ 65 | { 66 | "name": "port-8080", 67 | "protocol": "TCP", 68 | "port": 8080, 69 | "targetPort": 8080 70 | } 71 | ], 72 | "pods": [ 73 | "app-b@my-ns" 74 | ] 75 | }, 76 | "rules": [ 77 | { 78 | "httpRouteGroup": { 79 | "kind": "HTTPRouteGroup", 80 | "apiVersion": "specs.smi-spec.io/v1alpha3", 81 | "metadata": { 82 | "name": "http-rt-grp", 83 | "namespace": "my-ns", 84 | "creationTimestamp": null 85 | }, 86 | "spec": { 87 | "matches": [ 88 | { 89 | "name": "api", 90 | "methods": [ 91 | "GET", 92 | "POST" 93 | ], 94 | "pathRegex": "/api", 95 | "headers": [ 96 | { 97 | "User-Agent": "curl/.*" 98 | } 99 | ] 100 | }, 101 | { 102 | "name": "metric", 103 | "methods": [ 104 | "GET" 105 | ], 106 | "pathRegex": "/metric" 107 | } 108 | ] 109 | } 110 | }, 111 | "httpMatches": [ 112 | { 113 | "name": "api", 114 | "methods": [ 115 | "GET", 116 | "POST" 117 | ], 118 | "pathRegex": "/api", 119 | "headers": [ 120 | { 121 | "User-Agent": "curl/.*" 122 | } 123 | ] 124 | } 125 | ] 126 | } 127 | ] 128 | } 129 | }, 130 | "trafficSplits": {} 131 | } -------------------------------------------------------------------------------- /pkg/topology/topology_test.go: -------------------------------------------------------------------------------- 1 | package topology 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | corev1 "k8s.io/api/core/v1" 8 | "k8s.io/apimachinery/pkg/util/intstr" 9 | ) 10 | 11 | func TestTopology_ResolveServicePort(t *testing.T) { 12 | tests := []struct { 13 | desc string 14 | svcPort corev1.ServicePort 15 | containerPorts []corev1.ContainerPort 16 | expPort int32 17 | expResult bool 18 | }{ 19 | { 20 | desc: "should return the service TargetPort if it as Int", 21 | svcPort: corev1.ServicePort{ 22 | TargetPort: intstr.FromInt(3000), 23 | }, 24 | expPort: 3000, 25 | expResult: true, 26 | }, 27 | { 28 | desc: "should return false if the service TargetPort is a String and the container port list is empty", 29 | svcPort: corev1.ServicePort{ 30 | TargetPort: intstr.FromString("foo"), 31 | }, 32 | expPort: 0, 33 | expResult: false, 34 | }, 35 | { 36 | desc: "should return false if the service TargetPort is a String and it cannot be resolved", 37 | svcPort: corev1.ServicePort{ 38 | TargetPort: intstr.FromString("foo"), 39 | Protocol: corev1.ProtocolTCP, 40 | }, 41 | containerPorts: []corev1.ContainerPort{ 42 | { 43 | Name: "bar", 44 | ContainerPort: 3000, 45 | }, 46 | { 47 | Name: "foo", 48 | Protocol: corev1.ProtocolUDP, 49 | ContainerPort: 3000, 50 | }, 51 | }, 52 | expPort: 0, 53 | expResult: false, 54 | }, 55 | { 56 | desc: "should return true and the resolved service TargetPort", 57 | svcPort: corev1.ServicePort{ 58 | TargetPort: intstr.FromString("foo"), 59 | Protocol: corev1.ProtocolUDP, 60 | }, 61 | containerPorts: []corev1.ContainerPort{ 62 | { 63 | Name: "foo", 64 | Protocol: corev1.ProtocolUDP, 65 | ContainerPort: 3000, 66 | }, 67 | }, 68 | expPort: 3000, 69 | expResult: true, 70 | }, 71 | } 72 | 73 | for _, test := range tests { 74 | test := test 75 | t.Run(test.desc, func(t *testing.T) { 76 | t.Parallel() 77 | 78 | port, result := ResolveServicePort(test.svcPort, test.containerPorts) 79 | 80 | assert.Equal(t, test.expResult, result) 81 | assert.Equal(t, test.expPort, port) 82 | }) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /pkg/version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | var ( 4 | // Version holds the version name. 5 | Version = "dev" 6 | // Commit holds the version build commit. 7 | Commit = "I don't remember exactly" 8 | // Date holds the version build date. 9 | Date = "I don't remember exactly" 10 | ) 11 | -------------------------------------------------------------------------------- /spinach.yaml: -------------------------------------------------------------------------------- 1 | # Severities: Ok: 0, Info: 1, Warn: 2, Error: 3 2 | popeye: 3 | codes: 4 | 301: 5 | severity: 1 # Set severity level to Info if pod needs to connect to the API. 6 | 302: 7 | severity: 1 # Set severity level to Info if pod needs to run as root. 8 | -------------------------------------------------------------------------------- /tmpl.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.19-alpine AS builder 2 | 3 | # Package dependencies 4 | RUN apk --no-cache --no-progress add \ 5 | bash \ 6 | gcc \ 7 | git \ 8 | make \ 9 | musl-dev \ 10 | mercurial \ 11 | curl \ 12 | tar \ 13 | ca-certificates \ 14 | tzdata \ 15 | && update-ca-certificates \ 16 | && rm -rf /var/cache/apk/* 17 | 18 | WORKDIR /go/src/github.com/traefik/mesh 19 | 20 | # Download goreleaser binary to bin folder in $GOPATH 21 | RUN curl -sfL https://gist.githubusercontent.com/traefiker/6d7ac019c11d011e4f131bb2cca8900e/raw/goreleaser.sh | sh 22 | 23 | ENV GO111MODULE on 24 | COPY go.mod go.sum ./ 25 | RUN go mod download 26 | COPY . . 27 | 28 | RUN GOARCH={{ .GoARCH }} GOARM={{ .GoARM }} make local-build 29 | 30 | ## IMAGE 31 | FROM {{ .RuntimeImage }} 32 | 33 | RUN addgroup -g 1000 -S app && \ 34 | adduser -u 1000 -S app -G app 35 | 36 | COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ 37 | COPY --from=builder /go/src/github.com/traefik/mesh/dist/traefik-mesh /app/ 38 | 39 | ENTRYPOINT ["/app/traefik-mesh"] 40 | --------------------------------------------------------------------------------