├── .dockerignore
├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── .tmuxinator.yml
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── Vagrantfile
├── cmd
└── k8s-tew
│ ├── ceph.go
│ ├── completion.go
│ ├── configure.go
│ ├── dashboard.go
│ ├── deploy.go
│ ├── environment.go
│ ├── generate.go
│ ├── initialize.go
│ ├── main.go
│ ├── node-add.go
│ ├── node-list.go
│ ├── node-remove.go
│ └── run.go
├── conformance
└── certified-kubernetes-color.svg
├── data
├── data.go
└── templates
│ ├── ceph
│ ├── ceph.conf
│ ├── client-admin.keyring
│ ├── client.keyring
│ └── monitor.keyring
│ ├── k8s
│ ├── config-map.yaml
│ ├── credentials.yaml
│ ├── cri
│ │ └── containerd.toml
│ ├── encryption-config.yaml
│ ├── kube-proxy-configuration.yaml
│ ├── kube-scheduler-configuration.yaml
│ ├── kubeconfig.yaml
│ ├── kubelet-configuration.yaml
│ ├── lb
│ │ └── gobetween.toml
│ ├── manifests
│ │ ├── etcd.yaml
│ │ ├── gobetween.yaml
│ │ ├── kube-apiserver.yaml
│ │ ├── kube-controller-manager.yaml
│ │ ├── kube-proxy.yaml
│ │ ├── kube-scheduler.yaml
│ │ └── virtual-ip.yaml
│ ├── service-account.yaml
│ └── setup
│ │ ├── backup
│ │ └── velero.yaml
│ │ ├── dns
│ │ └── coredns.yaml
│ │ ├── ingress
│ │ └── letsencrypt-cluster-issuer.yaml
│ │ ├── kubelet-setup.yaml
│ │ ├── logging
│ │ └── efk.yaml
│ │ ├── management
│ │ └── kubernetes-dashboard.yaml
│ │ ├── miscellaneous
│ │ └── wordpress.yaml
│ │ ├── monitoring
│ │ ├── alert-manager.yaml
│ │ ├── grafana-dashboards.yaml
│ │ ├── grafana.yaml
│ │ ├── kube-state-metrics.yaml
│ │ ├── metrics-server.yaml
│ │ ├── node-exporter.yaml
│ │ ├── prometheus-alerts.yaml
│ │ ├── prometheus-rules.yaml
│ │ └── prometheus.yaml
│ │ ├── networking
│ │ ├── calico.yaml
│ │ ├── cert-manager.yaml
│ │ ├── metallb.yaml
│ │ └── nginx-ingress.yaml
│ │ └── storage
│ │ ├── ceph-csi.yaml
│ │ ├── ceph-secrets.yaml
│ │ └── ceph-setup.yaml
│ └── system
│ ├── environment.sh
│ ├── k8s-tew.service
│ └── k8s-tew.sh
├── docs
├── .nojekyll
├── Makefile
├── _build
│ ├── doctrees
│ │ ├── about.doctree
│ │ ├── environment.pickle
│ │ ├── features.doctree
│ │ ├── index.doctree
│ │ ├── installation.doctree
│ │ ├── quickstart.doctree
│ │ ├── setups.doctree
│ │ ├── troubleshooting.doctree
│ │ └── usage.doctree
│ └── html
│ │ ├── .buildinfo
│ │ ├── .nojekyll
│ │ ├── _sources
│ │ ├── about.rst.txt
│ │ ├── features.rst.txt
│ │ ├── index.rst.txt
│ │ ├── installation.rst.txt
│ │ ├── quickstart.rst.txt
│ │ ├── setups.rst.txt
│ │ ├── troubleshooting.rst.txt
│ │ └── usage.rst.txt
│ │ ├── _static
│ │ ├── _sphinx_javascript_frameworks_compat.js
│ │ ├── basic.css
│ │ ├── css
│ │ │ ├── badge_only.css
│ │ │ ├── fonts
│ │ │ │ ├── Roboto-Slab-Bold.woff
│ │ │ │ ├── Roboto-Slab-Bold.woff2
│ │ │ │ ├── Roboto-Slab-Regular.woff
│ │ │ │ ├── Roboto-Slab-Regular.woff2
│ │ │ │ ├── fontawesome-webfont.eot
│ │ │ │ ├── fontawesome-webfont.svg
│ │ │ │ ├── fontawesome-webfont.ttf
│ │ │ │ ├── fontawesome-webfont.woff
│ │ │ │ ├── fontawesome-webfont.woff2
│ │ │ │ ├── lato-bold-italic.woff
│ │ │ │ ├── lato-bold-italic.woff2
│ │ │ │ ├── lato-bold.woff
│ │ │ │ ├── lato-bold.woff2
│ │ │ │ ├── lato-normal-italic.woff
│ │ │ │ ├── lato-normal-italic.woff2
│ │ │ │ ├── lato-normal.woff
│ │ │ │ └── lato-normal.woff2
│ │ │ └── theme.css
│ │ ├── doctools.js
│ │ ├── documentation_options.js
│ │ ├── file.png
│ │ ├── jquery.js
│ │ ├── js
│ │ │ ├── badge_only.js
│ │ │ └── theme.js
│ │ ├── language_data.js
│ │ ├── logo.svg
│ │ ├── minus.png
│ │ ├── plus.png
│ │ ├── pygments.css
│ │ ├── searchtools.js
│ │ └── sphinx_highlight.js
│ │ ├── about.html
│ │ ├── features.html
│ │ ├── genindex.html
│ │ ├── index.html
│ │ ├── installation.html
│ │ ├── objects.inv
│ │ ├── quickstart.html
│ │ ├── search.html
│ │ ├── searchindex.js
│ │ ├── setups.html
│ │ ├── troubleshooting.html
│ │ └── usage.html
├── _static
│ └── .gitkeep
├── _templates
│ └── .gitkeep
├── about.rst
├── conf.py
├── eval
├── features.rst
├── index.html
├── index.rst
├── installation.rst
├── quickstart.rst
├── setups.rst
├── troubleshooting.rst
└── usage.rst
├── go.mod
├── go.sum
├── logo.svg
├── pkg
├── ceph
│ └── ceph.go
├── config
│ ├── asset_config.go
│ ├── asset_directory.go
│ ├── asset_file.go
│ ├── command.go
│ ├── config.go
│ ├── features.go
│ ├── images.go
│ ├── internal_config.go
│ ├── labels.go
│ ├── logger_config.go
│ ├── node.go
│ ├── server_config.go
│ └── versions.go
├── container
│ ├── image
│ │ ├── converter
│ │ │ ├── converter.go
│ │ │ └── layers.go
│ │ ├── manifest
│ │ │ ├── config.go
│ │ │ ├── file_system_layers.go
│ │ │ ├── history.go
│ │ │ └── manifest.go
│ │ └── storage
│ │ │ └── storage.go
│ └── pods.go
├── deployment
│ ├── deployment.go
│ └── node_deployment.go
├── download
│ └── download.go
├── generate
│ └── generate.go
├── k8s
│ └── k8s.go
├── pki
│ └── pki.go
├── servers
│ ├── server.go
│ ├── server_wrapper.go
│ └── servers.go
├── utils
│ ├── checksums.go
│ ├── constants.go
│ ├── limiter.go
│ ├── logger.go
│ ├── progress.go
│ ├── tasks.go
│ ├── templates.go
│ └── utils.go
└── version
│ └── version.go
└── setup
├── centos-multi-node
└── Makefile
├── centos-single-node
└── Makefile
├── checks
├── backup.sh
└── ceph.sh
├── docker
├── Dockerfile
└── Makefile
├── local
└── Makefile
├── multipass
└── Makefile
├── ubuntu-multi-node
└── Makefile
└── ubuntu-single-node
└── Makefile
/.dockerignore:
--------------------------------------------------------------------------------
1 | setup
2 | vendor
3 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Makefile CI
2 |
3 | on:
4 | push:
5 | tags: ['*']
6 |
7 | jobs:
8 | build:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v3
12 | - name: Make
13 | run: make
14 | - name: Create release
15 | id: create_release
16 | uses: actions/create-release@v1
17 | env:
18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
19 | with:
20 | tag_name: ${{ github.ref }}
21 | release_name: Release ${{ github.ref }}
22 | body: |
23 | Release ${{ github.ref }}
24 | draft: false
25 | prerelease: false
26 | - name: Upload release asset
27 | id: upload-release-asset
28 | uses: actions/upload-release-asset@v1
29 | env:
30 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
31 | with:
32 | upload_url: ${{ steps.create_release.outputs.upload_url }}
33 | asset_path: ./k8s-tew
34 | asset_name: k8s-tew
35 | asset_content_type: application/octet-stream
36 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vagrant
2 | assets
3 | tags
4 | utils/a_utils-packr.go
5 | embedded
6 | bin
7 | vendor
8 | .env
9 | /k8s-tew
10 | sandbox
11 | setup/docker/files
12 |
--------------------------------------------------------------------------------
/.tmuxinator.yml:
--------------------------------------------------------------------------------
1 | name: k8s-tew
2 | on_project_start: export PATH=$(pwd):$PATH
3 |
4 | windows:
5 | - editor:
6 | layout: main-horizontal
7 | panes:
8 | - vi
9 | - make watch-and-compile
10 | - shell:
11 | - sandbox-command:
12 | - cd setup/sandbox/vbox-single
13 | - sandbox-environment:
14 | - cd setup/sandbox/vbox-single
15 | - source <(k8s-tew environment)
16 | - k9s:
17 | - cd setup/sandbox/vbox-single
18 | - source <(k8s-tew environment)
19 | - k9s
20 | - documentation-update:
21 | - make watch-and-update-documentation
22 | - documentation-server:
23 | - (cd docs/_build/html && python -m http.server 8080)
24 | - port-forwarding-80:
25 | - make forward-80
26 | - port-forwarding-443:
27 | - make forward-443
28 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | ARG GO_VERSION=1.22.2
2 |
3 | FROM golang:${GO_VERSION}-alpine AS builder
4 |
5 | RUN apk add --update --no-cache ca-certificates make git curl mercurial build-base
6 |
7 | ARG PACKAGE=github.com/darxkies/k8s-tew
8 | ARG WORKING_DIRECTORY=/go/src/${PACKAGE}/
9 |
10 | ENV GO111MODULE=on
11 |
12 | RUN echo ${WORKING_DIRECOTORY}
13 | RUN mkdir -p ${WORKING_DIRECTORY}
14 |
15 | WORKDIR ${WORKING_DIRECTORY}
16 |
17 | COPY go.mod go.sum ${WORKING_DIRECTORY}
18 |
19 | RUN go mod download
20 |
21 | RUN git config --global --add safe.directory /go/src/github.com/darxkies/k8s-tew
22 |
23 | CMD ["make", "build-binaries"]
24 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | BUILD_IMAGE = darxkies/k8s-tew-build
2 | VERSION = $(shell git describe --tags)
3 | PACKAGE = github.com/darxkies/k8s-tew
4 |
5 | compile:
6 | docker buildx build --ulimit memlock=-1:-1 -t $(BUILD_IMAGE) .
7 | docker run --rm --ulimit memlock=-1:-1 -v $$(pwd):/go/src/$(PACKAGE) $(BUILD_IMAGE)
8 |
9 | build-binaries:
10 | CGO_ENABLED=0 go build -ldflags "-X ${PACKAGE}/pkg/version.Version=${VERSION} -s -w" -o k8s-tew ${PACKAGE}/cmd/k8s-tew
11 |
12 | watch-and-compile:
13 | go get github.com/cespare/reflex
14 | reflex -r '\.(go|yaml)$$' -R '^vendor' -R '^setup' -R '^pkg/utils/a_utils-packr\.go$$' make build-binaries
15 |
16 | watch-and-update-documentation:
17 | (cd docs && reflex -r '\.rst' -R "^_build" make clean html)
18 |
19 | clean:
20 | sudo rm -Rf bin vendor
21 |
22 | .PHONY: build clean
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |

2 |
3 | 
4 |
5 | # Kubernetes - The Easier Way (k8s-tew)
6 |
7 | [](https://goreportcard.com/report/github.com/darxkies/k8s-tew)
8 | [](https://github.com/darxkies/k8s-tew/releases/latest)
9 | [](https://github.com/darxkies/k8s-tew/releases/latest)
10 | 
11 |
12 | k8s-tew is a CLI tool to install a [Kubernetes](https://kubernetes.io/) Cluster (local, single-node, multi-node or HA-cluster) on Bare Metal. It installs the most essential components needed by a cluster such as networking, storage, monitoring, logging, backuping/restoring and so on. Besides that, k8s-tew is also a supervisor that starts all cluster components on each node, once it setup the nodes.
13 |
14 | ## TL;DR
15 |
16 | [](https://www.youtube.com/watch?v=53qQa5EkBTU)
17 |
18 | # Documentation
19 |
20 | The project documentation can be found here: [https://darxkies.github.io/k8s-tew](https://darxkies.github.io/k8s-tew)
21 |
22 | # Caveats
23 |
24 | - The local setup uses for ingress the ports 80, 443 so they need to be free on the host. It also turns swapping off which is a requirement for kubelet.
25 | - On CentOS nodes the firewall and SELinux are disabled to not interfere with Kubernetes.
26 |
27 | # Feedback
28 |
29 | - E-Mail: darxkies@gmail.com
30 |
--------------------------------------------------------------------------------
/cmd/k8s-tew/completion.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "os"
7 |
8 | "github.com/spf13/cobra"
9 | )
10 |
11 | var completionShell string
12 |
13 | var completionCmd = &cobra.Command{
14 | Use: "completion",
15 | Short: "Generates bash completion scripts",
16 | Long: `To load bash completion run
17 |
18 | . <(k8s-tew completion)
19 |
20 | or
21 |
22 | . <(k8s-tew completion bash)
23 |
24 | and for zsh run
25 |
26 | . <(k8s-tew completion zsh)
27 |
28 | To configure your bash shell to load completions for each session add to your bashrc
29 |
30 | # ~/.bashrc or ~/.profile
31 | . <(k8s-tew completion)
32 | `,
33 | RunE: func(cmd *cobra.Command, args []string) error {
34 | if len(args) > 1 {
35 | return errors.New("Please specify only the shell: bash or zsh")
36 | }
37 |
38 | if len(args) == 0 || args[0] == "bash" {
39 | return RootCmd.GenBashCompletion(os.Stdout)
40 |
41 | } else if args[0] == "zsh" {
42 | return RootCmd.GenZshCompletion(os.Stdout)
43 |
44 | }
45 |
46 | return fmt.Errorf("Unknown shell '%s'", args[0])
47 | },
48 | ValidArgs: []string{"bash", "zsh"},
49 | }
50 |
51 | func init() {
52 | RootCmd.AddCommand(completionCmd)
53 | }
54 |
--------------------------------------------------------------------------------
/cmd/k8s-tew/deploy.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "os"
5 | "path"
6 |
7 | "github.com/darxkies/k8s-tew/pkg/deployment"
8 | "github.com/darxkies/k8s-tew/pkg/utils"
9 |
10 | log "github.com/sirupsen/logrus"
11 | "github.com/spf13/cobra"
12 | )
13 |
14 | var identityFile string
15 | var commandRetries uint
16 | var skipSetup bool
17 | var skipUpload bool
18 | var skipRestart bool
19 | var skipStorageSetup bool
20 | var skipMonitoringSetup bool
21 | var skipLoggingSetup bool
22 | var skipBackupSetup bool
23 | var skipShowcaseSetup bool
24 | var skipIngressSetup bool
25 | var forceUpload bool
26 | var importImages bool
27 | var wait uint
28 |
29 | var deployCmd = &cobra.Command{
30 | Use: "deploy",
31 | Short: "Deploy assets to a remote cluster",
32 | Long: "Deploy assets to a remote cluster",
33 | Run: func(cmd *cobra.Command, args []string) {
34 | if error := bootstrap(false); error != nil {
35 | log.WithFields(log.Fields{"error": error}).Error("Failed initializing")
36 |
37 | os.Exit(-1)
38 | }
39 |
40 | _deployment := deployment.NewDeployment(_config, identityFile, importImages, forceUpload, parallel, commandRetries, skipSetup, skipUpload, skipRestart, skipStorageSetup, skipMonitoringSetup, skipLoggingSetup, skipBackupSetup, skipShowcaseSetup, skipIngressSetup, wait)
41 |
42 | utils.SetProgressSteps(_deployment.Steps() + 1)
43 |
44 | utils.ShowProgress()
45 |
46 | if error := _deployment.Deploy(); error != nil {
47 | log.WithFields(log.Fields{"error": error}).Error("Failed deploying")
48 |
49 | os.Exit(-2)
50 | }
51 |
52 | utils.HideProgress()
53 |
54 | log.Info("Done")
55 | },
56 | }
57 |
58 | func init() {
59 | deployCmd.Flags().StringVarP(&identityFile, "identity-file", "i", path.Join(os.Getenv("HOME"), ".ssh/id_rsa"), "SSH identity file")
60 | deployCmd.Flags().UintVarP(&commandRetries, "command-retries", "r", 1200, "The number of command retries during the setup")
61 | deployCmd.Flags().BoolVar(&skipSetup, "skip-setup", false, "Skip setup steps")
62 | deployCmd.Flags().BoolVar(&skipUpload, "skip-upload", false, "Skip upload steps")
63 | deployCmd.Flags().BoolVar(&skipRestart, "skip-restart", false, "Skip restart steps")
64 | deployCmd.Flags().BoolVar(&skipStorageSetup, "skip-storage-setup", false, "Skip storage setup and all other feature setup steps")
65 | deployCmd.Flags().BoolVar(&skipMonitoringSetup, "skip-monitoring-setup", false, "Skip monitoring setup")
66 | deployCmd.Flags().BoolVar(&skipLoggingSetup, "skip-logging-setup", false, "Skip logging setup")
67 | deployCmd.Flags().BoolVar(&skipBackupSetup, "skip-backup-setup", false, "Skip backup setup")
68 | deployCmd.Flags().BoolVar(&skipShowcaseSetup, "skip-showcase-setup", false, "Skip showcase setup")
69 | deployCmd.Flags().BoolVar(&skipIngressSetup, "skip-ingress-setup", false, "Skip ingress setup")
70 | deployCmd.Flags().BoolVar(&importImages, "import-images", false, "Install images")
71 | deployCmd.Flags().BoolVar(¶llel, "parallel", false, "Run steps in parallel")
72 | deployCmd.Flags().BoolVar(&forceUpload, "force-upload", false, "Files are uploaded without checking if they are already installed")
73 | deployCmd.Flags().UintVar(&wait, "wait", 0, "Wait for all cluster relevant pods to be ready and jobs to be completed. The parameter reflects the number of seconds in which the pods have to run stable.")
74 | RootCmd.AddCommand(deployCmd)
75 | }
76 |
--------------------------------------------------------------------------------
/cmd/k8s-tew/environment.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "strings"
7 |
8 | "github.com/darxkies/k8s-tew/pkg/utils"
9 |
10 | log "github.com/sirupsen/logrus"
11 | "github.com/spf13/cobra"
12 | )
13 |
14 | var environmentCmd = &cobra.Command{
15 | Use: "environment",
16 | Short: "Set environment variables",
17 | Long: `To set the environment variables run
18 |
19 | . <(k8s-tew environment)
20 | `,
21 | Run: func(cmd *cobra.Command, args []string) {
22 | // Load config and check the rights
23 | if error := bootstrap(false); error != nil {
24 | log.WithFields(log.Fields{"error": error}).Error("Failed initializing")
25 |
26 | os.Exit(-1)
27 | }
28 |
29 | checkShell := func(name string) bool {
30 | command := fmt.Sprintf("/proc/%d/exe -c 'echo $%s_VERSION'", os.Getppid(), strings.ToUpper(name))
31 |
32 | output, error := utils.RunCommandWithOutput(command)
33 |
34 | return error == nil && len(strings.TrimSpace(output)) > 0
35 | }
36 |
37 | shell := ""
38 |
39 | // Figure out if bash or zsh
40 | if checkShell("bash") {
41 | shell = "bash"
42 | } else if checkShell("zsh") {
43 | shell = "zsh"
44 | }
45 |
46 | outputBashCompletion := func(binaryName string) {
47 | if len(shell) == 0 {
48 | return
49 | }
50 |
51 | binary := _config.GetFullLocalAssetFilename(binaryName)
52 |
53 | command := fmt.Sprintf("%s completion %s", binary, shell)
54 |
55 | content, error := utils.RunCommandWithOutput(command)
56 | if error != nil {
57 | return
58 | }
59 |
60 | fmt.Println(content)
61 | }
62 |
63 | outputBashCompletion(utils.BinaryK8sTew)
64 | outputBashCompletion(utils.BinaryKubectl)
65 | outputBashCompletion(utils.BinaryHelm)
66 | outputBashCompletion(utils.BinaryVelero)
67 | outputBashCompletion(utils.BinaryCrictl)
68 |
69 | kubeConfig := _config.GetFullLocalAssetFilename(utils.KubeconfigAdmin)
70 |
71 | if !utils.FileExists(kubeConfig) {
72 | kubeConfig = ""
73 | }
74 |
75 | content, error := utils.ApplyTemplate("Environment", utils.GetTemplate(utils.TemplateEnvironment), struct {
76 | CurrentPath string
77 | K8STEWPath string
78 | K8SPath string
79 | EtcdPath string
80 | CRIPath string
81 | CNIPath string
82 | VeleroPath string
83 | HostPath string
84 | KubeConfig string
85 | ContainerdSock string
86 | }{
87 | CurrentPath: os.Getenv("PATH"),
88 | K8STEWPath: _config.GetFullLocalAssetDirectory(utils.DirectoryBinaries),
89 | K8SPath: _config.GetFullLocalAssetDirectory(utils.DirectoryK8sBinaries),
90 | EtcdPath: _config.GetFullLocalAssetDirectory(utils.DirectoryEtcdBinaries),
91 | CRIPath: _config.GetFullLocalAssetDirectory(utils.DirectoryCriBinaries),
92 | CNIPath: _config.GetFullLocalAssetDirectory(utils.DirectoryCniBinaries),
93 | VeleroPath: _config.GetFullLocalAssetDirectory(utils.DirectoryVeleroBinaries),
94 | HostPath: _config.GetFullLocalAssetDirectory(utils.DirectoryHostBinaries),
95 | KubeConfig: kubeConfig,
96 | ContainerdSock: _config.GetFullTargetAssetFilename(utils.ContainerdSock),
97 | }, false)
98 |
99 | if error != nil {
100 | log.WithFields(log.Fields{"error": error}).Error("Failed generating environment")
101 |
102 | os.Exit(-1)
103 | }
104 |
105 | fmt.Println(content)
106 | },
107 | }
108 |
109 | func init() {
110 | RootCmd.AddCommand(environmentCmd)
111 | }
112 |
--------------------------------------------------------------------------------
/cmd/k8s-tew/generate.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/darxkies/k8s-tew/pkg/download"
5 | "github.com/darxkies/k8s-tew/pkg/generate"
6 | "github.com/darxkies/k8s-tew/pkg/utils"
7 |
8 | "os"
9 |
10 | log "github.com/sirupsen/logrus"
11 | "github.com/spf13/cobra"
12 | )
13 |
14 | var forceDownload bool
15 | var parallel bool
16 | var pullImages bool
17 |
18 | var generateCmd = &cobra.Command{
19 | Use: "generate",
20 | Short: "Generate assets",
21 | Long: "Generate assets",
22 | Run: func(cmd *cobra.Command, args []string) {
23 | // Load config and check the rights
24 | if error := bootstrap(false); error != nil {
25 | log.WithFields(log.Fields{"error": error}).Error("Generate failed")
26 |
27 | os.Exit(-1)
28 | }
29 |
30 | if !_config.Config.Nodes.HasControllerNode() {
31 | log.WithFields(log.Fields{"error": "No controller node found"}).Error("Generate failed")
32 |
33 | os.Exit(-2)
34 | }
35 |
36 | if !_config.Config.Nodes.HasWorkerNode() {
37 | log.WithFields(log.Fields{"error": "No worker node found"}).Error("Generate failed")
38 |
39 | os.Exit(-3)
40 | }
41 |
42 | if !_config.Config.Nodes.HasStorageNode() {
43 | log.WithFields(log.Fields{"error": "No storage node found"}).Error("Generate failed")
44 |
45 | os.Exit(-4)
46 | }
47 |
48 | downloader := download.NewDownloader(_config, forceDownload, parallel, pullImages)
49 | generator := generate.NewGenerator(_config)
50 |
51 | utils.SetProgressSteps(2 + downloader.Steps() + generator.Steps() + 1)
52 |
53 | utils.ShowProgress()
54 |
55 | _config.Generate()
56 |
57 | log.Info("Generated config entries")
58 |
59 | utils.IncreaseProgressStep()
60 |
61 | if error := _config.Save(); error != nil {
62 | log.WithFields(log.Fields{"error": error}).Error("Generate failed")
63 |
64 | os.Exit(-1)
65 | }
66 |
67 | utils.IncreaseProgressStep()
68 |
69 | // Download binaries
70 | if error := downloader.DownloadBinaries(); error != nil {
71 | log.WithFields(log.Fields{"error": error}).Error("Generate failed")
72 |
73 | os.Exit(-1)
74 | }
75 |
76 | // Download binaries
77 | if error := generator.GenerateFiles(); error != nil {
78 | log.WithFields(log.Fields{"error": error}).Error("Generate failed")
79 |
80 | os.Exit(-1)
81 | }
82 |
83 | utils.HideProgress()
84 |
85 | log.Info("Done")
86 | },
87 | }
88 |
89 | func init() {
90 | generateCmd.Flags().UintVarP(&commandRetries, "command-retries", "r", 300, "The count of command retries during the setup")
91 | generateCmd.Flags().BoolVar(&forceDownload, "force-download", false, "Force downloading all binary dependencies from the internet")
92 | generateCmd.Flags().BoolVar(¶llel, "parallel", false, "Download binary dependencies in parallel")
93 | generateCmd.Flags().BoolVar(&pullImages, "pull-images", false, "Pull and convert images to OCI to be deployed later on")
94 | RootCmd.AddCommand(generateCmd)
95 | }
96 |
--------------------------------------------------------------------------------
/cmd/k8s-tew/initialize.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "os"
5 |
6 | "github.com/darxkies/k8s-tew/pkg/config"
7 | "github.com/darxkies/k8s-tew/pkg/utils"
8 |
9 | log "github.com/sirupsen/logrus"
10 | "github.com/spf13/cobra"
11 | )
12 |
13 | var force bool
14 |
15 | var initializeCmd = &cobra.Command{
16 | Use: "initialize",
17 | Short: "Initialize the configuration",
18 | Long: "Initialize the configuration",
19 | Run: func(cmd *cobra.Command, args []string) {
20 | _config = config.NewInternalConfig(getBaseDirectory())
21 |
22 | if force {
23 | utils.SetProgressSteps(3)
24 | } else {
25 | utils.SetProgressSteps(2)
26 | }
27 |
28 | utils.ShowProgress()
29 |
30 | if force {
31 | log.Info("Forcing initialization")
32 |
33 | oldConfig := config.NewInternalConfig(getBaseDirectory())
34 |
35 | // Extract Cluster ID from old configuration and pass it to the new configuration
36 | if error := oldConfig.Load(); error == nil {
37 | _config.Config.ClusterID = oldConfig.Config.ClusterID
38 | }
39 |
40 | utils.IncreaseProgressStep()
41 |
42 | } else {
43 | if error := _config.Load(); error == nil {
44 | log.WithFields(log.Fields{"error": "already initialized"}).Error("Initialize failed")
45 |
46 | os.Exit(-1)
47 | }
48 | }
49 |
50 | if error := _config.Save(); error != nil {
51 | log.WithFields(log.Fields{"error": error}).Error("Initialize failed")
52 |
53 | os.Exit(-1)
54 | }
55 |
56 | utils.IncreaseProgressStep()
57 |
58 | log.Info("Done")
59 | },
60 | }
61 |
62 | func init() {
63 | initializeCmd.Flags().BoolVarP(&force, "force", "f", false, "Force initialization if already initialized. This will basically remove the previous configuration.")
64 | RootCmd.AddCommand(initializeCmd)
65 | }
66 |
--------------------------------------------------------------------------------
/cmd/k8s-tew/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "os"
7 | "path"
8 |
9 | "github.com/darxkies/k8s-tew/pkg/config"
10 | "github.com/darxkies/k8s-tew/pkg/utils"
11 | "github.com/darxkies/k8s-tew/pkg/version"
12 |
13 | log "github.com/sirupsen/logrus"
14 |
15 | "github.com/spf13/cobra"
16 | _ "github.com/spf13/viper"
17 | )
18 |
19 | var debug *bool
20 | var hideProgress *bool
21 | var baseDirectory string
22 | var _config *config.InternalConfig
23 |
24 | func init() {
25 | utils.SetupLogger()
26 | }
27 |
28 | // RootCmd represents the base command when called without any subcommands
29 | var RootCmd = &cobra.Command{
30 | Use: "k8s-tew",
31 | Short: utils.ProjectTitle,
32 | Long: utils.ProjectTitle,
33 | Run: func(cmd *cobra.Command, args []string) {
34 | fmt.Printf("Version: %s\n", version.Version)
35 | fmt.Printf("OS: %s\n", utils.GetOSNameAndRelease())
36 | fmt.Println()
37 |
38 | _ = cmd.Help()
39 | },
40 | }
41 |
42 | // getDefaultBaseDirectory returns the base directory based on the current working directory
43 | func getDefaultBaseDirectory() string {
44 | directory, error := os.Getwd()
45 | if error != nil {
46 | log.WithFields(log.Fields{"error": error}).Fatal("Failed to retrieve cwd")
47 | }
48 |
49 | return path.Join(directory, utils.BaseDirectory)
50 | }
51 |
52 | // getBaseDirectory returns the base directory pointing to the assets
53 | func getBaseDirectory() string {
54 | result := baseDirectory
55 |
56 | environmentBaseDirectory := os.Getenv(utils.K8sTewBaseDirectory)
57 |
58 | if len(environmentBaseDirectory) > 0 {
59 | result = environmentBaseDirectory
60 | }
61 |
62 | return result
63 | }
64 |
65 | // bootstrap loads the configuration and performs other checks such as the need for root rights
66 | func bootstrap(needsRoot bool) error {
67 | utils.SetDebug(*debug)
68 | utils.SupressProgress(*hideProgress)
69 |
70 | if needsRoot && !utils.IsRoot() {
71 | return errors.New("this program needs root rights")
72 | }
73 |
74 | _config = config.NewInternalConfig(getBaseDirectory())
75 |
76 | return _config.Load()
77 | }
78 |
79 | func main() {
80 | debug = RootCmd.PersistentFlags().BoolP("debug", "d", false, "Show debug messages")
81 | hideProgress = RootCmd.PersistentFlags().Bool("hide-progress", false, "Hide progress")
82 | RootCmd.PersistentFlags().StringVar(&baseDirectory, "base-directory", getDefaultBaseDirectory(), "Base directory")
83 |
84 | if _error := RootCmd.Execute(); _error != nil {
85 | fmt.Println(_error)
86 |
87 | os.Exit(-1)
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/cmd/k8s-tew/node-add.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "os"
7 | "strings"
8 |
9 | "github.com/darxkies/k8s-tew/pkg/utils"
10 |
11 | log "github.com/sirupsen/logrus"
12 | "github.com/spf13/cobra"
13 | )
14 |
15 | var nodeName string
16 | var nodeIP string
17 | var nodeIndex uint
18 | var nodeStorageIndex uint
19 | var nodeLabels string
20 | var nodeSelf bool
21 |
22 | func addNode() error {
23 | // Load config and check the rights
24 | if error := bootstrap(false); error != nil {
25 | return error
26 | }
27 |
28 | utils.SetProgressSteps(1)
29 |
30 | var error error
31 |
32 | labels := []string{}
33 |
34 | for _, label := range strings.Split(nodeLabels, ",") {
35 | labels = append(labels, strings.Trim(label, "\n "))
36 | }
37 |
38 | if nodeSelf {
39 | log.Debug("Adding self as node")
40 |
41 | // Get ip of the node
42 | nodeIP, error = utils.RunCommandWithOutput("ip route get 8.8.8.8 | cut -d ' ' -f 7")
43 | if error != nil {
44 | return error
45 | }
46 |
47 | // Parse the ip
48 | nodeIP = strings.Trim(nodeIP, "\n")
49 |
50 | // Throw error if the IP could not be retrieved
51 | if len(nodeIP) == 0 {
52 | return errors.New("Could not find own IP")
53 | }
54 |
55 | // Set name of the node
56 | nodeName, error = os.Hostname()
57 | if error != nil {
58 | return error
59 | }
60 |
61 | // Set labels
62 | labels = []string{utils.NodeBootstrapper, utils.NodeController, utils.NodeWorker, utils.NodeStorage}
63 |
64 | // Get public network settings
65 | network, error := utils.RunCommandWithOutput(fmt.Sprintf("ip address | grep %s | cut -d ' ' -f 6", nodeIP))
66 | if error != nil {
67 | return error
68 | }
69 |
70 | // Set public network settings
71 | _config.Config.PublicNetwork = network
72 |
73 | // Set deployment directory by assigning the base directory
74 | _config.Config.DeploymentDirectory = _config.BaseDirectory
75 | }
76 |
77 | node, nodeName, error := _config.AddNode(nodeName, nodeIP, nodeIndex, nodeStorageIndex, labels)
78 | if error != nil {
79 | return error
80 | }
81 |
82 | log.WithFields(log.Fields{"name": nodeName, "ip": node.IP, "index": node.Index, "storage-index": node.StorageIndex, "labels": node.Labels}).Info("Node added")
83 |
84 | if error := _config.Save(); error != nil {
85 | return error
86 | }
87 |
88 | return nil
89 | }
90 |
91 | var nodeAddCmd = &cobra.Command{
92 | Use: "node-add",
93 | Short: "Add or update a node",
94 | Long: "Add a node. This can be also called when updating a node, only the name has to be unique.",
95 | Run: func(cmd *cobra.Command, args []string) {
96 | if error := addNode(); error != nil {
97 | log.WithFields(log.Fields{"error": error}).Error("Failed to add node")
98 |
99 | os.Exit(-1)
100 | }
101 | },
102 | }
103 |
104 | func init() {
105 | nodeAddCmd.Flags().StringVarP(&nodeName, "name", "n", "single-node", "The hostname of the node")
106 | nodeAddCmd.Flags().StringVarP(&nodeIP, "ip", "i", "192.168.100.50", "IP of the node")
107 | nodeAddCmd.Flags().UintVarP(&nodeIndex, "index", "x", 0, "The unique index of the node which should never be reused; if it is already in use a new one is assigned")
108 | nodeAddCmd.Flags().UintVarP(&nodeStorageIndex, "storage-index", "r", 0, "The unique index of the storage node which should never be reused; if it is already in use a new one is assigned")
109 | nodeAddCmd.Flags().StringVarP(&nodeLabels, "labels", "l", fmt.Sprintf("%s,%s", utils.NodeController, utils.NodeWorker), "The labels of the node which define the attributes of the node")
110 | nodeAddCmd.Flags().BoolVarP(&nodeSelf, "self", "s", false, "Add this machine by inferring the host's name & IP and by setting the labels controller,worker,bootstrapper - The public-network and the deployment-directory are also updated")
111 | RootCmd.AddCommand(nodeAddCmd)
112 | }
113 |
--------------------------------------------------------------------------------
/cmd/k8s-tew/node-list.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "os"
5 |
6 | "github.com/darxkies/k8s-tew/pkg/utils"
7 | log "github.com/sirupsen/logrus"
8 | "github.com/spf13/cobra"
9 | )
10 |
11 | func listNodes() error {
12 | // Load config and check the rights
13 | if error := bootstrap(false); error != nil {
14 | return error
15 | }
16 |
17 | utils.SetProgressSteps(1)
18 |
19 | for name, node := range _config.Config.Nodes {
20 | log.WithFields(log.Fields{"index": node.Index, "name": name, "ip": node.IP, "labels": node.Labels}).Info("Node")
21 | }
22 |
23 | return nil
24 | }
25 |
26 | var nodeListCmd = &cobra.Command{
27 | Use: "node-list",
28 | Short: "List nodes",
29 | Long: "List nodes",
30 | Run: func(cmd *cobra.Command, args []string) {
31 | if error := listNodes(); error != nil {
32 | log.WithFields(log.Fields{"error": error}).Error("Failed to list nodes")
33 |
34 | os.Exit(-1)
35 | }
36 | },
37 | }
38 |
39 | func init() {
40 | RootCmd.AddCommand(nodeListCmd)
41 | }
42 |
--------------------------------------------------------------------------------
/cmd/k8s-tew/node-remove.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "os"
5 |
6 | "github.com/darxkies/k8s-tew/pkg/utils"
7 | log "github.com/sirupsen/logrus"
8 | "github.com/spf13/cobra"
9 | )
10 |
11 | var removeNodeName string
12 |
13 | func removeNode() error {
14 | // Load config and check the rights
15 | if error := bootstrap(false); error != nil {
16 | return error
17 | }
18 |
19 | utils.SetProgressSteps(1)
20 |
21 | if error := _config.RemoveNode(removeNodeName); error != nil {
22 | return error
23 | }
24 |
25 | log.WithFields(log.Fields{"name": removeNodeName}).Info("Node removed")
26 |
27 | if error := _config.Save(); error != nil {
28 | return error
29 | }
30 |
31 | return nil
32 | }
33 |
34 | var nodeRemoveCmd = &cobra.Command{
35 | Use: "node-remove",
36 | Short: "Remove a node",
37 | Long: "Remove a node",
38 | Run: func(cmd *cobra.Command, args []string) {
39 | if error := removeNode(); error != nil {
40 | log.WithFields(log.Fields{"error": error}).Error("Failed to remove node")
41 |
42 | os.Exit(-1)
43 | }
44 | },
45 | }
46 |
47 | func init() {
48 | nodeRemoveCmd.Flags().StringVarP(&removeNodeName, "name", "n", "", "Unique name of the node")
49 | RootCmd.AddCommand(nodeRemoveCmd)
50 | }
51 |
--------------------------------------------------------------------------------
/cmd/k8s-tew/run.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "os"
5 |
6 | "github.com/darxkies/k8s-tew/pkg/container"
7 | "github.com/darxkies/k8s-tew/pkg/servers"
8 | "github.com/darxkies/k8s-tew/pkg/utils"
9 | "github.com/spf13/cobra"
10 |
11 | log "github.com/sirupsen/logrus"
12 | )
13 |
14 | var killContainers bool
15 |
16 | var runCmd = &cobra.Command{
17 | Use: "run",
18 | Short: "Run",
19 | Long: "Run servers",
20 | Run: func(cmd *cobra.Command, args []string) {
21 | if error := bootstrap(true); error != nil {
22 | log.WithFields(log.Fields{"error": error}).Error("Failed initialization")
23 |
24 | os.Exit(-1)
25 | }
26 |
27 | if len(_config.Config.Nodes) == 0 {
28 | log.WithFields(log.Fields{"error": "no nodes defined"}).Error("Failed to run")
29 |
30 | os.Exit(-1)
31 | }
32 |
33 | if _config.Node == nil {
34 | log.WithFields(log.Fields{"error": "current host not found in the list of nodes"}).Error("Failed to run")
35 |
36 | os.Exit(-1)
37 | }
38 |
39 | serversContainer := servers.NewServers(_config)
40 |
41 | utils.SetProgressSteps(serversContainer.Steps())
42 |
43 | utils.ShowProgress()
44 |
45 | if error := serversContainer.Run(commandRetries, func() {
46 | if killContainers {
47 | pods := container.NewPods(_config)
48 |
49 | pods.Kill()
50 | }
51 |
52 | }); error != nil {
53 | log.WithFields(log.Fields{"error": error}).Error("Failed to run")
54 |
55 | os.Exit(-1)
56 | }
57 |
58 | },
59 | }
60 |
61 | func init() {
62 | runCmd.Flags().UintVarP(&commandRetries, "command-retries", "r", 300, "The count of command retries")
63 | runCmd.Flags().BoolVarP(&killContainers, "kill-containers", "k", true, "Kill containers when shutting down")
64 | RootCmd.AddCommand(runCmd)
65 | }
66 |
--------------------------------------------------------------------------------
/data/data.go:
--------------------------------------------------------------------------------
1 | package data
2 |
3 | import "embed"
4 |
5 | //go:embed templates
6 | var Templates embed.FS
7 |
--------------------------------------------------------------------------------
/data/templates/ceph/ceph.conf:
--------------------------------------------------------------------------------
1 | [global]
2 | fsid = {{.ClusterID}}
3 |
4 | auth cluster required = cephx
5 | auth service required = cephx
6 | auth client required = cephx
7 |
8 | public network = {{.PublicNetwork}}
9 | cluster network = {{.ClusterNetwork}}
10 |
11 | osd objectstore = bluestore
12 | osd journal size = 100
13 | osd max object name len = 256
14 | osd max object namespace len = 64
15 | osd pg bits = 11
16 | osd pgp bits = 11
17 | osd pool default size = {{len .StorageNodes}}
18 | osd pool default min size = 1
19 |
20 | rbd_default_features = 3
21 |
22 | fatal signal handlers = false
23 |
24 | mon_allow_pool_delete = true
25 | mon_clock_drift_allowed = 1
26 |
27 | log file = /dev/null
28 |
29 | [mon]
30 | keyring = {{$.MonKeyringTemplate}}
31 | mon data = {{$.MonDataTemplate}}
32 |
33 | [osd]
34 | keyring = {{$.OsdKeyringTemplate}}
35 | osd data = {{$.OsdDataTemplate}}
36 | osd journal = {{$.OsdJournalTemplate}}
37 |
38 | {{- range $index, $node := .StorageControllers}}
39 |
40 | [mon.{{$node.Name}}]
41 | host = {{$node.Name}}
42 | mon addr = {{$node.IP}}:6789
43 | {{- end}}
44 |
45 | {{- range $index, $node := .StorageNodes}}
46 |
47 | [mds.{{$node.Name}}]
48 | host = {{$node.Name}}
49 | {{- end}}
50 |
51 | {{- range $index, $node := .StorageNodes}}
52 |
53 | [ods.{{$node.StorageIndex}}]
54 | host = {{$node.Name}}
55 | {{- end}}
56 |
--------------------------------------------------------------------------------
/data/templates/ceph/client-admin.keyring:
--------------------------------------------------------------------------------
1 | [client.admin]
2 | key = {{.Key | unescape}}
3 | auid = 0
4 | caps mds = "allow"
5 | caps mgr = "allow *"
6 | caps mon = "allow *"
7 | caps osd = "allow *"
8 |
--------------------------------------------------------------------------------
/data/templates/ceph/client.keyring:
--------------------------------------------------------------------------------
1 | [client.{{.Name}}]
2 | key = {{.Key | unescape}}
3 | caps mon = "allow profile {{.Name}}"
4 |
--------------------------------------------------------------------------------
/data/templates/ceph/monitor.keyring:
--------------------------------------------------------------------------------
1 | [mon.]
2 | key = {{.MonitorKey | unescape}}
3 | caps mon = "allow *"
4 | [client.admin]
5 | key = {{.ClientAdminKey | unescape}}
6 | auid = 0
7 | caps mds = "allow"
8 | caps mgr = "allow *"
9 | caps mon = "allow *"
10 | caps osd = "allow *"
11 | [client.bootstrap-mds]
12 | key = {{.ClientBootstrapMetadataServerKey | unescape}}
13 | caps mon = "allow profile bootstrap-mds"
14 | [client.bootstrap-osd]
15 | key = {{.ClientBootstrapObjectStorageKey | unescape}}
16 | caps mon = "allow profile bootstrap-osd"
17 | [client.bootstrap-rbd]
18 | key = {{.ClientBootstrapRadosBlockDeviceKey | unescape}}
19 | caps mon = "allow profile bootstrap-rbd"
20 | [client.bootstrap-rgw]
21 | key = {{.ClientBootstrapRadosGatewayKey | unescape}}
22 | caps mon = "allow profile bootstrap-rgw"
23 | [client.k8s-tew]
24 | key = {{.ClientK8STEWKey | unescape}}
25 | caps mon = "allow r"
26 | caps mds = "allow rw"
27 | caps osd = "allow rwx pool={{.CephRbdPoolName}}, allow class-read object_prefix rbd_children, allow rwx pool={{.CephFsPoolName}}, allow rwx pool={{.CephFsMetadataPoolName}}"
28 | caps mgr = "allow rw"
29 |
--------------------------------------------------------------------------------
/data/templates/k8s/config-map.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | kind: Namespace
4 | metadata:
5 | name: {{.Namespace}}
6 |
7 | ---
8 | apiVersion: v1
9 | kind: ConfigMap
10 | metadata:
11 | namespace: {{.Namespace}}
12 | name: {{.Name}}
13 | data:
14 | {{range $key, $value := .Data -}}
15 | {{$key}}: |
16 | {{ $value | file}}
17 | {{end}}
18 |
--------------------------------------------------------------------------------
/data/templates/k8s/credentials.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | kind: Namespace
4 | metadata:
5 | name: {{.Namespace}}
6 |
7 | ---
8 | apiVersion: v1
9 | kind: Secret
10 | metadata:
11 | namespace: {{.Namespace}}
12 | name: {{.SecretName}}
13 | type: Opaque
14 | data:
15 | {{range $key, $value := .Data -}}
16 | {{$key}}: "{{ $value | base64 }}"
17 | {{end}}
18 |
--------------------------------------------------------------------------------
/data/templates/k8s/cri/containerd.toml:
--------------------------------------------------------------------------------
1 | version = 2
2 | root = "{{.ContainerdRootDirectory}}"
3 | state = "{{.ContainerdStateDirectory}}"
4 | plugin_dir = ""
5 | disabled_plugins = []
6 | required_plugins = []
7 | oom_score = 0
8 |
9 | [grpc]
10 | address = "{{.ContainerdSock}}"
11 | tcp_address = ""
12 | tcp_tls_cert = ""
13 | tcp_tls_key = ""
14 | uid = 0
15 | gid = 0
16 | max_recv_message_size = 16777216
17 | max_send_message_size = 16777216
18 |
19 | [ttrpc]
20 | address = ""
21 | uid = 0
22 | gid = 0
23 |
24 | [debug]
25 | address = ""
26 | uid = 0
27 | gid = 0
28 | level = ""
29 |
30 | [metrics]
31 | address = ""
32 | grpc_histogram = false
33 |
34 | [cgroup]
35 | path = ""
36 |
37 | [timeouts]
38 | "io.containerd.timeout.shim.cleanup" = "5s"
39 | "io.containerd.timeout.shim.load" = "5s"
40 | "io.containerd.timeout.shim.shutdown" = "3s"
41 | "io.containerd.timeout.task.state" = "2s"
42 |
43 | [plugins]
44 | [plugins."io.containerd.gc.v1.scheduler"]
45 | pause_threshold = 0.02
46 | deletion_threshold = 0
47 | mutation_threshold = 100
48 | schedule_delay = "0s"
49 | startup_delay = "100ms"
50 | [plugins."io.containerd.grpc.v1.cri"]
51 | disable_tcp_service = true
52 | stream_server_address = "{{.IP}}"
53 | stream_server_port = "0"
54 | stream_idle_timeout = "4h0m0s"
55 | enable_selinux = false
56 | sandbox_image = "{{.PauseImage}}"
57 | stats_collect_period = 10
58 | systemd_cgroup = false
59 | enable_tls_streaming = false
60 | max_container_log_line_size = 16384
61 | disable_cgroup = false
62 | disable_apparmor = false
63 | restrict_oom_score_adj = false
64 | max_concurrent_downloads = 3
65 | disable_proc_mount = false
66 | [plugins."io.containerd.grpc.v1.cri".containerd]
67 | snapshotter = "overlayfs"
68 | default_runtime_name = "runc"
69 | no_pivot = false
70 | [plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
71 | [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
72 | runtime_type = "io.containerd.runc.v2"
73 | runtime_engine = ""
74 | runtime_root = ""
75 | privileged_without_host_devices = false
76 | [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
77 | BinaryName = "{{.CRIBinariesDirectory}}/runc"
78 | [plugins."io.containerd.grpc.v1.cri".cni]
79 | bin_dir = "{{.CNIBinariesDirectory}}"
80 | conf_dir = "{{.CNIConfigDirectory}}"
81 | max_conf_num = 1
82 | conf_template = ""
83 | [plugins."io.containerd.grpc.v1.cri".registry]
84 | [plugins."io.containerd.grpc.v1.cri".registry.mirrors]
85 | [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
86 | endpoint = ["https://registry-1.docker.io"]
87 | [plugins."io.containerd.grpc.v1.cri".x509_key_pair_streaming]
88 | tls_cert_file = ""
89 | tls_key_file = ""
90 | [plugins."io.containerd.internal.v1.opt"]
91 | path = "/opt/containerd"
92 | [plugins."io.containerd.internal.v1.restart"]
93 | interval = "10s"
94 | [plugins."io.containerd.metadata.v1.bolt"]
95 | content_sharing_policy = "shared"
96 | [plugins."io.containerd.monitor.v1.cgroups"]
97 | no_prometheus = false
98 | [plugins."io.containerd.runtime.v1.linux"]
99 | shim = "{{.CRIBinariesDirectory}}/containerd-shim"
100 | runtime = "{{.CRIBinariesDirectory}}/runc"
101 | runtime_root = ""
102 | no_shim = false
103 | shim_debug = false
104 | [plugins."io.containerd.runtime.v2.task"]
105 | platforms = ["linux/amd64"]
106 | [plugins."io.containerd.service.v1.diff-service"]
107 | default = ["walking"]
108 | [plugins."io.containerd.snapshotter.v1.devmapper"]
109 | root_path = ""
110 | pool_name = ""
111 | base_image_size = ""
112 |
--------------------------------------------------------------------------------
/data/templates/k8s/encryption-config.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: EncryptionConfig
3 | resources:
4 | - resources:
5 | - secrets
6 | providers:
7 | - aescbc:
8 | keys:
9 | - name: key1
10 | secret: {{.EncryptionKey | unescape}}
11 | - identity: {}
12 |
--------------------------------------------------------------------------------
/data/templates/k8s/kube-proxy-configuration.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kubeproxy.config.k8s.io/v1alpha1
2 | kind: KubeProxyConfiguration
3 | clientConnection:
4 | kubeconfig: "{{.KubeConfig}}"
5 | clusterCIDR: "{{.ClusterCIDR}}"
6 | mode: "iptables"
7 | metricsBindAddress: "0.0.0.0:10249"
8 |
--------------------------------------------------------------------------------
/data/templates/k8s/kube-scheduler-configuration.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kubescheduler.config.k8s.io/v1
2 | kind: KubeSchedulerConfiguration
3 | clientConnection:
4 | kubeconfig: "{{.KubeConfig}}"
5 | leaderElection:
6 | leaderElect: true
7 |
--------------------------------------------------------------------------------
/data/templates/k8s/kubeconfig.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Config
3 | clusters:
4 | - cluster:
5 | certificate-authority-data: {{.CAData}}
6 | server: https://{{.APIServer}}
7 | name: kubernetes-the-easier-way
8 | users:
9 | - name: {{.Name}}
10 | user:
11 | client-certificate-data: {{.CertificateData}}
12 | client-key-data: {{.KeyData}}
13 | contexts:
14 | - context:
15 | cluster: kubernetes-the-easier-way
16 | user: {{.User}}
17 | name: default
18 | current-context: default
19 |
--------------------------------------------------------------------------------
/data/templates/k8s/kubelet-configuration.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kubelet.config.k8s.io/v1beta1
2 | kind: KubeletConfiguration
3 | authentication:
4 | anonymous:
5 | enabled: false
6 | webhook:
7 | enabled: true
8 | x509:
9 | clientCAFile: "{{.CA}}"
10 | authorization:
11 | mode: Webhook
12 | clusterDomain: "cluster.local"
13 | clusterDNS:
14 | - "{{.ClusterDNSIP}}"
15 | podCIDR: "{{.PODCIDR}}"
16 | runtimeRequestTimeout: "15m"
17 | tlsCertFile: "{{.CertificateFilename}}"
18 | tlsPrivateKeyFile: "{{.KeyFilename}}"
19 | staticPodPath: "{{.StaticPodPath}}"
20 | failSwapOn: false
21 | resolvConf: "{{.ResolvConf}}"
22 | readOnlyPort: 10255
23 | maxPods: {{.MaxPods}}
24 | containerRuntimeEndpoint: {{.ContainerRuntimeEndpoint}}
25 |
--------------------------------------------------------------------------------
/data/templates/k8s/lb/gobetween.toml:
--------------------------------------------------------------------------------
1 | [servers.kube-apiserver]
2 | bind = "0.0.0.0:{{ .LoadBalancerPort }}"
3 | protocol = "tcp"
4 | balance = "roundrobin"
5 |
6 | max_connections = 10000
7 | client_idle_timeout = "10m"
8 | backend_idle_timeout = "10m"
9 | backend_connection_timeout = "2s"
10 |
11 | [servers.kube-apiserver.discovery]
12 | kind = "static"
13 | static_list = [ {{ .KubeAPIServers | quoted_string_list }} ]
14 |
--------------------------------------------------------------------------------
/data/templates/k8s/manifests/etcd.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | namespace: kube-system
5 | name: etcd
6 | labels:
7 | app.kubernetes.io/name: etcd
8 | app.kubernetes.io/instance: {{.Name}}
9 | cluster-relevant: "true"
10 | cluster-weight: "100"
11 | spec:
12 | hostNetwork: true
13 | containers:
14 | - name: etcd
15 | image: {{.EtcdImage}}
16 | command:
17 | - etcd
18 | - --advertise-client-urls=https://{{.NodeIP}}:2379
19 | - --cert-file={{.PemKubernetes}}
20 | - --client-cert-auth
21 | - --data-dir={{.EtcdDataDirectory}}
22 | - --initial-advertise-peer-urls=https://{{.NodeIP}}:2380
23 | - --initial-cluster={{.EtcdCluster}}
24 | - --initial-cluster-state=new
25 | - --initial-cluster-token=etcd-cluster
26 | - --key-file={{.PemKubernetesKey}}
27 | - --listen-client-urls=https://{{.NodeIP}}:2379
28 | - --listen-peer-urls=https://{{.NodeIP}}:2380
29 | - --name={{.Name}}
30 | - --peer-cert-file={{.PemKubernetes}}
31 | - --peer-client-cert-auth
32 | - --peer-key-file={{.PemKubernetesKey}}
33 | - --peer-trusted-ca-file={{.PemCA}}
34 | - --trusted-ca-file={{.PemCA}}
35 | - --listen-metrics-urls=http://{{.NodeIP}}:2381
36 | readinessProbe:
37 | httpGet:
38 | path: /health
39 | port: 2381
40 | scheme: HTTP
41 | initialDelaySeconds: 1
42 | timeoutSeconds: 5
43 | failureThreshold: 5
44 | livenessProbe:
45 | httpGet:
46 | path: /health
47 | port: 2381
48 | scheme: HTTP
49 | initialDelaySeconds: 10
50 | timeoutSeconds: 10
51 | failureThreshold: 5
52 | volumeMounts:
53 | - name: etcd-data-directory
54 | mountPath: {{.EtcdDataDirectory}}
55 | - name: pem-ca
56 | mountPath: {{.PemCA}}
57 | readOnly: true
58 | - name: pem-kubernetes
59 | mountPath: {{.PemKubernetes}}
60 | readOnly: true
61 | - name: pem-kubernetes-key
62 | mountPath: {{.PemKubernetesKey}}
63 | readOnly: true
64 | volumes:
65 | - name: etcd-data-directory
66 | hostPath:
67 | type: DirectoryOrCreate
68 | path: {{.EtcdDataDirectory}}
69 | - name: pem-ca
70 | hostPath:
71 | type: File
72 | path: {{.PemCA}}
73 | - name: pem-kubernetes
74 | hostPath:
75 | type: File
76 | path: {{.PemKubernetes}}
77 | - name: pem-kubernetes-key
78 | hostPath:
79 | type: File
80 | path: {{.PemKubernetesKey}}
81 |
--------------------------------------------------------------------------------
/data/templates/k8s/manifests/gobetween.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | namespace: kube-system
5 | name: gobetween
6 | labels:
7 | cluster-relevant: "true"
8 | cluster-weight: "98"
9 | spec:
10 | hostNetwork: true
11 | containers:
12 | - name: gobetween
13 | image: {{.GobetweenImage}}
14 | command:
15 | - /gobetween
16 | - -c
17 | - {{.Config}}
18 | resources:
19 | requests:
20 | cpu: 100m
21 | volumeMounts:
22 | - name: config
23 | mountPath: {{.Config}}
24 | readOnly: true
25 | volumes:
26 | - name: config
27 | hostPath:
28 | type: File
29 | path: {{.Config}}
30 |
--------------------------------------------------------------------------------
/data/templates/k8s/manifests/kube-apiserver.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | namespace: kube-system
5 | name: kube-apiserver
6 | labels:
7 | cluster-relevant: "true"
8 | cluster-weight: "95"
9 | spec:
10 | hostNetwork: true
11 | containers:
12 | - name: kube-apiserver
13 | image: {{.KubernetesImage}}
14 | command:
15 | - kube-apiserver
16 | - --advertise-address={{.NodeIP}}
17 | - --allow-privileged=true
18 | - --apiserver-count={{.ControllersCount}}
19 | - --audit-log-maxage=30
20 | - --audit-log-maxbackup=3
21 | - --audit-log-maxsize=100
22 | - --audit-log-path={{.AuditLog}}
23 | - --authorization-mode=Node,RBAC
24 | - --bind-address=0.0.0.0
25 | - --client-ca-file={{.PemCA}}
26 | - --enable-admission-plugins=NamespaceLifecycle,NodeRestriction,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota
27 | - --enable-aggregator-routing=true
28 | - --etcd-cafile={{.PemCA}}
29 | - --etcd-certfile={{.PemKubernetes}}
30 | - --etcd-keyfile={{.PemKubernetesKey}}
31 | - --etcd-servers={{.EtcdServers}}
32 | - --event-ttl=1h
33 | - --encryption-provider-config={{.EncryptionConfig}}
34 | - --kubelet-certificate-authority={{.PemCA}}
35 | - --kubelet-client-certificate={{.PemKubernetes}}
36 | - --kubelet-client-key={{.PemKubernetesKey}}
37 | - --proxy-client-cert-file={{.PemAggregator}}
38 | - --proxy-client-key-file={{.PemAggregatorKey}}
39 | - --requestheader-allowed-names=aggregator,admin,system:kube-controller-manager,system:kube-controller-manager,system:kube-scheduler,system:node:single-node
40 | - --requestheader-client-ca-file={{.PemCA}}
41 | - --requestheader-extra-headers-prefix=X-Remote-Extra-
42 | - --requestheader-group-headers=X-Remote-Group
43 | - --requestheader-username-headers=X-Remote-User
44 | - --runtime-config=api/all=true
45 | - --secure-port={{.APIServerPort}}
46 | - --service-account-signing-key-file={{.PemServiceAccountKey}}
47 | - --service-account-key-file={{.PemServiceAccount}}
48 | - --service-account-issuer=https://kubernetes.default.svc.{{.ClusterDomain}}
49 | - --service-cluster-ip-range={{.ClusterIPRange}}
50 | - --service-node-port-range=30000-32767
51 | - --tls-cert-file={{.PemKubernetes}}
52 | - --tls-private-key-file={{.PemKubernetesKey}}
53 | livenessProbe:
54 | failureThreshold: 8
55 | httpGet:
56 | scheme: HTTPS
57 | host: {{.NodeIP}}
58 | port: 6443
59 | path: /healthz
60 | initialDelaySeconds: 15
61 | timeoutSeconds: 15
62 | resources:
63 | requests:
64 | cpu: 250m
65 | volumeMounts:
66 | - name: pem-ca
67 | mountPath: {{.PemCA}}
68 | readOnly: true
69 | - name: pem-kubernetes
70 | mountPath: {{.PemKubernetes}}
71 | readOnly: true
72 | - name: pem-kubernetes-key
73 | mountPath: {{.PemKubernetesKey}}
74 | readOnly: true
75 | - name: pem-aggregator
76 | mountPath: {{.PemAggregator}}
77 | readOnly: true
78 | - name: pem-aggregator-key
79 | mountPath: {{.PemAggregatorKey}}
80 | readOnly: true
81 | - name: pem-service-account
82 | mountPath: {{.PemServiceAccount}}
83 | readOnly: true
84 | - name: pem-service-account-key
85 | mountPath: {{.PemServiceAccountKey}}
86 | readOnly: true
87 | - name: encryption-config
88 | mountPath: {{.EncryptionConfig}}
89 | readOnly: true
90 | - name: audit-log
91 | mountPath: {{.AuditLog}}
92 | volumes:
93 | - name: pem-ca
94 | hostPath:
95 | type: File
96 | path: {{.PemCA}}
97 | - name: pem-kubernetes
98 | hostPath:
99 | type: File
100 | path: {{.PemKubernetes}}
101 | - name: pem-kubernetes-key
102 | hostPath:
103 | type: File
104 | path: {{.PemKubernetesKey}}
105 | - name: pem-aggregator
106 | hostPath:
107 | type: File
108 | path: {{.PemAggregator}}
109 | - name: pem-aggregator-key
110 | hostPath:
111 | type: File
112 | path: {{.PemAggregatorKey}}
113 | - name: pem-service-account
114 | hostPath:
115 | type: File
116 | path: {{.PemServiceAccount}}
117 | - name: pem-service-account-key
118 | hostPath:
119 | type: File
120 | path: {{.PemServiceAccountKey}}
121 | - name: encryption-config
122 | hostPath:
123 | type: File
124 | path: {{.EncryptionConfig}}
125 | - name: audit-log
126 | hostPath:
127 | type: FileOrCreate
128 | path: {{.AuditLog}}
129 |
--------------------------------------------------------------------------------
/data/templates/k8s/manifests/kube-controller-manager.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | namespace: kube-system
5 | name: kube-controller-manager
6 | labels:
7 | cluster-relevant: "true"
8 | cluster-weight: "90"
9 | spec:
10 | hostNetwork: true
11 | containers:
12 | - name: kube-controller-manager
13 | image: {{.KubernetesImage}}
14 | command:
15 | - kube-controller-manager
16 | - --bind-address=0.0.0.0
17 | - --allocate-node-cidrs=true
18 | - --cluster-cidr={{.ClusterCIDR}}
19 | - --cluster-name=kubernetes
20 | - --cluster-signing-cert-file={{.PemCA}}
21 | - --cluster-signing-key-file={{.PemCAKey}}
22 | - --kubeconfig={{.Kubeconfig}}
23 | - --leader-elect=true
24 | - --root-ca-file={{.PemCA}}
25 | - --service-account-private-key-file={{.PemServiceAccountKey}}
26 | - --service-cluster-ip-range={{.ClusterIPRange}}
27 | - --use-service-account-credentials=true
28 | - --authorization-always-allow-paths=/healthz,/readyz,/livez,/metrics
29 | livenessProbe:
30 | failureThreshold: 8
31 | httpGet:
32 | scheme: HTTPS
33 | host: 127.0.0.1
34 | port: 10257
35 | path: /healthz
36 | initialDelaySeconds: 15
37 | timeoutSeconds: 15
38 | resources:
39 | requests:
40 | cpu: 200m
41 | volumeMounts:
42 | - name: pem-ca
43 | mountPath: {{.PemCA}}
44 | readOnly: true
45 | - name: pem-ca-key
46 | mountPath: {{.PemCAKey}}
47 | readOnly: true
48 | - name: pem-service-account-key
49 | mountPath: {{.PemServiceAccountKey}}
50 | readOnly: true
51 | - name: kubeconfig
52 | mountPath: {{.Kubeconfig}}
53 | readOnly: true
54 | volumes:
55 | - name: pem-ca
56 | hostPath:
57 | type: File
58 | path: {{.PemCA}}
59 | - name: pem-ca-key
60 | hostPath:
61 | type: File
62 | path: {{.PemCAKey}}
63 | - name: pem-service-account-key
64 | hostPath:
65 | type: File
66 | path: {{.PemServiceAccountKey}}
67 | - name: kubeconfig
68 | hostPath:
69 | type: File
70 | path: {{.Kubeconfig}}
71 |
--------------------------------------------------------------------------------
/data/templates/k8s/manifests/kube-proxy.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | namespace: kube-system
5 | name: kube-proxy
6 | labels:
7 | cluster-relevant: "true"
8 | cluster-weight: "80"
9 | spec:
10 | hostNetwork: true
11 | containers:
12 | - name: kube-proxy
13 | image: {{.KubernetesImage}}
14 | command:
15 | - kube-proxy
16 | - --config={{.KubeProxyConfig}}
17 | securityContext:
18 | privileged: true
19 | livenessProbe:
20 | failureThreshold: 8
21 | httpGet:
22 | scheme: HTTP
23 | host: 127.0.0.1
24 | port: 10249
25 | path: /healthz
26 | initialDelaySeconds: 15
27 | timeoutSeconds: 15
28 | resources:
29 | requests:
30 | cpu: 200m
31 | volumeMounts:
32 | - name: kube-proxy-config
33 | mountPath: {{.KubeProxyConfig}}
34 | readOnly: true
35 | - name: kube-proxy-kubeconfig
36 | mountPath: {{.KubeProxyKubeconfig}}
37 | readOnly: true
38 | - name: lib-modules
39 | mountPath: /lib/modules
40 | readOnly: true
41 | volumes:
42 | - name: kube-proxy-config
43 | hostPath:
44 | type: File
45 | path: {{.KubeProxyConfig}}
46 | - name: kube-proxy-kubeconfig
47 | hostPath:
48 | type: File
49 | path: {{.KubeProxyKubeconfig}}
50 | - name: lib-modules
51 | hostPath:
52 | type: Directory
53 | path: /lib/modules
54 |
--------------------------------------------------------------------------------
/data/templates/k8s/manifests/kube-scheduler.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | namespace: kube-system
5 | name: kube-scheduler
6 | labels:
7 | cluster-relevant: "true"
8 | cluster-weight: "85"
9 | spec:
10 | hostNetwork: true
11 | containers:
12 | - name: kube-scheduler
13 | image: {{.KubernetesImage}}
14 | command:
15 | - kube-scheduler
16 | - --config={{.KubeSchedulerConfig}}
17 | - --authorization-always-allow-paths=/healthz,/readyz,/livez,/metrics
18 | livenessProbe:
19 | failureThreshold: 8
20 | httpGet:
21 | scheme: HTTPS
22 | host: 127.0.0.1
23 | port: 10259
24 | path: /healthz
25 | initialDelaySeconds: 15
26 | timeoutSeconds: 15
27 | resources:
28 | requests:
29 | cpu: 100m
30 | volumeMounts:
31 | - name: kube-scheduler-config
32 | mountPath: {{.KubeSchedulerConfig}}
33 | readOnly: true
34 | - name: kube-scheduler-kubeconfig
35 | mountPath: {{.KubeSchedulerKubeconfig}}
36 | readOnly: true
37 | volumes:
38 | - name: kube-scheduler-config
39 | hostPath:
40 | type: File
41 | path: {{.KubeSchedulerConfig}}
42 | - name: kube-scheduler-kubeconfig
43 | hostPath:
44 | type: File
45 | path: {{.KubeSchedulerKubeconfig}}
46 |
--------------------------------------------------------------------------------
/data/templates/k8s/manifests/virtual-ip.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | namespace: kube-system
5 | name: {{.Type}}-virtual-ip
6 | labels:
7 | cluster-relevant: "true"
8 | cluster-weight: "99"
9 | spec:
10 | hostNetwork: true
11 | containers:
12 | - name: virtual-ip
13 | image: {{.VirtualIPImage}}
14 | command:
15 | - /virtual-ip
16 | - -id
17 | - {{.ID}}
18 | - -bind
19 | - {{.Bind}}
20 | - -peers
21 | - {{.Peers}}
22 | - -interface
23 | - {{.Interface}}
24 | - -virtual-ip
25 | - {{.VirtualIP}}
26 | securityContext:
27 | privileged: true
28 |
--------------------------------------------------------------------------------
/data/templates/k8s/service-account.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ServiceAccount
3 | metadata:
4 | name: {{.Name}}
5 | namespace: {{.Namespace}}
6 | ---
7 | apiVersion: rbac.authorization.k8s.io/v1
8 | kind: ClusterRoleBinding
9 | metadata:
10 | name: {{.Name}}
11 | roleRef:
12 | apiGroup: rbac.authorization.k8s.io
13 | kind: ClusterRole
14 | name: cluster-admin
15 | subjects:
16 | - kind: ServiceAccount
17 | name: {{.Name}}
18 | namespace: {{.Namespace}}
19 |
--------------------------------------------------------------------------------
/data/templates/k8s/setup/dns/coredns.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | kind: ConfigMap
4 | metadata:
5 | name: coredns
6 | namespace: {{.Namespace}}
7 | labels:
8 | app.kubernetes.io/instance: "k8s-tew"
9 | k8s-app: coredns
10 | kubernetes.io/cluster-service: "true"
11 | kubernetes.io/name: "CoreDNS"
12 | app.kubernetes.io/name: coredns
13 | data:
14 | Corefile: |-
15 | .:53 {
16 | errors
17 | health {
18 | lameduck 10s
19 | }
20 | ready
21 | kubernetes {{.ClusterDomain}} in-addr.arpa ip6.arpa {
22 | pods verified
23 | fallthrough in-addr.arpa ip6.arpa
24 | ttl 30
25 | }
26 | prometheus 0.0.0.0:9153
27 | forward . /etc/resolv.conf
28 | cache 30
29 | loop
30 | reload
31 | loadbalance
32 | }
33 |
34 | ---
35 | apiVersion: v1
36 | kind: ServiceAccount
37 | metadata:
38 | name: coredns
39 | namespace: {{.Namespace}}
40 |
41 | ---
42 | apiVersion: rbac.authorization.k8s.io/v1
43 | kind: ClusterRole
44 | metadata:
45 | name: coredns
46 | labels:
47 | app.kubernetes.io/instance: "k8s-tew"
48 | k8s-app: coredns
49 | kubernetes.io/cluster-service: "true"
50 | kubernetes.io/name: "CoreDNS"
51 | app.kubernetes.io/name: coredns
52 | rules:
53 | - apiGroups:
54 | - ""
55 | resources:
56 | - endpoints
57 | - services
58 | - pods
59 | - namespaces
60 | verbs:
61 | - list
62 | - watch
63 | - apiGroups:
64 | - discovery.k8s.io
65 | resources:
66 | - endpointslices
67 | verbs:
68 | - list
69 | - watch
70 |
71 | ---
72 | apiVersion: rbac.authorization.k8s.io/v1
73 | kind: ClusterRoleBinding
74 | metadata:
75 | name: coredns
76 | labels:
77 | app.kubernetes.io/instance: "k8s-tew"
78 | k8s-app: coredns
79 | kubernetes.io/cluster-service: "true"
80 | kubernetes.io/name: "CoreDNS"
81 | app.kubernetes.io/name: coredns
82 | roleRef:
83 | apiGroup: rbac.authorization.k8s.io
84 | kind: ClusterRole
85 | name: coredns
86 | subjects:
87 | - kind: ServiceAccount
88 | name: coredns
89 | namespace: {{.Namespace}}
90 |
91 | ---
92 | apiVersion: v1
93 | kind: Service
94 | metadata:
95 | name: coredns
96 | namespace: {{.Namespace}}
97 | annotations:
98 | prometheus.io/port: "9153"
99 | prometheus.io/scrape: "true"
100 | labels:
101 | app.kubernetes.io/instance: "k8s-tew"
102 | k8s-app: coredns
103 | kubernetes.io/cluster-service: "true"
104 | kubernetes.io/name: "CoreDNS"
105 | app.kubernetes.io/name: coredns
106 | spec:
107 | selector:
108 | app.kubernetes.io/instance: "k8s-tew"
109 | k8s-app: coredns
110 | app.kubernetes.io/name: coredns
111 | clusterIP: {{.ClusterDNSIP}}
112 | ports:
113 | - {"name":"udp-53","port":53,"protocol":"UDP","targetPort":53}
114 | - {"name":"tcp-53","port":53,"protocol":"TCP","targetPort":53}
115 | - {"name":"metrics","port": 9153,"protocol": "TCP"}
116 | type: ClusterIP
117 |
118 | ---
119 | apiVersion: apps/v1
120 | kind: Deployment
121 | metadata:
122 | name: coredns
123 | namespace: {{.Namespace}}
124 | labels:
125 | app.kubernetes.io/instance: "k8s-tew"
126 | k8s-app: coredns
127 | kubernetes.io/cluster-service: "true"
128 | kubernetes.io/name: "CoreDNS"
129 | app.kubernetes.io/name: coredns
130 | spec:
131 | replicas: 2
132 | strategy:
133 | type: RollingUpdate
134 | rollingUpdate:
135 | maxUnavailable: 1
136 | maxSurge: 25%
137 | selector:
138 | matchLabels:
139 | app.kubernetes.io/instance: "k8s-tew"
140 | k8s-app: coredns
141 | app.kubernetes.io/name: coredns
142 | template:
143 | metadata:
144 | labels:
145 | k8s-app: coredns
146 | app.kubernetes.io/name: coredns
147 | app.kubernetes.io/instance: "k8s-tew"
148 | spec:
149 | terminationGracePeriodSeconds: 30
150 | serviceAccountName: coredns
151 | dnsPolicy: Default
152 | containers:
153 | - name: "coredns"
154 | image: {{.CoreDNSImage}}
155 | imagePullPolicy: IfNotPresent
156 | args: [ "-conf", "/etc/coredns/Corefile" ]
157 | volumeMounts:
158 | - name: config-volume
159 | mountPath: /etc/coredns
160 | resources:
161 | limits:
162 | cpu: 100m
163 | memory: 128Mi
164 | requests:
165 | cpu: 100m
166 | memory: 128Mi
167 | ports:
168 | - {"containerPort":53,"name":"udp-53","protocol":"UDP"}
169 | - {"containerPort":53,"name":"tcp-53","protocol":"TCP"}
170 | - {"containerPort":9153,"name":"metrics","protocol":"TCP"}
171 | livenessProbe:
172 | httpGet:
173 | path: /health
174 | port: 8080
175 | scheme: HTTP
176 | initialDelaySeconds: 60
177 | periodSeconds: 10
178 | timeoutSeconds: 5
179 | successThreshold: 1
180 | failureThreshold: 5
181 | readinessProbe:
182 | httpGet:
183 | path: /ready
184 | port: 8181
185 | scheme: HTTP
186 | initialDelaySeconds: 30
187 | periodSeconds: 5
188 | timeoutSeconds: 5
189 | successThreshold: 1
190 | failureThreshold: 1
191 | securityContext:
192 | allowPrivilegeEscalation: false
193 | capabilities:
194 | add:
195 | - NET_BIND_SERVICE
196 | drop:
197 | - ALL
198 | readOnlyRootFilesystem: true
199 | volumes:
200 | - name: config-volume
201 | configMap:
202 | name: coredns
203 | items:
204 | - key: Corefile
205 | path: Corefile
206 |
--------------------------------------------------------------------------------
/data/templates/k8s/setup/ingress/letsencrypt-cluster-issuer.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: cert-manager.io/v1
2 | kind: ClusterIssuer
3 | metadata:
4 | name: letsencrypt-production
5 | spec:
6 | acme:
7 | server: https://acme-v02.api.letsencrypt.org/directory
8 | email: "{{.Email}}"
9 | solvers:
10 | - selector: {}
11 | http01:
12 | ingress:
13 | class: nginx
14 | privateKeySecretRef:
15 | name: letsencrypt-production
16 |
--------------------------------------------------------------------------------
/data/templates/k8s/setup/kubelet-setup.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRole
3 | metadata:
4 | annotations:
5 | rbac.authorization.kubernetes.io/autoupdate: "true"
6 | labels:
7 | kubernetes.io/bootstrapping: rbac-defaults
8 | name: system:kube-apiserver-to-kubelet
9 | rules:
10 | - apiGroups:
11 | - ""
12 | resources:
13 | - nodes/proxy
14 | - nodes/stats
15 | - nodes/log
16 | - nodes/spec
17 | - nodes/metrics
18 | verbs:
19 | - "*"
20 | ---
21 | apiVersion: rbac.authorization.k8s.io/v1
22 | kind: ClusterRoleBinding
23 | metadata:
24 | name: system:kube-apiserver
25 | namespace: ""
26 | roleRef:
27 | apiGroup: rbac.authorization.k8s.io
28 | kind: ClusterRole
29 | name: system:kube-apiserver-to-kubelet
30 | subjects:
31 | - apiGroup: rbac.authorization.k8s.io
32 | kind: User
33 | name: kubernetes
34 |
--------------------------------------------------------------------------------
/data/templates/k8s/setup/miscellaneous/wordpress.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Namespace
3 | metadata:
4 | name: {{.Namespace}}
5 |
6 | ---
7 | apiVersion: v1
8 | kind: PersistentVolumeClaim
9 | metadata:
10 | name: mysql-pv-claim
11 | namespace: {{.Namespace}}
12 | labels:
13 | app: wordpress
14 | spec:
15 | accessModes:
16 | - ReadWriteOnce
17 | resources:
18 | requests:
19 | storage: 2Gi
20 |
21 | ---
22 | apiVersion: v1
23 | kind: PersistentVolumeClaim
24 | metadata:
25 | name: wordpress-pv-claim
26 | namespace: {{.Namespace}}
27 | labels:
28 | app: wordpress
29 | spec:
30 | accessModes:
31 | - ReadWriteOnce
32 | resources:
33 | requests:
34 | storage: 2Gi
35 |
36 | ---
37 | apiVersion: apps/v1
38 | kind: Deployment
39 | metadata:
40 | name: mysql
41 | namespace: {{.Namespace}}
42 | labels:
43 | app: wordpress
44 | spec:
45 | selector:
46 | matchLabels:
47 | app: wordpress
48 | tier: mysql
49 | strategy:
50 | type: Recreate
51 | template:
52 | metadata:
53 | labels:
54 | app: wordpress
55 | tier: mysql
56 | spec:
57 | containers:
58 | - image: {{.MySQLImage}}
59 | name: mysql
60 | args:
61 | - --default-authentication-plugin=mysql_native_password
62 | env:
63 | - name: MYSQL_ROOT_PASSWORD
64 | value: changeme
65 | - name: MYSQL_USER
66 | value: wordpress
67 | - name: MYSQL_PASSWORD
68 | value: changeme
69 | - name: MYSQL_DATABASE
70 | value: wordpress
71 | ports:
72 | - containerPort: 3306
73 | name: mysql
74 | protocol: TCP
75 | volumeMounts:
76 | - name: mysql-persistent-storage
77 | mountPath: /var/lib/mysql
78 | volumes:
79 | - name: mysql-persistent-storage
80 | persistentVolumeClaim:
81 | claimName: mysql-pv-claim
82 |
83 | ---
84 | apiVersion: apps/v1
85 | kind: Deployment
86 | metadata:
87 | name: wordpress
88 | namespace: {{.Namespace}}
89 | labels:
90 | app: wordpress
91 | spec:
92 | selector:
93 | matchLabels:
94 | app: wordpress
95 | strategy:
96 | type: Recreate
97 | template:
98 | metadata:
99 | labels:
100 | app: wordpress
101 | tier: frontend
102 | spec:
103 | containers:
104 | - image: {{.WordPressImage}}
105 | name: wordpress
106 | env:
107 | - name: WORDPRESS_DB_HOST
108 | value: mysql
109 | - name: WORDPRESS_DB_USER
110 | value: wordpress
111 | - name: WORDPRESS_DB_PASSWORD
112 | value: changeme
113 | - name: WORDPRESS_DB_NAME
114 | value: wordpress
115 | livenessProbe:
116 | httpGet:
117 | path: /wp-admin/install.php
118 | port: wordpress
119 | periodSeconds: 5
120 | timeoutSeconds: 5
121 | ports:
122 | - containerPort: 80
123 | name: wordpress
124 | protocol: TCP
125 | volumeMounts:
126 | - name: wordpress-persistent-storage
127 | mountPath: /var/www/html
128 | volumes:
129 | - name: wordpress-persistent-storage
130 | persistentVolumeClaim:
131 | claimName: wordpress-pv-claim
132 |
133 | ---
134 | apiVersion: v1
135 | kind: Service
136 | metadata:
137 | name: mysql
138 | namespace: {{.Namespace}}
139 | labels:
140 | app: wordpress
141 | spec:
142 | ports:
143 | - port: 3306
144 | protocol: TCP
145 | selector:
146 | app: wordpress
147 | tier: mysql
148 | clusterIP: None
149 |
150 | ---
151 | apiVersion: v1
152 | kind: Service
153 | metadata:
154 | name: wordpress
155 | namespace: {{.Namespace}}
156 | labels:
157 | app: wordpress
158 | spec:
159 | ports:
160 | - port: 80
161 | nodePort: {{.WordPressPort}}
162 | protocol: TCP
163 | selector:
164 | app: wordpress
165 | tier: frontend
166 | type: NodePort
167 |
168 | ---
169 | apiVersion: networking.k8s.io/v1
170 | kind: Ingress
171 | metadata:
172 | name: wordpress
173 | namespace: {{.Namespace}}
174 | annotations:
175 | ingress.kubernetes.io/ssl-redirect: "true"
176 | kubernetes.io/tls-acme: "true"
177 | cert-manager.io/cluster-issuer: letsencrypt-production
178 | spec:
179 | tls:
180 | - hosts:
181 | - {{.WordPressIngressDomain}}
182 | secretName: wordpress-letsencrypt
183 | rules:
184 | - host: {{.WordPressIngressDomain}}
185 | http:
186 | paths:
187 | - path: /
188 | pathType: Prefix
189 | backend:
190 | service:
191 | name: wordpress
192 | port:
193 | number: 80
194 |
--------------------------------------------------------------------------------
/data/templates/k8s/setup/monitoring/alert-manager.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ServiceAccount
3 | metadata:
4 | namespace: {{.Namespace}}
5 | name: alert-manager
6 | ---
7 | apiVersion: v1
8 | kind: ConfigMap
9 | metadata:
10 | namespace: {{.Namespace}}
11 | name: alert-manager-config
12 | labels:
13 | k8s-app: alert-manager
14 | data:
15 | alert-manager.yml: |
16 | global: null
17 | receivers:
18 | - name: default-receiver
19 | route:
20 | group_interval: 5m
21 | group_wait: 10s
22 | receiver: default-receiver
23 | repeat_interval: 3h
24 | ---
25 | apiVersion: v1
26 | kind: Service
27 | metadata:
28 | namespace: {{.Namespace}}
29 | name: alert-manager-operated
30 | labels:
31 | k8s-app: alert-manager
32 | spec:
33 | type: "ClusterIP"
34 | clusterIP: None
35 | selector:
36 | k8s-app: alert-manager
37 | ports:
38 | - name: mesh
39 | port: 6783
40 | protocol: TCP
41 | targetPort: 6783
42 | - name: http
43 | port: 9093
44 | protocol: TCP
45 | targetPort: 9093
46 | ---
47 | apiVersion: v1
48 | kind: Service
49 | metadata:
50 | namespace: {{.Namespace}}
51 | name: alert-manager
52 | labels:
53 | k8s-app: alert-manager
54 | spec:
55 | ports:
56 | - name: http
57 | port: 9093
58 | protocol: TCP
59 | targetPort: 9093
60 | selector:
61 | k8s-app: alert-manager
62 | type: "ClusterIP"
63 | ---
64 | apiVersion: apps/v1
65 | kind: StatefulSet
66 | metadata:
67 | namespace: {{.Namespace}}
68 | name: alert-manager
69 | labels:
70 | k8s-app: alert-manager
71 | spec:
72 | serviceName: "alert-manager-operated"
73 | replicas: {{ .AlertManagerCount }}
74 | podManagementPolicy: OrderedReady
75 | updateStrategy:
76 | type: RollingUpdate
77 | revisionHistoryLimit: 10
78 | selector:
79 | matchLabels:
80 | k8s-app: alert-manager
81 | template:
82 | metadata:
83 | labels:
84 | k8s-app: alert-manager
85 | spec:
86 | serviceAccountName: alert-manager
87 | initContainers:
88 | - name: "init-chmod-data"
89 | image: {{ .BusyboxImage }}
90 | imagePullPolicy: "IfNotPresent"
91 | command: ["chmod", "777", "/data"]
92 | volumeMounts:
93 | - name: alert-manager-data
94 | mountPath: "/data"
95 | containers:
96 | - name: alert-manager
97 | image: {{ .AlertManagerImage }}
98 | imagePullPolicy: IfNotPresent
99 | args:
100 | - --config.file=/etc/config/alert-manager.yml
101 | - --storage.path=/data
102 | - --web.listen-address=:9093
103 | - --web.route-prefix=/
104 | - --cluster.listen-address=$(POD_IP):6783
105 | {{ range $index, $element := .AlertManagerCounts -}}
106 | - --cluster.peer=alert-manager-{{ $element }}.alert-manager-operated.{{$.Namespace}}.svc:6783
107 | {{ end -}}
108 | - --log.level=debug
109 | env:
110 | - name: POD_IP
111 | valueFrom:
112 | fieldRef:
113 | apiVersion: v1
114 | fieldPath: status.podIP
115 | ports:
116 | - containerPort: 9093
117 | name: http
118 | protocol: TCP
119 | - containerPort: 6783
120 | name: mesh
121 | protocol: TCP
122 | readinessProbe:
123 | httpGet:
124 | path: /#/status
125 | port: 9093
126 | initialDelaySeconds: 30
127 | timeoutSeconds: 30
128 | volumeMounts:
129 | - name: config-volume
130 | mountPath: /etc/config
131 | - name: alert-manager-data
132 | mountPath: "/data"
133 | resources:
134 | limits:
135 | cpu: 10m
136 | memory: 50Mi
137 | requests:
138 | cpu: 10m
139 | memory: 50Mi
140 | volumes:
141 | - name: config-volume
142 | configMap:
143 | name: alert-manager-config
144 | affinity:
145 | podAntiAffinity:
146 | requiredDuringSchedulingIgnoredDuringExecution:
147 | - labelSelector:
148 | matchExpressions:
149 | - key: k8s-app
150 | operator: In
151 | values:
152 | - alert-manager
153 | topologyKey: "kubernetes.io/hostname"
154 | volumeClaimTemplates:
155 | - metadata:
156 | name: alert-manager-data
157 | spec:
158 | accessModes:
159 | - ReadWriteOnce
160 | resources:
161 | requests:
162 | storage: "{{ .AlertManagerSize }}Gi"
163 |
--------------------------------------------------------------------------------
/data/templates/k8s/setup/monitoring/node-exporter.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | kind: ServiceAccount
4 | metadata:
5 | name: prometheus-node-exporter
6 | namespace: {{.Namespace}}
7 | labels:
8 | app.kubernetes.io/component: metrics
9 | app.kubernetes.io/part-of: prometheus-node-exporter
10 | app.kubernetes.io/name: prometheus-node-exporter
11 | imagePullSecrets:
12 | []
13 |
14 | ---
15 | kind: ClusterRole
16 | apiVersion: rbac.authorization.k8s.io/v1
17 | metadata:
18 | name: node-exporter
19 | labels:
20 | app: node-exporter
21 | rules:
22 | - apiGroups: ['extensions']
23 | resources: ['podsecuritypolicies']
24 | verbs: ['use']
25 | resourceNames:
26 | - prometheus-node-exporter
27 |
28 | ---
29 | apiVersion: rbac.authorization.k8s.io/v1
30 | kind: ClusterRoleBinding
31 | metadata:
32 | name: node-exporter
33 | labels:
34 | app: node-exporter
35 | roleRef:
36 | apiGroup: rbac.authorization.k8s.io
37 | kind: ClusterRole
38 | name: node-exporter
39 | subjects:
40 | - kind: ServiceAccount
41 | name: node-exporter
42 | namespace: {{.Namespace}}
43 |
44 | ---
45 | apiVersion: v1
46 | kind: Service
47 | metadata:
48 | name: prometheus-node-exporter
49 | namespace: {{.Namespace}}
50 | labels:
51 | app.kubernetes.io/component: metrics
52 | app.kubernetes.io/part-of: prometheus-node-exporter
53 | app.kubernetes.io/name: prometheus-node-exporter
54 | app.kubernetes.io/instance: k8s-tew
55 | annotations:
56 | prometheus.io/scrape: "true"
57 | spec:
58 | type: ClusterIP
59 | ports:
60 | - port: 9100
61 | targetPort: 9100
62 | protocol: TCP
63 | name: metrics
64 | selector:
65 | app.kubernetes.io/name: prometheus-node-exporter
66 | app.kubernetes.io/instance: k8s-tew
67 | ---
68 | apiVersion: apps/v1
69 | kind: DaemonSet
70 | metadata:
71 | name: prometheus-node-exporter
72 | namespace: {{.Namespace}}
73 | labels:
74 | app.kubernetes.io/component: metrics
75 | app.kubernetes.io/part-of: prometheus-node-exporter
76 | app.kubernetes.io/name: prometheus-node-exporter
77 | app.kubernetes.io/instance: k8s-tew
78 | spec:
79 | selector:
80 | matchLabels:
81 | app.kubernetes.io/name: prometheus-node-exporter
82 | app.kubernetes.io/instance: k8s-tew
83 | revisionHistoryLimit: 10
84 | updateStrategy:
85 | rollingUpdate:
86 | maxUnavailable: 1
87 | type: RollingUpdate
88 | template:
89 | metadata:
90 | annotations:
91 | cluster-autoscaler.kubernetes.io/safe-to-evict: "true"
92 | labels:
93 | app.kubernetes.io/component: metrics
94 | app.kubernetes.io/part-of: prometheus-node-exporter
95 | app.kubernetes.io/name: prometheus-node-exporter
96 | app.kubernetes.io/instance: k8s-tew
97 | spec:
98 | automountServiceAccountToken: false
99 | securityContext:
100 | fsGroup: 65534
101 | runAsGroup: 65534
102 | runAsNonRoot: true
103 | runAsUser: 65534
104 | serviceAccountName: prometheus-node-exporter
105 | containers:
106 | - name: node-exporter
107 | image: {{ .NodeExporterImage }}
108 | imagePullPolicy: IfNotPresent
109 | args:
110 | - --path.procfs=/host/proc
111 | - --path.sysfs=/host/sys
112 | - --path.rootfs=/host/root
113 | - --path.udev.data=/host/root/run/udev/data
114 | - --web.listen-address=[$(HOST_IP)]:9100
115 | securityContext:
116 | readOnlyRootFilesystem: true
117 | env:
118 | - name: HOST_IP
119 | value: 0.0.0.0
120 | ports:
121 | - name: metrics
122 | containerPort: 9100
123 | protocol: TCP
124 | livenessProbe:
125 | failureThreshold: 3
126 | httpGet:
127 | httpHeaders:
128 | path: /
129 | port: 9100
130 | scheme: HTTP
131 | initialDelaySeconds: 0
132 | periodSeconds: 10
133 | successThreshold: 1
134 | timeoutSeconds: 1
135 | readinessProbe:
136 | failureThreshold: 3
137 | httpGet:
138 | httpHeaders:
139 | path: /
140 | port: 9100
141 | scheme: HTTP
142 | initialDelaySeconds: 0
143 | periodSeconds: 10
144 | successThreshold: 1
145 | timeoutSeconds: 1
146 | volumeMounts:
147 | - name: proc
148 | mountPath: /host/proc
149 | readOnly: true
150 | - name: sys
151 | mountPath: /host/sys
152 | readOnly: true
153 | - name: root
154 | mountPath: /host/root
155 | mountPropagation: HostToContainer
156 | readOnly: true
157 | hostNetwork: true
158 | hostPID: true
159 | nodeSelector:
160 | kubernetes.io/os: linux
161 | tolerations:
162 | - effect: NoSchedule
163 | operator: Exists
164 | volumes:
165 | - name: proc
166 | hostPath:
167 | path: /proc
168 | - name: sys
169 | hostPath:
170 | path: /sys
171 | - name: root
172 | hostPath:
173 | path: /
174 |
--------------------------------------------------------------------------------
/data/templates/k8s/setup/storage/ceph-secrets.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Namespace
3 | metadata:
4 | name: {{.Namespace}}
5 |
6 | ---
7 | apiVersion: v1
8 | kind: Secret
9 | metadata:
10 | name: ceph-admin
11 | namespace: {{.Namespace}}
12 | type: "kubernetes.io/rbd"
13 | data:
14 | key: {{.ClientAdminKey | base64}}
15 |
16 | ---
17 | apiVersion: v1
18 | kind: Secret
19 | metadata:
20 | name: ceph-k8s-tew
21 | namespace: {{.Namespace}}
22 | type: "kubernetes.io/rbd"
23 | data:
24 | key: {{.ClientK8STEWKey | base64}}
25 |
26 | ---
27 | apiVersion: v1
28 | kind: Secret
29 | metadata:
30 | name: csi-rbd-secret
31 | namespace: {{.Namespace}}
32 | data:
33 | adminID: {{"admin" | base64}}
34 | adminKey: {{.ClientAdminKey | base64}}
35 | userID: {{"k8s-tew" | base64}}
36 | userKey: {{.ClientK8STEWKey | base64}}
37 |
38 | ---
39 | apiVersion: v1
40 | kind: Secret
41 | metadata:
42 | name: csi-cephfs-secret
43 | namespace: {{.Namespace}}
44 | data:
45 | adminID: {{"admin" | base64}}
46 | adminKey: {{.ClientAdminKey | base64}}
47 | userID: {{"k8s-tew" | base64}}
48 | userKey: {{.ClientK8STEWKey | base64}}
49 |
--------------------------------------------------------------------------------
/data/templates/system/environment.sh:
--------------------------------------------------------------------------------
1 | export PATH="{{.K8STEWPath}}":"{{.K8SPath}}":"{{.EtcdPath}}":"{{.CRIPath}}":"{{.CNIPath}}":"{{.VeleroPath}}":"{{.HostPath}}":{{.CurrentPath}}
2 | {{- if .KubeConfig }}
3 | export KUBECONFIG="{{.KubeConfig}}"
4 | {{- end }}
5 | export VELERO_NAMESPACE=backup
6 | export CONTAINER_RUNTIME_ENDPOINT=unix://{{.ContainerdSock}}
7 | export CONTAINERD_NAMESPACE=k8s.io
8 | export ETCDCTL_API=3
9 |
--------------------------------------------------------------------------------
/data/templates/system/k8s-tew.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description={{.ProjectTitle}}
3 |
4 | [Service]
5 | ExecStart={{.Command}} run --base-directory={{.BaseDirectory}} --hide-progress --kill-containers=true
6 | Restart=on-failure
7 | KillSignal=SIGINT
8 | KillMode=process
9 | RestartSec=5
10 | LimitNOFILE=1000000
11 | Delegate=yes
12 |
13 | [Install]
14 | WantedBy=multi-user.target
15 |
--------------------------------------------------------------------------------
/data/templates/system/k8s-tew.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | export K8S_TEW_BASE_DIRECTORY={{.BaseDirectory}}
4 |
5 | source <({{.Binary}} environment)
6 |
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/.nojekyll
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | SOURCEDIR = .
8 | BUILDDIR = _build
9 |
10 | # Put it first so that "make" without argument is like "make help".
11 | help:
12 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
13 |
14 | .PHONY: help Makefile
15 |
16 | # Catch-all target: route all unknown targets to Sphinx using the new
17 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
18 | %: Makefile
19 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
--------------------------------------------------------------------------------
/docs/_build/doctrees/about.doctree:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/doctrees/about.doctree
--------------------------------------------------------------------------------
/docs/_build/doctrees/environment.pickle:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/doctrees/environment.pickle
--------------------------------------------------------------------------------
/docs/_build/doctrees/features.doctree:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/doctrees/features.doctree
--------------------------------------------------------------------------------
/docs/_build/doctrees/index.doctree:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/doctrees/index.doctree
--------------------------------------------------------------------------------
/docs/_build/doctrees/installation.doctree:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/doctrees/installation.doctree
--------------------------------------------------------------------------------
/docs/_build/doctrees/quickstart.doctree:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/doctrees/quickstart.doctree
--------------------------------------------------------------------------------
/docs/_build/doctrees/setups.doctree:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/doctrees/setups.doctree
--------------------------------------------------------------------------------
/docs/_build/doctrees/troubleshooting.doctree:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/doctrees/troubleshooting.doctree
--------------------------------------------------------------------------------
/docs/_build/doctrees/usage.doctree:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/doctrees/usage.doctree
--------------------------------------------------------------------------------
/docs/_build/html/.buildinfo:
--------------------------------------------------------------------------------
1 | # Sphinx build info version 1
2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
3 | config: ee51ab9b04222949e74f650086225f02
4 | tags: 645f666f9bcd5a90fca523b33c5a78b7
5 |
--------------------------------------------------------------------------------
/docs/_build/html/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/html/.nojekyll
--------------------------------------------------------------------------------
/docs/_build/html/_sources/about.rst.txt:
--------------------------------------------------------------------------------
1 | About
2 | =====
3 |
4 | k8s-tew_ is a CLI tool to install a Kubernetes Cluster (local, single-node, multi-node or HA-cluster) on Bare Metal. It installs the most essential components needed by a cluster such as networking, storage, monitoring, logging, backuping/restoring and so on. Once the nodes are configured, k8s-tew is started on each node to supervise the cluster components. k8s-tew is also used internally to generate the configuration files for a Ceph cluster and also to start the necessary Ceph deamons with the right parameters. The Ceph functionality can be used with or without Kubernetes.
5 |
6 | .. _k8s-tew: https://github.com/darxkies/k8s-tew
7 |
8 | Why
9 | ---
10 |
11 | Kubernetes_ is a fairly complex project. For a newbie it is hard to understand and also to use. While `Kelsey Hightower's Kubernetes The Hard Way `_, on which this project is based, helps a lot to understand Kubernetes, it is optimized for the use with Google Cloud Platform.
12 |
13 | Thus, this project's aim is to give newbies an easy to use tool that allows them to tinker with Kubernetes and later on to install HA production grade clusters.
14 |
15 | .. _Kubernetes: https://kubernetes.io/
16 |
17 |
--------------------------------------------------------------------------------
/docs/_build/html/_sources/features.rst.txt:
--------------------------------------------------------------------------------
1 | Features
2 | ========
3 |
4 | * HA cluster setup passes all CNCF conformance tests (Kubernetes `1.10 `_, `1.11 `_, `1.12 `_, `1.13 `_, `1.14 `_, `1.15 `_, `1.16 `_, `1.17 `_, `1.18 `_, `1.19 `_, `1.20 `_, `1.21 `_, `1.22 `_ , `1.23 `_ , `1.24 `_ , `1.25 `_ , `1.26 `_ & `1.27 `_)
5 | * Container Management: `Containerd `_
6 | * Networking: `Calico `_
7 | * Ingress: `NGINX Ingress `_ and `cert-manager `_ for `Let's Encrypt `_
8 | * Storage: `Ceph/RBD `_
9 | * Metrics: `metering-metrics `_ and `Heapster `_
10 | * Monitoring: `Prometheus `_ and `Grafana `_
11 | * Logging: `Fluent-Bit `_, `Elasticsearch `_, `Kibana `_ and `Cerebro `_
12 | * Backups: `Velero `_, `Restic `_ and `Minio `_
13 | * Cluster Load Balancing: `MetalLB `_
14 | * Controller Load Balancing: `gobetween `_
15 | * Package Manager: `Helm `_
16 | * Dashboard: `Kubernetes Dashboard `_
17 | * The communication between the components is encrypted
18 | * RBAC is enabled
19 | * The controllers and the workers have Floating/Virtual IPs
20 | * Integrated Load Balancer for the API Servers
21 | * Support for deployment to a HA cluster using ssh
22 | * Only the changed files are deployed
23 | * No `Docker `_ installation required
24 | * No cloud provider required
25 | * Single binary without any dependencies
26 | * Runs locally
27 | * Nodes management from the command line
28 | * Downloads all the used binaries (kubernetes, calico, ceph...) from the Internet
29 | * Pull Images, Convert them to OCI and import them on the cluster for offline installations
30 | * Uses systemd to install itself as a service on the remote machine
31 | * Installs `WordPress `_ and `MySQL `_ to test drive the installation
32 |
33 |
--------------------------------------------------------------------------------
/docs/_build/html/_sources/index.rst.txt:
--------------------------------------------------------------------------------
1 | .. spelling::
2 |
3 | prem
4 |
5 | .. k8s-tew documentation master file, created by
6 | sphinx-quickstart on Sat Oct 6 19:46:44 2018.
7 | You can adapt this file completely to your liking, but it should at least
8 | contain the root `toctree` directive.
9 |
10 | Welcome to k8s-tew's documentation!
11 | ===================================
12 |
13 | .. toctree::
14 | :maxdepth: 2
15 | :caption: Contents:
16 |
17 | about
18 | features
19 | quickstart
20 | installation
21 | usage
22 | setups
23 | troubleshooting
24 |
25 |
--------------------------------------------------------------------------------
/docs/_build/html/_sources/installation.rst.txt:
--------------------------------------------------------------------------------
1 | Installation
2 | ============
3 |
4 | The commands in the upcoming sections will assume that k8s-tew is going to be installed in the directory :file:`/usr/local/bin`. That means that the aforementioned directory exists and it is included in the PATH. If that is not the case use the following commands:
5 |
6 | .. code:: shell
7 |
8 | sudo mkdir -p /usr/local/bin
9 | export PATH=/usr/local/bin:$PATH
10 |
11 | Binary
12 | ------
13 |
14 | The x86 64-bit binary can be downloaded from the following address: https://github.com/darxkies/k8s-tew/releases
15 |
16 | Additionally the these commands can be used to download it and install it in :file:`/usr/local/bin`
17 |
18 | .. code:: shell
19 |
20 | curl -s https://api.github.com/repos/darxkies/k8s-tew/releases/latest | grep "browser_download_url" | cut -d : -f 2,3 | tr -d \" | sudo wget -O /usr/local/bin/k8s-tew -qi -
21 | sudo chmod a+x /usr/local/bin/k8s-tew
22 |
23 | Source
24 | ------
25 |
26 | To compile it from source you will need a Go (version 1.15+) environment, Git, Make and Docker installed. Once everything is installed, enter the following commands:
27 |
28 | .. code:: shell
29 |
30 | export GOPATH=~/go
31 | export PATH=$GOPATH/bin:$PATH
32 | mkdir -p $GOPATH/src/github.com/darxkies
33 | cd $GOPATH/src/github.com/darxkies
34 | git clone https://github.com/darxkies/k8s-tew.git
35 | cd k8s-tew
36 | make
37 | sudo mv k8s-tew /usr/local/bin
38 |
39 |
--------------------------------------------------------------------------------
/docs/_build/html/_sources/quickstart.rst.txt:
--------------------------------------------------------------------------------
1 | Quick Start
2 | ===========
3 |
4 | The following snippet will create a cluster on the host computer or in a virtual machine:
5 |
6 | .. code:: shell
7 |
8 | # Switch to user root
9 | sudo su -
10 |
11 | # Download Binary
12 | wget https://github.com/darxkies/k8s-tew/releases/download/2.4.1/k8s-tew
13 | chmod a+x k8s-tew
14 |
15 | # Everything is installed relative to the root directory
16 | export K8S_TEW_BASE_DIRECTORY=/
17 |
18 | # Initialize cluster configuration
19 | ./k8s-tew initialize
20 |
21 | # Node the current machine to the cluster (the settings such as IP and hostname are inferred)
22 | ./k8s-tew node-add -s
23 |
24 | # Only on Ubuntu 20.04 to solve any DNS related issues
25 | ./k8s-tew configure --resolv-conf=/run/systemd/resolve/resolv.conf
26 |
27 | # Generate artefacts (e.g. certificates, configurations and so on)
28 | ./k8s-tew generate
29 |
30 | # Activate and start service
31 | systemctl daemon-reload
32 | systemctl enable k8s-tew
33 | systemctl start k8s-tew
34 |
35 | # Activate environment variables and switch back to root
36 | exit
37 | sudo su -
38 |
39 | # Watch the pods being installed
40 | watch -n 1 kubectl get pods --all-namespaces
41 |
42 | .. note:: You will need at least 20GB HDD, 8GB RAM and 4 CPU Cores.
43 | .. note:: To use k8s-tew with Vagrant take a look at `https://github.com/darxkies/k8s-tew/tree/2.4.1/setup `_.
44 |
45 |
--------------------------------------------------------------------------------
/docs/_build/html/_sources/setups.rst.txt:
--------------------------------------------------------------------------------
1 | Setups
2 | ======
3 |
4 | Vagrant
5 | -------
6 |
7 | Vagrant/VirtualBox can be used to test drive k8s-tew. The host is used to bootstrap the cluster which runs in VirtualBox. The Vagrantfile included in the repository can be used for single-node/multi-node & Ubuntu 20.04/CentOS 8.2 setups.
8 |
9 | The Vagrantfile can be configured using the environment variables:
10 |
11 | - OS - define the operating system. It accepts ubuntu, the default value, and centos.
12 | - MULTI_NODE - if set then a HA cluster is generated. Otherwise a single-node setup is used.
13 | - CONTROLLERS - defines the number of controller nodes. The default number is 3.
14 | - WORKERS - specifies the number of worker nodes. The default number is 2.
15 | - SSH_PUBLIC_KEY - if this environment variable is not set, then :file:`$HOME/.ssh/id_rsa` is used by default.
16 | - IP_PREFIX - this value is used to generate the IP addresses of the nodes. If not set 192.168.100 will be used. The single node has the IP address 192.168.100.50. The controllers start with the IP address 192.168.100.200 and the workers with 192.168.100.100.
17 | - CONTROLLERS_RAM - amount of RAM for one controller
18 | - WORKERS_RAM - amount of RAM for one worker
19 | - STORAGE_RAM - amount of RAM for one storage
20 | - CONTROLLERS_CPUS - number of CPUs per controller
21 | - WORKERS_CPUS - number of CPUs per worker
22 | - STORAGE_CPUS - number of CPUs per storage
23 |
24 | .. note:: The multi-node setup with the default settings needs about 20GB RAM for itself.
25 |
26 |
27 | Usage
28 | ^^^^^
29 |
30 | The directory called :file:`setup` (`https://github.com/darxkies/k8s-tew/tree/2.4.1/setup `_) contains sub-directories for various cluster setup configurations:
31 |
32 | - local - it starts a single-node cluster locally without using any kind of virtualization. This kind of setup needs root rights. It is meant for local development where it might be important to fire the cluster up and shut it down fast. If you want it to start automatically, take a look at the quickstart section.
33 | - ubuntu-single-node - Ubuntu 20.04 single-node cluster. It needs about 8GB Ram.
34 | - ubuntu-multi-node - Ubuntu 20.04 HA cluster. It needs around 20GB Ram.
35 | - centos-single-node - CentOS 8.2 single-node cluster. It needs about 8GB Ram.
36 | - centos-multi-node - CentOS 8.2 HA cluster. It needs around 20GB Ram.
37 |
38 | .. note:: Regardless of the setup, once the deployment is done it will take a while to download all required containers from the internet. So better use kubectl to check the status of the pods.
39 |
40 | .. note:: For the local setup, to access the Kubernetes Dashboard use the internal IP address (e.g. 192.168.x.y or 10.x.y.z) and not 127.0.0.1/localhost. Depending on the hardware used, it might take a while until it starts and setups everything.
41 |
42 | Create
43 | ^^^^^^
44 |
45 | Change to one of the sub-directories and enter the following command to start the cluster:
46 |
47 | .. code:: shell
48 |
49 | make
50 |
51 | .. note:: This will destroy any existing VMs, creates new VMs and performs all the steps (forced initialization, configuration, generation and deployment) to create the cluster.
52 |
53 | Stop
54 | ^^^^^^
55 |
56 | For the local setup, just press CTRL+C.
57 |
58 | For the other setups enter:
59 |
60 | .. code:: shell
61 |
62 | make halt
63 |
64 | Start
65 | ^^^^^
66 |
67 | To start an existing setup/VMs enter:
68 |
69 | .. code:: shell
70 |
71 | make up
72 |
73 | .. note:: This and the following commands work only for Vagrant based setups.
74 |
75 | SSH
76 | ^^^
77 |
78 | For single-node setups enter:
79 |
80 | .. code:: shell
81 |
82 | make ssh
83 |
84 | And for multi-node setups:
85 |
86 | .. code:: shell
87 |
88 | make ssh-controller00
89 | make ssh-controller01
90 | make ssh-controller02
91 | make ssh-worker00
92 | make ssh-worker01
93 |
94 | Kubernetes Dashboard
95 | ^^^^^^^^^^^^^^^^^^^^
96 |
97 | This will display the token, and then it will open the web browser pointing to the address of Kubernetes Dashboard:
98 |
99 | .. code:: shell
100 |
101 | make dashboard
102 |
103 | Ingress Port Forwarding
104 | ^^^^^^^^^^^^^^^^^^^^^^^
105 |
106 | In order to start port forwarding from your host's ports 80 and 443 to Vagrant's VMs for Ingress enter:
107 |
108 | .. code:: shell
109 |
110 | make forward-80
111 | make forward-443
112 |
113 | .. note:: Both commands are blocking. So you need two different terminal sessions.
114 |
115 |
--------------------------------------------------------------------------------
/docs/_build/html/_sources/troubleshooting.rst.txt:
--------------------------------------------------------------------------------
1 | Troubleshooting
2 | ===============
3 |
4 | k8s-tew enables logging for all components by default. The log files are stored in :file:`{base-directory}/var/log/k8s-tew/`.
5 |
6 |
--------------------------------------------------------------------------------
/docs/_build/html/_static/_sphinx_javascript_frameworks_compat.js:
--------------------------------------------------------------------------------
1 | /* Compatability shim for jQuery and underscores.js.
2 | *
3 | * Copyright Sphinx contributors
4 | * Released under the two clause BSD licence
5 | */
6 |
7 | /**
8 | * small helper function to urldecode strings
9 | *
10 | * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL
11 | */
12 | jQuery.urldecode = function(x) {
13 | if (!x) {
14 | return x
15 | }
16 | return decodeURIComponent(x.replace(/\+/g, ' '));
17 | };
18 |
19 | /**
20 | * small helper function to urlencode strings
21 | */
22 | jQuery.urlencode = encodeURIComponent;
23 |
24 | /**
25 | * This function returns the parsed url parameters of the
26 | * current request. Multiple values per key are supported,
27 | * it will always return arrays of strings for the value parts.
28 | */
29 | jQuery.getQueryParameters = function(s) {
30 | if (typeof s === 'undefined')
31 | s = document.location.search;
32 | var parts = s.substr(s.indexOf('?') + 1).split('&');
33 | var result = {};
34 | for (var i = 0; i < parts.length; i++) {
35 | var tmp = parts[i].split('=', 2);
36 | var key = jQuery.urldecode(tmp[0]);
37 | var value = jQuery.urldecode(tmp[1]);
38 | if (key in result)
39 | result[key].push(value);
40 | else
41 | result[key] = [value];
42 | }
43 | return result;
44 | };
45 |
46 | /**
47 | * highlight a given string on a jquery object by wrapping it in
48 | * span elements with the given class name.
49 | */
50 | jQuery.fn.highlightText = function(text, className) {
51 | function highlight(node, addItems) {
52 | if (node.nodeType === 3) {
53 | var val = node.nodeValue;
54 | var pos = val.toLowerCase().indexOf(text);
55 | if (pos >= 0 &&
56 | !jQuery(node.parentNode).hasClass(className) &&
57 | !jQuery(node.parentNode).hasClass("nohighlight")) {
58 | var span;
59 | var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg");
60 | if (isInSVG) {
61 | span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
62 | } else {
63 | span = document.createElement("span");
64 | span.className = className;
65 | }
66 | span.appendChild(document.createTextNode(val.substr(pos, text.length)));
67 | node.parentNode.insertBefore(span, node.parentNode.insertBefore(
68 | document.createTextNode(val.substr(pos + text.length)),
69 | node.nextSibling));
70 | node.nodeValue = val.substr(0, pos);
71 | if (isInSVG) {
72 | var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
73 | var bbox = node.parentElement.getBBox();
74 | rect.x.baseVal.value = bbox.x;
75 | rect.y.baseVal.value = bbox.y;
76 | rect.width.baseVal.value = bbox.width;
77 | rect.height.baseVal.value = bbox.height;
78 | rect.setAttribute('class', className);
79 | addItems.push({
80 | "parent": node.parentNode,
81 | "target": rect});
82 | }
83 | }
84 | }
85 | else if (!jQuery(node).is("button, select, textarea")) {
86 | jQuery.each(node.childNodes, function() {
87 | highlight(this, addItems);
88 | });
89 | }
90 | }
91 | var addItems = [];
92 | var result = this.each(function() {
93 | highlight(this, addItems);
94 | });
95 | for (var i = 0; i < addItems.length; ++i) {
96 | jQuery(addItems[i].parent).before(addItems[i].target);
97 | }
98 | return result;
99 | };
100 |
101 | /*
102 | * backward compatibility for jQuery.browser
103 | * This will be supported until firefox bug is fixed.
104 | */
105 | if (!jQuery.browser) {
106 | jQuery.uaMatch = function(ua) {
107 | ua = ua.toLowerCase();
108 |
109 | var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
110 | /(webkit)[ \/]([\w.]+)/.exec(ua) ||
111 | /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
112 | /(msie) ([\w.]+)/.exec(ua) ||
113 | ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
114 | [];
115 |
116 | return {
117 | browser: match[ 1 ] || "",
118 | version: match[ 2 ] || "0"
119 | };
120 | };
121 | jQuery.browser = {};
122 | jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true;
123 | }
124 |
--------------------------------------------------------------------------------
/docs/_build/html/_static/css/badge_only.css:
--------------------------------------------------------------------------------
1 | .clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}
--------------------------------------------------------------------------------
/docs/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff
--------------------------------------------------------------------------------
/docs/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff2
--------------------------------------------------------------------------------
/docs/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff
--------------------------------------------------------------------------------
/docs/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff2
--------------------------------------------------------------------------------
/docs/_build/html/_static/css/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/html/_static/css/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/docs/_build/html/_static/css/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/html/_static/css/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/docs/_build/html/_static/css/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/html/_static/css/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/docs/_build/html/_static/css/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/html/_static/css/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/docs/_build/html/_static/css/fonts/lato-bold-italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/html/_static/css/fonts/lato-bold-italic.woff
--------------------------------------------------------------------------------
/docs/_build/html/_static/css/fonts/lato-bold-italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/html/_static/css/fonts/lato-bold-italic.woff2
--------------------------------------------------------------------------------
/docs/_build/html/_static/css/fonts/lato-bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/html/_static/css/fonts/lato-bold.woff
--------------------------------------------------------------------------------
/docs/_build/html/_static/css/fonts/lato-bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/html/_static/css/fonts/lato-bold.woff2
--------------------------------------------------------------------------------
/docs/_build/html/_static/css/fonts/lato-normal-italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/html/_static/css/fonts/lato-normal-italic.woff
--------------------------------------------------------------------------------
/docs/_build/html/_static/css/fonts/lato-normal-italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/html/_static/css/fonts/lato-normal-italic.woff2
--------------------------------------------------------------------------------
/docs/_build/html/_static/css/fonts/lato-normal.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/html/_static/css/fonts/lato-normal.woff
--------------------------------------------------------------------------------
/docs/_build/html/_static/css/fonts/lato-normal.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/html/_static/css/fonts/lato-normal.woff2
--------------------------------------------------------------------------------
/docs/_build/html/_static/doctools.js:
--------------------------------------------------------------------------------
1 | /*
2 | * doctools.js
3 | * ~~~~~~~~~~~
4 | *
5 | * Base JavaScript utilities for all Sphinx HTML documentation.
6 | *
7 | * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS.
8 | * :license: BSD, see LICENSE for details.
9 | *
10 | */
11 | "use strict";
12 |
13 | const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([
14 | "TEXTAREA",
15 | "INPUT",
16 | "SELECT",
17 | "BUTTON",
18 | ]);
19 |
20 | const _ready = (callback) => {
21 | if (document.readyState !== "loading") {
22 | callback();
23 | } else {
24 | document.addEventListener("DOMContentLoaded", callback);
25 | }
26 | };
27 |
28 | /**
29 | * Small JavaScript module for the documentation.
30 | */
31 | const Documentation = {
32 | init: () => {
33 | Documentation.initDomainIndexTable();
34 | Documentation.initOnKeyListeners();
35 | },
36 |
37 | /**
38 | * i18n support
39 | */
40 | TRANSLATIONS: {},
41 | PLURAL_EXPR: (n) => (n === 1 ? 0 : 1),
42 | LOCALE: "unknown",
43 |
44 | // gettext and ngettext don't access this so that the functions
45 | // can safely bound to a different name (_ = Documentation.gettext)
46 | gettext: (string) => {
47 | const translated = Documentation.TRANSLATIONS[string];
48 | switch (typeof translated) {
49 | case "undefined":
50 | return string; // no translation
51 | case "string":
52 | return translated; // translation exists
53 | default:
54 | return translated[0]; // (singular, plural) translation tuple exists
55 | }
56 | },
57 |
58 | ngettext: (singular, plural, n) => {
59 | const translated = Documentation.TRANSLATIONS[singular];
60 | if (typeof translated !== "undefined")
61 | return translated[Documentation.PLURAL_EXPR(n)];
62 | return n === 1 ? singular : plural;
63 | },
64 |
65 | addTranslations: (catalog) => {
66 | Object.assign(Documentation.TRANSLATIONS, catalog.messages);
67 | Documentation.PLURAL_EXPR = new Function(
68 | "n",
69 | `return (${catalog.plural_expr})`
70 | );
71 | Documentation.LOCALE = catalog.locale;
72 | },
73 |
74 | /**
75 | * helper function to focus on search bar
76 | */
77 | focusSearchBar: () => {
78 | document.querySelectorAll("input[name=q]")[0]?.focus();
79 | },
80 |
81 | /**
82 | * Initialise the domain index toggle buttons
83 | */
84 | initDomainIndexTable: () => {
85 | const toggler = (el) => {
86 | const idNumber = el.id.substr(7);
87 | const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`);
88 | if (el.src.substr(-9) === "minus.png") {
89 | el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`;
90 | toggledRows.forEach((el) => (el.style.display = "none"));
91 | } else {
92 | el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`;
93 | toggledRows.forEach((el) => (el.style.display = ""));
94 | }
95 | };
96 |
97 | const togglerElements = document.querySelectorAll("img.toggler");
98 | togglerElements.forEach((el) =>
99 | el.addEventListener("click", (event) => toggler(event.currentTarget))
100 | );
101 | togglerElements.forEach((el) => (el.style.display = ""));
102 | if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler);
103 | },
104 |
105 | initOnKeyListeners: () => {
106 | // only install a listener if it is really needed
107 | if (
108 | !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS &&
109 | !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS
110 | )
111 | return;
112 |
113 | document.addEventListener("keydown", (event) => {
114 | // bail for input elements
115 | if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return;
116 | // bail with special keys
117 | if (event.altKey || event.ctrlKey || event.metaKey) return;
118 |
119 | if (!event.shiftKey) {
120 | switch (event.key) {
121 | case "ArrowLeft":
122 | if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
123 |
124 | const prevLink = document.querySelector('link[rel="prev"]');
125 | if (prevLink && prevLink.href) {
126 | window.location.href = prevLink.href;
127 | event.preventDefault();
128 | }
129 | break;
130 | case "ArrowRight":
131 | if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
132 |
133 | const nextLink = document.querySelector('link[rel="next"]');
134 | if (nextLink && nextLink.href) {
135 | window.location.href = nextLink.href;
136 | event.preventDefault();
137 | }
138 | break;
139 | }
140 | }
141 |
142 | // some keyboard layouts may need Shift to get /
143 | switch (event.key) {
144 | case "/":
145 | if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break;
146 | Documentation.focusSearchBar();
147 | event.preventDefault();
148 | }
149 | });
150 | },
151 | };
152 |
153 | // quick alias for translations
154 | const _ = Documentation.gettext;
155 |
156 | _ready(Documentation.init);
157 |
--------------------------------------------------------------------------------
/docs/_build/html/_static/documentation_options.js:
--------------------------------------------------------------------------------
1 | const DOCUMENTATION_OPTIONS = {
2 | VERSION: '',
3 | LANGUAGE: 'en',
4 | COLLAPSE_INDEX: false,
5 | BUILDER: 'html',
6 | FILE_SUFFIX: '.html',
7 | LINK_SUFFIX: '.html',
8 | HAS_SOURCE: true,
9 | SOURCELINK_SUFFIX: '.txt',
10 | NAVIGATION_WITH_KEYS: false,
11 | SHOW_SEARCH_SUMMARY: true,
12 | ENABLE_SEARCH_SHORTCUTS: true,
13 | };
--------------------------------------------------------------------------------
/docs/_build/html/_static/file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/html/_static/file.png
--------------------------------------------------------------------------------
/docs/_build/html/_static/js/badge_only.js:
--------------------------------------------------------------------------------
1 | !function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=4)}({4:function(e,t,r){}});
--------------------------------------------------------------------------------
/docs/_build/html/_static/js/theme.js:
--------------------------------------------------------------------------------
1 | !function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap(""),n("table.docutils.footnote").wrap(""),n("table.docutils.citation").wrap(""),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t0
63 | var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
64 | var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
65 | var s_v = "^(" + C + ")?" + v; // vowel in stem
66 |
67 | this.stemWord = function (w) {
68 | var stem;
69 | var suffix;
70 | var firstch;
71 | var origword = w;
72 |
73 | if (w.length < 3)
74 | return w;
75 |
76 | var re;
77 | var re2;
78 | var re3;
79 | var re4;
80 |
81 | firstch = w.substr(0,1);
82 | if (firstch == "y")
83 | w = firstch.toUpperCase() + w.substr(1);
84 |
85 | // Step 1a
86 | re = /^(.+?)(ss|i)es$/;
87 | re2 = /^(.+?)([^s])s$/;
88 |
89 | if (re.test(w))
90 | w = w.replace(re,"$1$2");
91 | else if (re2.test(w))
92 | w = w.replace(re2,"$1$2");
93 |
94 | // Step 1b
95 | re = /^(.+?)eed$/;
96 | re2 = /^(.+?)(ed|ing)$/;
97 | if (re.test(w)) {
98 | var fp = re.exec(w);
99 | re = new RegExp(mgr0);
100 | if (re.test(fp[1])) {
101 | re = /.$/;
102 | w = w.replace(re,"");
103 | }
104 | }
105 | else if (re2.test(w)) {
106 | var fp = re2.exec(w);
107 | stem = fp[1];
108 | re2 = new RegExp(s_v);
109 | if (re2.test(stem)) {
110 | w = stem;
111 | re2 = /(at|bl|iz)$/;
112 | re3 = new RegExp("([^aeiouylsz])\\1$");
113 | re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
114 | if (re2.test(w))
115 | w = w + "e";
116 | else if (re3.test(w)) {
117 | re = /.$/;
118 | w = w.replace(re,"");
119 | }
120 | else if (re4.test(w))
121 | w = w + "e";
122 | }
123 | }
124 |
125 | // Step 1c
126 | re = /^(.+?)y$/;
127 | if (re.test(w)) {
128 | var fp = re.exec(w);
129 | stem = fp[1];
130 | re = new RegExp(s_v);
131 | if (re.test(stem))
132 | w = stem + "i";
133 | }
134 |
135 | // Step 2
136 | re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
137 | if (re.test(w)) {
138 | var fp = re.exec(w);
139 | stem = fp[1];
140 | suffix = fp[2];
141 | re = new RegExp(mgr0);
142 | if (re.test(stem))
143 | w = stem + step2list[suffix];
144 | }
145 |
146 | // Step 3
147 | re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
148 | if (re.test(w)) {
149 | var fp = re.exec(w);
150 | stem = fp[1];
151 | suffix = fp[2];
152 | re = new RegExp(mgr0);
153 | if (re.test(stem))
154 | w = stem + step3list[suffix];
155 | }
156 |
157 | // Step 4
158 | re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
159 | re2 = /^(.+?)(s|t)(ion)$/;
160 | if (re.test(w)) {
161 | var fp = re.exec(w);
162 | stem = fp[1];
163 | re = new RegExp(mgr1);
164 | if (re.test(stem))
165 | w = stem;
166 | }
167 | else if (re2.test(w)) {
168 | var fp = re2.exec(w);
169 | stem = fp[1] + fp[2];
170 | re2 = new RegExp(mgr1);
171 | if (re2.test(stem))
172 | w = stem;
173 | }
174 |
175 | // Step 5
176 | re = /^(.+?)e$/;
177 | if (re.test(w)) {
178 | var fp = re.exec(w);
179 | stem = fp[1];
180 | re = new RegExp(mgr1);
181 | re2 = new RegExp(meq1);
182 | re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
183 | if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
184 | w = stem;
185 | }
186 | re = /ll$/;
187 | re2 = new RegExp(mgr1);
188 | if (re.test(w) && re2.test(w)) {
189 | re = /.$/;
190 | w = w.replace(re,"");
191 | }
192 |
193 | // and turn initial Y back to y
194 | if (firstch == "y")
195 | w = firstch.toLowerCase() + w.substr(1);
196 | return w;
197 | }
198 | }
199 |
200 |
--------------------------------------------------------------------------------
/docs/_build/html/_static/minus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/html/_static/minus.png
--------------------------------------------------------------------------------
/docs/_build/html/_static/plus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/html/_static/plus.png
--------------------------------------------------------------------------------
/docs/_build/html/_static/pygments.css:
--------------------------------------------------------------------------------
1 | pre { line-height: 125%; }
2 | td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
3 | span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
4 | td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
5 | span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
6 | .highlight .hll { background-color: #ffffcc }
7 | .highlight { background: #f8f8f8; }
8 | .highlight .c { color: #3D7B7B; font-style: italic } /* Comment */
9 | .highlight .err { border: 1px solid #FF0000 } /* Error */
10 | .highlight .k { color: #008000; font-weight: bold } /* Keyword */
11 | .highlight .o { color: #666666 } /* Operator */
12 | .highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */
13 | .highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */
14 | .highlight .cp { color: #9C6500 } /* Comment.Preproc */
15 | .highlight .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */
16 | .highlight .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */
17 | .highlight .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */
18 | .highlight .gd { color: #A00000 } /* Generic.Deleted */
19 | .highlight .ge { font-style: italic } /* Generic.Emph */
20 | .highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
21 | .highlight .gr { color: #E40000 } /* Generic.Error */
22 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
23 | .highlight .gi { color: #008400 } /* Generic.Inserted */
24 | .highlight .go { color: #717171 } /* Generic.Output */
25 | .highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
26 | .highlight .gs { font-weight: bold } /* Generic.Strong */
27 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
28 | .highlight .gt { color: #0044DD } /* Generic.Traceback */
29 | .highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
30 | .highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
31 | .highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
32 | .highlight .kp { color: #008000 } /* Keyword.Pseudo */
33 | .highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
34 | .highlight .kt { color: #B00040 } /* Keyword.Type */
35 | .highlight .m { color: #666666 } /* Literal.Number */
36 | .highlight .s { color: #BA2121 } /* Literal.String */
37 | .highlight .na { color: #687822 } /* Name.Attribute */
38 | .highlight .nb { color: #008000 } /* Name.Builtin */
39 | .highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */
40 | .highlight .no { color: #880000 } /* Name.Constant */
41 | .highlight .nd { color: #AA22FF } /* Name.Decorator */
42 | .highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */
43 | .highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */
44 | .highlight .nf { color: #0000FF } /* Name.Function */
45 | .highlight .nl { color: #767600 } /* Name.Label */
46 | .highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
47 | .highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */
48 | .highlight .nv { color: #19177C } /* Name.Variable */
49 | .highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
50 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */
51 | .highlight .mb { color: #666666 } /* Literal.Number.Bin */
52 | .highlight .mf { color: #666666 } /* Literal.Number.Float */
53 | .highlight .mh { color: #666666 } /* Literal.Number.Hex */
54 | .highlight .mi { color: #666666 } /* Literal.Number.Integer */
55 | .highlight .mo { color: #666666 } /* Literal.Number.Oct */
56 | .highlight .sa { color: #BA2121 } /* Literal.String.Affix */
57 | .highlight .sb { color: #BA2121 } /* Literal.String.Backtick */
58 | .highlight .sc { color: #BA2121 } /* Literal.String.Char */
59 | .highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */
60 | .highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
61 | .highlight .s2 { color: #BA2121 } /* Literal.String.Double */
62 | .highlight .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */
63 | .highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */
64 | .highlight .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */
65 | .highlight .sx { color: #008000 } /* Literal.String.Other */
66 | .highlight .sr { color: #A45A77 } /* Literal.String.Regex */
67 | .highlight .s1 { color: #BA2121 } /* Literal.String.Single */
68 | .highlight .ss { color: #19177C } /* Literal.String.Symbol */
69 | .highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */
70 | .highlight .fm { color: #0000FF } /* Name.Function.Magic */
71 | .highlight .vc { color: #19177C } /* Name.Variable.Class */
72 | .highlight .vg { color: #19177C } /* Name.Variable.Global */
73 | .highlight .vi { color: #19177C } /* Name.Variable.Instance */
74 | .highlight .vm { color: #19177C } /* Name.Variable.Magic */
75 | .highlight .il { color: #666666 } /* Literal.Number.Integer.Long */
--------------------------------------------------------------------------------
/docs/_build/html/genindex.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Index — k8s-tew documentation
7 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
61 |
62 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | - Index
73 | -
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
Index
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
105 |
106 |
107 |
108 |
109 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/docs/_build/html/objects.inv:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_build/html/objects.inv
--------------------------------------------------------------------------------
/docs/_build/html/search.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Search — k8s-tew documentation
7 |
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
64 |
65 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | - Search
76 | -
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
113 |
114 |
115 |
116 |
117 |
122 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
--------------------------------------------------------------------------------
/docs/_build/html/troubleshooting.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Troubleshooting — k8s-tew documentation
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
63 |
64 |
68 |
69 |
70 |
71 |
81 |
82 |
83 |
84 |
85 | Troubleshooting
86 | k8s-tew enables logging for all components by default. The log files are stored in base-directory/var/log/k8s-tew/
.
87 |
88 |
89 |
90 |
91 |
92 |
108 |
109 |
110 |
111 |
112 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/docs/_static/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_static/.gitkeep
--------------------------------------------------------------------------------
/docs/_templates/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/_templates/.gitkeep
--------------------------------------------------------------------------------
/docs/about.rst:
--------------------------------------------------------------------------------
1 | About
2 | =====
3 |
4 | k8s-tew_ is a CLI tool to install a Kubernetes Cluster (local, single-node, multi-node or HA-cluster) on Bare Metal. It installs the most essential components needed by a cluster such as networking, storage, monitoring, logging, backuping/restoring and so on. Once the nodes are configured, k8s-tew is started on each node to supervise the cluster components. k8s-tew is also used internally to generate the configuration files for a Ceph cluster and also to start the necessary Ceph deamons with the right parameters. The Ceph functionality can be used with or without Kubernetes.
5 |
6 | .. _k8s-tew: https://github.com/darxkies/k8s-tew
7 |
8 | Why
9 | ---
10 |
11 | Kubernetes_ is a fairly complex project. For a newbie it is hard to understand and also to use. While `Kelsey Hightower's Kubernetes The Hard Way `_, on which this project is based, helps a lot to understand Kubernetes, it is optimized for the use with Google Cloud Platform.
12 |
13 | Thus, this project's aim is to give newbies an easy to use tool that allows them to tinker with Kubernetes and later on to install HA production grade clusters.
14 |
15 | .. _Kubernetes: https://kubernetes.io/
16 |
17 |
--------------------------------------------------------------------------------
/docs/eval:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darxkies/k8s-tew/25cfcb404439ce9c4b4e8f5ec1602f7b28f3d5a2/docs/eval
--------------------------------------------------------------------------------
/docs/features.rst:
--------------------------------------------------------------------------------
1 | Features
2 | ========
3 |
4 | * HA cluster setup passes all CNCF conformance tests (Kubernetes `1.10 `_, `1.11 `_, `1.12 `_, `1.13 `_, `1.14 `_, `1.15 `_, `1.16 `_, `1.17 `_, `1.18 `_, `1.19 `_, `1.20 `_, `1.21 `_, `1.22 `_ , `1.23 `_ , `1.24 `_ , `1.25 `_ , `1.26 `_ & `1.27 `_)
5 | * Container Management: `Containerd `_
6 | * Networking: `Calico `_
7 | * Ingress: `NGINX Ingress `_ and `cert-manager `_ for `Let's Encrypt `_
8 | * Storage: `Ceph/RBD `_
9 | * Metrics: `metering-metrics `_ and `Heapster `_
10 | * Monitoring: `Prometheus `_ and `Grafana `_
11 | * Logging: `Fluent-Bit `_, `Elasticsearch `_, `Kibana `_ and `Cerebro `_
12 | * Backups: `Velero `_, `Restic `_ and `Minio `_
13 | * Cluster Load Balancing: `MetalLB `_
14 | * Controller Load Balancing: `gobetween `_
15 | * Package Manager: `Helm `_
16 | * Dashboard: `Kubernetes Dashboard `_
17 | * The communication between the components is encrypted
18 | * RBAC is enabled
19 | * The controllers and the workers have Floating/Virtual IPs
20 | * Integrated Load Balancer for the API Servers
21 | * Support for deployment to a HA cluster using ssh
22 | * Only the changed files are deployed
23 | * No `Docker `_ installation required
24 | * No cloud provider required
25 | * Single binary without any dependencies
26 | * Runs locally
27 | * Nodes management from the command line
28 | * Downloads all the used binaries (kubernetes, calico, ceph...) from the Internet
29 | * Pull Images, Convert them to OCI and import them on the cluster for offline installations
30 | * Uses systemd to install itself as a service on the remote machine
31 | * Installs `WordPress `_ and `MySQL `_ to test drive the installation
32 |
33 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | .. spelling::
2 |
3 | prem
4 |
5 | .. k8s-tew documentation master file, created by
6 | sphinx-quickstart on Sat Oct 6 19:46:44 2018.
7 | You can adapt this file completely to your liking, but it should at least
8 | contain the root `toctree` directive.
9 |
10 | Welcome to k8s-tew's documentation!
11 | ===================================
12 |
13 | .. toctree::
14 | :maxdepth: 2
15 | :caption: Contents:
16 |
17 | about
18 | features
19 | quickstart
20 | installation
21 | usage
22 | setups
23 | troubleshooting
24 |
25 |
--------------------------------------------------------------------------------
/docs/installation.rst:
--------------------------------------------------------------------------------
1 | Installation
2 | ============
3 |
4 | The commands in the upcoming sections will assume that k8s-tew is going to be installed in the directory :file:`/usr/local/bin`. That means that the aforementioned directory exists and it is included in the PATH. If that is not the case use the following commands:
5 |
6 | .. code:: shell
7 |
8 | sudo mkdir -p /usr/local/bin
9 | export PATH=/usr/local/bin:$PATH
10 |
11 | Binary
12 | ------
13 |
14 | The x86 64-bit binary can be downloaded from the following address: https://github.com/darxkies/k8s-tew/releases
15 |
16 | Additionally the these commands can be used to download it and install it in :file:`/usr/local/bin`
17 |
18 | .. code:: shell
19 |
20 | curl -s https://api.github.com/repos/darxkies/k8s-tew/releases/latest | grep "browser_download_url" | cut -d : -f 2,3 | tr -d \" | sudo wget -O /usr/local/bin/k8s-tew -qi -
21 | sudo chmod a+x /usr/local/bin/k8s-tew
22 |
23 | Source
24 | ------
25 |
26 | To compile it from source you will need a Go (version 1.15+) environment, Git, Make and Docker installed. Once everything is installed, enter the following commands:
27 |
28 | .. code:: shell
29 |
30 | export GOPATH=~/go
31 | export PATH=$GOPATH/bin:$PATH
32 | mkdir -p $GOPATH/src/github.com/darxkies
33 | cd $GOPATH/src/github.com/darxkies
34 | git clone https://github.com/darxkies/k8s-tew.git
35 | cd k8s-tew
36 | make
37 | sudo mv k8s-tew /usr/local/bin
38 |
39 |
--------------------------------------------------------------------------------
/docs/quickstart.rst:
--------------------------------------------------------------------------------
1 | Quick Start
2 | ===========
3 |
4 | The following snippet will create a cluster on the host computer or in a virtual machine:
5 |
6 | .. code:: shell
7 |
8 | # Switch to user root
9 | sudo su -
10 |
11 | # Download Binary
12 | wget https://github.com/darxkies/k8s-tew/releases/download/2.4.1/k8s-tew
13 | chmod a+x k8s-tew
14 |
15 | # Everything is installed relative to the root directory
16 | export K8S_TEW_BASE_DIRECTORY=/
17 |
18 | # Initialize cluster configuration
19 | ./k8s-tew initialize
20 |
21 | # Node the current machine to the cluster (the settings such as IP and hostname are inferred)
22 | ./k8s-tew node-add -s
23 |
24 | # Only on Ubuntu 20.04 to solve any DNS related issues
25 | ./k8s-tew configure --resolv-conf=/run/systemd/resolve/resolv.conf
26 |
27 | # Generate artefacts (e.g. certificates, configurations and so on)
28 | ./k8s-tew generate
29 |
30 | # Activate and start service
31 | systemctl daemon-reload
32 | systemctl enable k8s-tew
33 | systemctl start k8s-tew
34 |
35 | # Activate environment variables and switch back to root
36 | exit
37 | sudo su -
38 |
39 | # Watch the pods being installed
40 | watch -n 1 kubectl get pods --all-namespaces
41 |
42 | .. note:: You will need at least 20GB HDD, 8GB RAM and 4 CPU Cores.
43 | .. note:: To use k8s-tew with Vagrant take a look at `https://github.com/darxkies/k8s-tew/tree/2.4.1/setup `_.
44 |
45 |
--------------------------------------------------------------------------------
/docs/setups.rst:
--------------------------------------------------------------------------------
1 | Setups
2 | ======
3 |
4 | Vagrant
5 | -------
6 |
7 | Vagrant/VirtualBox can be used to test drive k8s-tew. The host is used to bootstrap the cluster which runs in VirtualBox. The Vagrantfile included in the repository can be used for single-node/multi-node & Ubuntu 20.04/CentOS 8.2 setups.
8 |
9 | The Vagrantfile can be configured using the environment variables:
10 |
11 | - OS - define the operating system. It accepts ubuntu, the default value, and centos.
12 | - MULTI_NODE - if set then a HA cluster is generated. Otherwise a single-node setup is used.
13 | - CONTROLLERS - defines the number of controller nodes. The default number is 3.
14 | - WORKERS - specifies the number of worker nodes. The default number is 2.
15 | - SSH_PUBLIC_KEY - if this environment variable is not set, then :file:`$HOME/.ssh/id_rsa` is used by default.
16 | - IP_PREFIX - this value is used to generate the IP addresses of the nodes. If not set 192.168.100 will be used. The single node has the IP address 192.168.100.50. The controllers start with the IP address 192.168.100.200 and the workers with 192.168.100.100.
17 | - CONTROLLERS_RAM - amount of RAM for one controller
18 | - WORKERS_RAM - amount of RAM for one worker
19 | - STORAGE_RAM - amount of RAM for one storage
20 | - CONTROLLERS_CPUS - number of CPUs per controller
21 | - WORKERS_CPUS - number of CPUs per worker
22 | - STORAGE_CPUS - number of CPUs per storage
23 |
24 | .. note:: The multi-node setup with the default settings needs about 20GB RAM for itself.
25 |
26 |
27 | Usage
28 | ^^^^^
29 |
30 | The directory called :file:`setup` (`https://github.com/darxkies/k8s-tew/tree/2.4.1/setup `_) contains sub-directories for various cluster setup configurations:
31 |
32 | - local - it starts a single-node cluster locally without using any kind of virtualization. This kind of setup needs root rights. It is meant for local development where it might be important to fire the cluster up and shut it down fast. If you want it to start automatically, take a look at the quickstart section.
33 | - ubuntu-single-node - Ubuntu 20.04 single-node cluster. It needs about 8GB Ram.
34 | - ubuntu-multi-node - Ubuntu 20.04 HA cluster. It needs around 20GB Ram.
35 | - centos-single-node - CentOS 8.2 single-node cluster. It needs about 8GB Ram.
36 | - centos-multi-node - CentOS 8.2 HA cluster. It needs around 20GB Ram.
37 |
38 | .. note:: Regardless of the setup, once the deployment is done it will take a while to download all required containers from the internet. So better use kubectl to check the status of the pods.
39 |
40 | .. note:: For the local setup, to access the Kubernetes Dashboard use the internal IP address (e.g. 192.168.x.y or 10.x.y.z) and not 127.0.0.1/localhost. Depending on the hardware used, it might take a while until it starts and setups everything.
41 |
42 | Create
43 | ^^^^^^
44 |
45 | Change to one of the sub-directories and enter the following command to start the cluster:
46 |
47 | .. code:: shell
48 |
49 | make
50 |
51 | .. note:: This will destroy any existing VMs, creates new VMs and performs all the steps (forced initialization, configuration, generation and deployment) to create the cluster.
52 |
53 | Stop
54 | ^^^^^^
55 |
56 | For the local setup, just press CTRL+C.
57 |
58 | For the other setups enter:
59 |
60 | .. code:: shell
61 |
62 | make halt
63 |
64 | Start
65 | ^^^^^
66 |
67 | To start an existing setup/VMs enter:
68 |
69 | .. code:: shell
70 |
71 | make up
72 |
73 | .. note:: This and the following commands work only for Vagrant based setups.
74 |
75 | SSH
76 | ^^^
77 |
78 | For single-node setups enter:
79 |
80 | .. code:: shell
81 |
82 | make ssh
83 |
84 | And for multi-node setups:
85 |
86 | .. code:: shell
87 |
88 | make ssh-controller00
89 | make ssh-controller01
90 | make ssh-controller02
91 | make ssh-worker00
92 | make ssh-worker01
93 |
94 | Kubernetes Dashboard
95 | ^^^^^^^^^^^^^^^^^^^^
96 |
97 | This will display the token, and then it will open the web browser pointing to the address of Kubernetes Dashboard:
98 |
99 | .. code:: shell
100 |
101 | make dashboard
102 |
103 | Ingress Port Forwarding
104 | ^^^^^^^^^^^^^^^^^^^^^^^
105 |
106 | In order to start port forwarding from your host's ports 80 and 443 to Vagrant's VMs for Ingress enter:
107 |
108 | .. code:: shell
109 |
110 | make forward-80
111 | make forward-443
112 |
113 | .. note:: Both commands are blocking. So you need two different terminal sessions.
114 |
115 |
--------------------------------------------------------------------------------
/docs/troubleshooting.rst:
--------------------------------------------------------------------------------
1 | Troubleshooting
2 | ===============
3 |
4 | k8s-tew enables logging for all components by default. The log files are stored in :file:`{base-directory}/var/log/k8s-tew/`.
5 |
6 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/darxkies/k8s-tew
2 |
3 | go 1.18
4 |
5 | replace github.com/docker/distribution => github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible
6 |
7 | require (
8 | github.com/briandowns/spinner v1.23.0
9 | github.com/cavaliercoder/grab v2.0.0+incompatible
10 | github.com/davecgh/go-spew v1.1.1
11 | github.com/evanphx/json-patch v5.6.0+incompatible
12 | github.com/opencontainers/go-digest v1.0.0
13 | github.com/opencontainers/image-spec v1.0.2
14 | github.com/pkg/errors v0.9.1
15 | github.com/satori/go.uuid v1.2.0
16 | github.com/sethvargo/go-password v0.2.0
17 | github.com/sirupsen/logrus v1.9.0
18 | github.com/smallnest/goreq v0.0.0-20180727030113-2e3372c80388
19 | github.com/spf13/cobra v1.7.0
20 | github.com/spf13/pflag v1.0.5
21 | github.com/spf13/viper v1.15.0
22 | github.com/tmc/scp v0.0.0-20170824174625-f7b48647feef
23 | github.com/wille/osutil v0.0.0-20230417145339-416c15a22a77
24 | golang.org/x/crypto v0.8.0
25 | golang.org/x/sys v0.7.0
26 | google.golang.org/grpc v1.54.0
27 | gopkg.in/ini.v1 v1.67.0
28 | gopkg.in/yaml.v2 v2.4.0
29 | k8s.io/api v0.27.1
30 | k8s.io/apimachinery v0.27.1
31 | k8s.io/cli-runtime v0.27.1
32 | k8s.io/client-go v0.27.1
33 | k8s.io/cri-api v0.22.1
34 | k8s.io/kubectl v0.27.1
35 | )
36 |
37 | require (
38 | github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
39 | github.com/MakeNowJust/heredoc v1.0.0 // indirect
40 | github.com/PuerkitoBio/purell v1.2.0 // indirect
41 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
42 | github.com/cespare/reflex v0.3.1 // indirect
43 | github.com/chai2010/gettext-go v1.0.2 // indirect
44 | github.com/creack/pty v1.1.18 // indirect
45 | github.com/emicklei/go-restful/v3 v3.10.2 // indirect
46 | github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
47 | github.com/fatih/color v1.15.0 // indirect
48 | github.com/fsnotify/fsnotify v1.6.0 // indirect
49 | github.com/go-errors/errors v1.4.2 // indirect
50 | github.com/go-logr/logr v1.2.4 // indirect
51 | github.com/go-openapi/jsonpointer v0.19.6 // indirect
52 | github.com/go-openapi/jsonreference v0.20.2 // indirect
53 | github.com/go-openapi/swag v0.22.3 // indirect
54 | github.com/gogo/protobuf v1.3.2 // indirect
55 | github.com/golang/protobuf v1.5.3 // indirect
56 | github.com/google/btree v1.1.2 // indirect
57 | github.com/google/gnostic v0.6.9 // indirect
58 | github.com/google/go-cmp v0.5.9 // indirect
59 | github.com/google/gofuzz v1.2.0 // indirect
60 | github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
61 | github.com/google/uuid v1.3.0 // indirect
62 | github.com/googleapis/gnostic v0.5.5 // indirect
63 | github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
64 | github.com/hashicorp/hcl v1.0.0 // indirect
65 | github.com/imdario/mergo v0.3.15 // indirect
66 | github.com/inconshreveable/mousetrap v1.1.0 // indirect
67 | github.com/josharian/intern v1.0.0 // indirect
68 | github.com/json-iterator/go v1.1.12 // indirect
69 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
70 | github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
71 | github.com/magiconair/properties v1.8.7 // indirect
72 | github.com/mailru/easyjson v0.7.7 // indirect
73 | github.com/mattn/go-colorable v0.1.13 // indirect
74 | github.com/mattn/go-isatty v0.0.18 // indirect
75 | github.com/mitchellh/go-wordwrap v1.0.1 // indirect
76 | github.com/mitchellh/mapstructure v1.5.0 // indirect
77 | github.com/moby/spdystream v0.2.0 // indirect
78 | github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
79 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
80 | github.com/modern-go/reflect2 v1.0.2 // indirect
81 | github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
82 | github.com/moul/http2curl v1.0.0 // indirect
83 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
84 | github.com/ogier/pflag v0.0.1 // indirect
85 | github.com/onsi/gomega v1.27.4 // indirect
86 | github.com/pelletier/go-toml v1.9.5 // indirect
87 | github.com/pelletier/go-toml/v2 v2.0.7 // indirect
88 | github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
89 | github.com/pmezard/go-difflib v1.0.0 // indirect
90 | github.com/russross/blackfriday v1.6.0 // indirect
91 | github.com/russross/blackfriday/v2 v2.1.0 // indirect
92 | github.com/spf13/afero v1.9.5 // indirect
93 | github.com/spf13/cast v1.5.0 // indirect
94 | github.com/spf13/jwalterweatherman v1.1.0 // indirect
95 | github.com/stretchr/testify v1.8.2 // indirect
96 | github.com/subosito/gotenv v1.4.2 // indirect
97 | github.com/xlab/treeprint v1.2.0 // indirect
98 | go.starlark.net v0.0.0-20230302034142-4b1e35fe2254 // indirect
99 | golang.org/x/net v0.9.0 // indirect
100 | golang.org/x/oauth2 v0.7.0 // indirect
101 | golang.org/x/term v0.7.0 // indirect
102 | golang.org/x/text v0.9.0 // indirect
103 | golang.org/x/time v0.3.0 // indirect
104 | google.golang.org/appengine v1.6.7 // indirect
105 | google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
106 | google.golang.org/protobuf v1.30.0 // indirect
107 | gopkg.in/inf.v0 v0.9.1 // indirect
108 | gopkg.in/yaml.v3 v3.0.1 // indirect
109 | k8s.io/component-base v0.27.1 // indirect
110 | k8s.io/klog/v2 v2.90.1 // indirect
111 | k8s.io/kube-openapi v0.0.0-20230327201221-f5883ff37f0c // indirect
112 | k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect
113 | sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
114 | sigs.k8s.io/kustomize/api v0.13.2 // indirect
115 | sigs.k8s.io/kustomize/kyaml v0.14.1 // indirect
116 | sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
117 | sigs.k8s.io/yaml v1.3.0 // indirect
118 | )
119 |
--------------------------------------------------------------------------------
/pkg/config/asset_config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | type AssetConfig struct {
4 | Directories map[string]*AssetDirectory
5 | Files map[string]*AssetFile
6 | }
7 |
--------------------------------------------------------------------------------
/pkg/config/asset_directory.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | type AssetDirectory struct {
4 | Labels Labels `yaml:"labels,omitempty"`
5 | Directory string `yaml:"directory"`
6 | Absolute bool `yaml:"absolute,omitempty"`
7 | }
8 |
9 | func NewAssetDirectory(labels []string, directory string, absolute bool) *AssetDirectory {
10 | return &AssetDirectory{Labels: labels, Directory: directory, Absolute: absolute}
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/config/asset_file.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | type AssetFile struct {
4 | Labels Labels `yaml:"labels,omitempty"`
5 | Filename string `yaml:"filename,omitempty"`
6 | Directory string `yaml:"directory"`
7 | }
8 |
9 | func NewAssetFile(labels []string, filename, directory string) *AssetFile {
10 | return &AssetFile{Labels: labels, Filename: filename, Directory: directory}
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/config/command.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | type Command struct {
4 | Name string `yaml:"name"`
5 | Command string `yaml:"command,omitempty"`
6 | Manifest string `yaml:"manifest,omitempty"`
7 | Labels Labels `yaml:"labels,omitempty"`
8 | Features Features `yaml:"features,omitempty"`
9 | OS OS `yaml:"os,omitempty"`
10 | }
11 |
12 | type Commands []*Command
13 | type OS []string
14 |
15 | func NewCommand(name string, labels Labels, features Features, os OS, command string) *Command {
16 | return &Command{Name: name, Labels: labels, Features: features, OS: os, Command: command}
17 | }
18 |
19 | func NewManifest(name string, labels Labels, features Features, os OS, manifest string) *Command {
20 | return &Command{Name: name, Labels: labels, Features: features, OS: os, Manifest: manifest}
21 | }
22 |
--------------------------------------------------------------------------------
/pkg/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "github.com/darxkies/k8s-tew/pkg/utils"
5 | uuid "github.com/satori/go.uuid"
6 | )
7 |
8 | type Config struct {
9 | Version string `yaml:"version"`
10 | ClusterID string `yaml:"cluster-id"`
11 | ClusterName string `yaml:"cluster-name"`
12 | Email string `yaml:"email"`
13 | IngressDomain string `yaml:"ingress-domain"`
14 | LoadBalancerPort uint16 `yaml:"load-balancer-port"`
15 | VIPRaftControllerPort uint16 `yaml:"vip-raft-controller-port"`
16 | VIPRaftWorkerPort uint16 `yaml:"vip-raft-worker-port"`
17 | KubernetesDashboardPort uint16 `yaml:"kubernetes-dashboard-port"`
18 | APIServerPort uint16 `yaml:"apiserver-port,omitempty"`
19 | PublicNetwork string `yaml:"public-network"`
20 | ControllerVirtualIP string `yaml:"controller-virtual-ip,omitempty"`
21 | ControllerVirtualIPInterface string `yaml:"controller-virtual-ip-interface,omitempty"`
22 | WorkerVirtualIP string `yaml:"worker-virtual-ip,omitempty"`
23 | WorkerVirtualIPInterface string `yaml:"worker-virtual-ip-interface,omitempty"`
24 | ClusterDomain string `yaml:"cluster-domain"`
25 | ClusterIPRange string `yaml:"cluster-ip-range"`
26 | ClusterDNSIP string `yaml:"cluster-dns-ip"`
27 | ClusterCIDR string `yaml:"cluster-cidr"`
28 | CalicoTyphaIP string `yaml:"calico-typha-ip"`
29 | CephClusterName string `yaml:"ceph-cluster-name"`
30 | CephPlacementGroups uint `yaml:"ceph-placement-groups"`
31 | CephExpectedNumberOfObjects uint `yaml:"ceph-expected-number-of-objects"`
32 | MetalLBAddresses string `yaml:"metallb-addresses"`
33 | ResolvConf string `yaml:"resolv-conf"`
34 | DeploymentDirectory string `yaml:"deployment-directory,omitempty"`
35 | MaxPods uint16 `yaml:"max-pods"`
36 | SANIPAddresses string `yaml:"san-ip-addresses,omitempty"`
37 | SANDNSNames string `yaml:"san-dns-names,omitempty"`
38 | RSASize uint16 `yaml:"rsa-size"`
39 | CAValidityPeriod uint `yaml:"ca-validity-period"`
40 | ClientValidityPeriod uint `yaml:"client-validity-period"`
41 | GrafanaSize uint16 `yaml:"grafana-size"`
42 | PrometheusSize uint16 `yaml:"prometheus-size"`
43 | MinioSize uint16 `yaml:"minio-size"`
44 | ElasticsearchCount uint16 `yaml:"elasticsearch-count"`
45 | ElasticsearchSize uint16 `yaml:"elasticsearch-size"`
46 | AlertManagerCount uint16 `yaml:"alert-manager-count"`
47 | AlertManagerSize uint16 `yaml:"alert-manager-size"`
48 | KubeStateMetricsCount uint16 `yaml:"kube-state-metrics-count"`
49 | DrainGracePeriodSeconds uint16 `yaml:"drain-grace-period-seconds"`
50 | Versions Versions `yaml:"versions"`
51 | Assets AssetConfig `yaml:"assets,omitempty"`
52 | Nodes Nodes `yaml:"nodes"`
53 | Commands Commands `yaml:"commands,omitempty"`
54 | Servers Servers `yaml:"servers,omitempty"`
55 | }
56 |
57 | func NewConfig() *Config {
58 | config := &Config{Version: utils.VersionConfig}
59 |
60 | config.VIPRaftControllerPort = utils.PortVipRaftController
61 | config.VIPRaftWorkerPort = utils.PortVipRaftWorker
62 | config.ClusterID = uuid.NewV4().String()
63 | config.ClusterName = utils.ClusterName
64 | config.Email = utils.Email
65 | config.IngressDomain = utils.IngressDomain
66 | config.LoadBalancerPort = utils.PortLoadBalancer
67 | config.KubernetesDashboardPort = utils.PortKubernetesDashboard
68 | config.APIServerPort = utils.PortApiServer
69 | config.PublicNetwork = utils.PublicNetwork
70 | config.ClusterDomain = utils.ClusterDomain
71 | config.ClusterIPRange = utils.ClusterIpRange
72 | config.ClusterDNSIP = utils.ClusterDnsIp
73 | config.ClusterCIDR = utils.ClusterCidr
74 | config.CalicoTyphaIP = utils.CalicoTyphaIp
75 | config.CephClusterName = utils.CephClusterName
76 | config.CephPlacementGroups = utils.CephPlacementGroups
77 | config.CephExpectedNumberOfObjects = utils.CephExpectedNumberOfObjects
78 | config.MetalLBAddresses = utils.MetalLBAddresses
79 | config.ResolvConf = utils.ResolvConf
80 | config.DeploymentDirectory = utils.DeploymentDirectory
81 | config.MaxPods = utils.MaxPods
82 | config.RSASize = utils.RsaSize
83 | config.CAValidityPeriod = utils.CaValidityPeriod
84 | config.ClientValidityPeriod = utils.ClientValidityPeriod
85 | config.GrafanaSize = utils.GrafanaSize
86 | config.PrometheusSize = utils.PrometheusSize
87 | config.MinioSize = utils.MinioSize
88 | config.ElasticsearchCount = utils.ElasticsearchCount
89 | config.ElasticsearchSize = utils.ElasticsearchSize
90 | config.AlertManagerCount = utils.AlertManagerCount
91 | config.AlertManagerSize = utils.AlertManagerSize
92 | config.KubeStateMetricsCount = utils.KubeStateMetricsCount
93 | config.DrainGracePeriodSeconds = utils.DrainGracePeriodSeconds
94 | config.Versions = NewVersions()
95 | config.Assets = AssetConfig{Directories: map[string]*AssetDirectory{}, Files: map[string]*AssetFile{}}
96 | config.Nodes = Nodes{}
97 | config.Commands = Commands{}
98 | config.Servers = Servers{}
99 |
100 | return config
101 | }
102 |
--------------------------------------------------------------------------------
/pkg/config/features.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | type Features []string
4 |
5 | func (features Features) HasFeatures(otherFeatures Features) bool {
6 | for _, feature := range features {
7 | for _, otherFeature := range otherFeatures {
8 | if feature == otherFeature {
9 | return true
10 | }
11 | }
12 | }
13 |
14 | return false
15 | }
16 |
17 | func CompareFeatures(source, destination Features) bool {
18 | return source != nil && destination != nil && source.HasFeatures(destination)
19 | }
20 |
--------------------------------------------------------------------------------
/pkg/config/images.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "crypto/sha256"
5 | "fmt"
6 | )
7 |
8 | type Image struct {
9 | Name string
10 | Features Features
11 | }
12 |
13 | type Images []Image
14 |
15 | func (image Image) GetImageFilename() (result string) {
16 |
17 | for _, char := range image.Name {
18 | if (char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z') || (char >= '0' && char <= '9') {
19 | result += string(char)
20 |
21 | continue
22 | }
23 |
24 | result += "_"
25 | }
26 |
27 | result = fmt.Sprintf("%s.%X.tar", result, sha256.Sum256([]byte(image.Name)))
28 |
29 | return
30 | }
31 |
--------------------------------------------------------------------------------
/pkg/config/labels.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | type Labels []string
4 |
5 | func (labels Labels) HasLabels(otherLabels Labels) bool {
6 | for _, label := range labels {
7 | for _, otherLabel := range otherLabels {
8 | if label == otherLabel {
9 | return true
10 | }
11 | }
12 | }
13 |
14 | return false
15 | }
16 |
17 | func CompareLabels(source, destination Labels) bool {
18 | return source != nil && destination != nil && source.HasLabels(destination)
19 | }
20 |
--------------------------------------------------------------------------------
/pkg/config/logger_config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | type LoggerConfig struct {
4 | Enabled bool `yaml:"enabled"`
5 | Filename string `yaml:"filename"`
6 | }
7 |
--------------------------------------------------------------------------------
/pkg/config/node.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import "github.com/darxkies/k8s-tew/pkg/utils"
4 |
5 | type Node struct {
6 | IP string `yaml:"ip"`
7 | Index uint `yaml:"index"`
8 | StorageIndex uint `yaml:"storage-index,omitempty"`
9 | Labels Labels `yaml:"labels"`
10 | }
11 |
12 | type Nodes map[string]*Node
13 |
14 | func (nodes Nodes) HasControllerNode() bool {
15 | for _, node := range nodes {
16 | if node.IsController() {
17 | return true
18 | }
19 | }
20 |
21 | return false
22 | }
23 |
24 | func (nodes Nodes) HasWorkerNode() bool {
25 | for _, node := range nodes {
26 | if node.IsWorker() {
27 | return true
28 | }
29 | }
30 |
31 | return false
32 | }
33 |
34 | func (nodes Nodes) HasStorageNode() bool {
35 | for _, node := range nodes {
36 | if node.IsStorage() {
37 | return true
38 | }
39 | }
40 |
41 | return false
42 | }
43 |
44 | func NewNode(ip string, index, storageIndex uint, labels []string) *Node {
45 | return &Node{IP: ip, Index: index, StorageIndex: storageIndex, Labels: labels}
46 | }
47 |
48 | func (node *Node) IsController() bool {
49 | for _, label := range node.Labels {
50 | if label == utils.NodeController {
51 | return true
52 | }
53 | }
54 |
55 | return false
56 | }
57 |
58 | func (node *Node) IsWorker() bool {
59 | for _, label := range node.Labels {
60 | if label == utils.NodeWorker {
61 | return true
62 | }
63 | }
64 |
65 | return false
66 | }
67 |
68 | func (node *Node) IsStorage() bool {
69 | for _, label := range node.Labels {
70 | if label == utils.NodeStorage {
71 | return true
72 | }
73 | }
74 |
75 | return false
76 | }
77 |
78 | func (node *Node) IsControllerOnly() bool {
79 | return node.IsController() && !node.IsWorker() && !node.IsStorage()
80 | }
81 |
82 | func (node *Node) IsStorageOnly() bool {
83 | return !node.IsController() && !node.IsWorker() && node.IsStorage()
84 | }
85 |
86 | func (node *Node) IsWorkerOnly() bool {
87 | return !node.IsController() && node.IsWorker() && !node.IsStorage()
88 | }
89 |
--------------------------------------------------------------------------------
/pkg/config/server_config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | log "github.com/sirupsen/logrus"
5 | )
6 |
7 | type ServerConfig struct {
8 | Name string `yaml:"name"`
9 | Enabled bool `yaml:"enabled"`
10 | Labels Labels `yaml:"labels"`
11 | Logger LoggerConfig `yaml:"logger"`
12 | Command string `yaml:"command"`
13 | Arguments map[string]string `yaml:"arguments"`
14 | Environment map[string]string `yaml:"environment"`
15 | }
16 |
17 | type Servers []ServerConfig
18 |
19 | func (config ServerConfig) Dump() {
20 | log.WithFields(log.Fields{"name": config.Name, "labels": config.Labels, "command": config.Command}).Info("Config server")
21 |
22 | for key, value := range config.Arguments {
23 | log.WithFields(log.Fields{"name": config.Name, "argument": key, "value": value}).Info("Config server argument")
24 | }
25 |
26 | for key, value := range config.Environment {
27 | log.WithFields(log.Fields{"name": config.Name, "environment": key, "value": value}).Info("Config server environment")
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/pkg/container/image/converter/layers.go:
--------------------------------------------------------------------------------
1 | package converter
2 |
3 | type layer struct {
4 | MediaType string
5 | Size int64
6 | BlobDigest string
7 | TarDigest string
8 | Filename string
9 | }
10 |
11 | type layers []*layer
12 |
--------------------------------------------------------------------------------
/pkg/container/image/manifest/config.go:
--------------------------------------------------------------------------------
1 | package manifest
2 |
3 | type Config struct {
4 | ArgsEscaped bool
5 | AttachStdin bool
6 | AttachStderr bool
7 | AttachStdout bool
8 | Tty bool
9 | OpenStdin bool
10 | StdinOnce bool
11 | Hostname string
12 | Domainname string
13 | Image string
14 | Cmd []string
15 | Env []string
16 | User string
17 | ExposedPorts map[string]struct{}
18 | Volumes map[string]struct{}
19 | WorkingDir string
20 | Entrypoint []string
21 | OnBuild []string
22 | Labels map[string]string
23 | }
24 |
--------------------------------------------------------------------------------
/pkg/container/image/manifest/file_system_layers.go:
--------------------------------------------------------------------------------
1 | package manifest
2 |
3 | const EmptyLayer = "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
4 |
5 | type FileSystemLayer struct {
6 | BlobSum string `json:"blobSum"`
7 | }
8 |
9 | type FileSystemLayers []FileSystemLayer
10 |
--------------------------------------------------------------------------------
/pkg/container/image/manifest/history.go:
--------------------------------------------------------------------------------
1 | package manifest
2 |
3 | type HistoryEntryData struct {
4 | ID string `json:"id"`
5 | Parent string `json:"parent"`
6 | DockerVersion string `json:"docker_version"`
7 | Architecture string `json:"architecture"`
8 | OS string `json:"os"`
9 | Container string `json:"container"`
10 | Throwaway bool `json:"throwaway"`
11 | Config *Config `json:"config"`
12 | ContainerConfig *Config `json:"container_config"`
13 | Created string `json:"created"`
14 | Author string `json:"author"`
15 | }
16 |
17 | type HistoryEntry struct {
18 | V1Compatibility string `json:"v1Compatibility"`
19 | }
20 |
21 | type History []*HistoryEntry
22 |
--------------------------------------------------------------------------------
/pkg/container/image/manifest/manifest.go:
--------------------------------------------------------------------------------
1 | package manifest
2 |
3 | type Manifest struct {
4 | SchemaVersion int `json:"schemaVersion"`
5 | Name string `json:"name"`
6 | Tag string `json:"tag"`
7 | Architecture string `json:"architecture"`
8 | FileSystemLayers FileSystemLayers `json:"fsLayers"`
9 | History History `json:"history"`
10 | }
11 |
--------------------------------------------------------------------------------
/pkg/container/image/storage/storage.go:
--------------------------------------------------------------------------------
1 | package storage
2 |
3 | import (
4 | "archive/tar"
5 | "fmt"
6 | "os"
7 | "time"
8 |
9 | "github.com/pkg/errors"
10 | )
11 |
12 | type Storage interface {
13 | WriteFile(filename string, data []byte) error
14 | Close() error
15 | Remove() error
16 | }
17 |
18 | type TarStorage struct {
19 | filename string
20 | tarFile *os.File
21 | tarBall *tar.Writer
22 | timestamp time.Time
23 | }
24 |
25 | func NewTarStorage(filename string) (*TarStorage, error) {
26 | result := &TarStorage{filename: filename, timestamp: time.Now()}
27 |
28 | var error error
29 |
30 | result.tarFile, error = os.Create(filename)
31 | if error != nil {
32 | return nil, errors.Wrapf(error, "could not open %s", filename)
33 | }
34 |
35 | result.tarBall = tar.NewWriter(result.tarFile)
36 |
37 | return result, nil
38 | }
39 |
40 | func (storage *TarStorage) WriteFile(filename string, data []byte) error {
41 | tarHeader := new(tar.Header)
42 | tarHeader.Name = filename
43 | tarHeader.Size = int64(len(data))
44 | tarHeader.Mode = 0660
45 | tarHeader.ModTime = storage.timestamp
46 |
47 | if error := storage.tarBall.WriteHeader(tarHeader); error != nil {
48 | return errors.Wrapf(error, "could not write header for %s to %s", filename, storage.filename)
49 | }
50 |
51 | written, error := storage.tarBall.Write(data)
52 |
53 | if written != len(data) {
54 | return fmt.Errorf("could not write all data for %s to %s", filename, storage.filename)
55 | }
56 |
57 | if error != nil {
58 | return errors.Wrapf(error, "could not write data for %s to %s", filename, storage.filename)
59 | }
60 |
61 | return nil
62 | }
63 |
64 | func (storage *TarStorage) Close() error {
65 | if error := storage.tarBall.Close(); error != nil {
66 | return errors.Wrapf(error, "could not close tarball for %s", storage.filename)
67 | }
68 |
69 | if error := storage.tarFile.Close(); error != nil {
70 | return errors.Wrapf(error, "could not close tarfile for %s", storage.filename)
71 | }
72 |
73 | return nil
74 | }
75 |
76 | func (storage *TarStorage) Remove() error {
77 | return os.Remove(storage.filename)
78 | }
79 |
--------------------------------------------------------------------------------
/pkg/servers/server.go:
--------------------------------------------------------------------------------
1 | package servers
2 |
3 | type Server interface {
4 | Start() error
5 | Stop()
6 | Name() string
7 | }
8 |
--------------------------------------------------------------------------------
/pkg/servers/server_wrapper.go:
--------------------------------------------------------------------------------
1 | package servers
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "os"
7 | "os/exec"
8 | "path/filepath"
9 | "strings"
10 | "syscall"
11 | "time"
12 |
13 | log "github.com/sirupsen/logrus"
14 |
15 | "github.com/darxkies/k8s-tew/pkg/config"
16 | "github.com/darxkies/k8s-tew/pkg/utils"
17 | )
18 |
19 | type ServerWrapper struct {
20 | stop bool
21 | name string
22 | baseDirectory string
23 | command []string
24 | logger config.LoggerConfig
25 | environment map[string]string
26 | pathEnvironment string
27 | started bool
28 | context context.Context
29 | cancel context.CancelFunc
30 | done chan bool
31 | }
32 |
33 | func NewServerWrapper(_config config.InternalConfig, name string, serverConfig config.ServerConfig, pathEnvironment string) (Server, error) {
34 | var error error
35 |
36 | serverConfig.Command, error = _config.ApplyTemplate("command", serverConfig.Command)
37 |
38 | if error != nil {
39 | return nil, error
40 | }
41 |
42 | server := &ServerWrapper{name: name, baseDirectory: _config.BaseDirectory, command: []string{serverConfig.Command}, logger: serverConfig.Logger, pathEnvironment: pathEnvironment, environment: serverConfig.Environment}
43 |
44 | server.logger.Filename, error = _config.ApplyTemplate("LoggingDirectory", server.logger.Filename)
45 | if error != nil {
46 | return nil, error
47 | }
48 |
49 | for key, value := range serverConfig.Arguments {
50 | if len(value) == 0 {
51 | server.command = append(server.command, fmt.Sprintf("--%s", key))
52 |
53 | } else {
54 | newValue, error := _config.ApplyTemplate(fmt.Sprintf("%s.%s", server.Name(), key), value)
55 | if error != nil {
56 | return nil, error
57 | }
58 |
59 | server.command = append(server.command, fmt.Sprintf("--%s=%s", key, newValue))
60 | }
61 | }
62 |
63 | return server, nil
64 | }
65 |
66 | func (server *ServerWrapper) Start() error {
67 | if server.started {
68 | return nil
69 | }
70 |
71 | server.stop = false
72 |
73 | if server.logger.Enabled {
74 | logsDirectory := filepath.Dir(server.logger.Filename)
75 |
76 | if error := utils.CreateDirectoryIfMissing(logsDirectory); error != nil {
77 | return error
78 | }
79 | }
80 |
81 | log.WithFields(log.Fields{"name": server.Name(), "_command": strings.Join(server.command, " ")}).Info("Starting server")
82 |
83 | server.context, server.cancel = context.WithCancel(context.Background())
84 | server.done = make(chan bool, 1)
85 |
86 | server.started = true
87 |
88 | go func() {
89 | for !server.stop {
90 | command := exec.CommandContext(server.context, server.command[0], server.command[1:]...)
91 | command.SysProcAttr = &syscall.SysProcAttr{
92 | Setpgid: true,
93 | Pgid: 0,
94 | }
95 |
96 | command.Env = os.Environ()
97 | command.Env = append(command.Env, server.pathEnvironment)
98 |
99 | for key, value := range server.environment {
100 | command.Env = append(command.Env, fmt.Sprintf("%s=%s", key, value))
101 | }
102 |
103 | log.WithFields(log.Fields{"name": server.Name(), "environment": strings.Join(command.Env, " ")}).Debug("Server environment")
104 |
105 | var logFile *os.File
106 | var error error
107 |
108 | if server.logger.Enabled {
109 | logFile, error = os.OpenFile(server.logger.Filename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
110 |
111 | if error != nil {
112 | log.WithFields(log.Fields{"filename": logFile, "error": error}).Error("Could not open file")
113 |
114 | continue
115 | }
116 |
117 | command.Stdout = logFile
118 | command.Stderr = logFile
119 | }
120 |
121 | defer func() {
122 | if logFile != nil {
123 | logFile.Close()
124 | }
125 | }()
126 |
127 | error = command.Run()
128 |
129 | if !server.stop {
130 | time.Sleep(time.Second)
131 |
132 | log.WithFields(log.Fields{"name": server.name, "error": error, "_command": strings.Join(server.command, " ")}).Error("Restarting server")
133 | }
134 | }
135 |
136 | close(server.done)
137 | }()
138 |
139 | return nil
140 | }
141 |
142 | func (server *ServerWrapper) Stop() {
143 | if !server.started {
144 | return
145 | }
146 |
147 | log.WithFields(log.Fields{"name": server.Name()}).Info("Stopping server")
148 |
149 | server.stop = true
150 |
151 | server.cancel()
152 |
153 | <-server.done
154 |
155 | log.WithFields(log.Fields{"name": server.name, "_command": strings.Join(server.command, " ")}).Info("Stopped server")
156 | }
157 |
158 | func (server *ServerWrapper) Name() string {
159 | return server.name
160 | }
161 |
--------------------------------------------------------------------------------
/pkg/utils/checksums.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "crypto/md5"
5 | "encoding/hex"
6 | "fmt"
7 | "io"
8 | "os"
9 | "strings"
10 |
11 | "github.com/pkg/errors"
12 | log "github.com/sirupsen/logrus"
13 | )
14 |
15 | type Checksum struct {
16 | value string
17 | updated bool
18 | }
19 | type Checksums struct {
20 | filename string
21 | baseDirectory string
22 | checksums map[string]Checksum
23 | loaded bool
24 | }
25 |
26 | func NewChecksums(filename, baseDirectory string) *Checksums {
27 | return &Checksums{filename: filename, baseDirectory: baseDirectory, checksums: map[string]Checksum{}}
28 | }
29 |
30 | func (checksums *Checksums) md5(targetFilename string) (result string, error error) {
31 | file, error := os.Open(targetFilename)
32 | if error != nil {
33 | return
34 | }
35 |
36 | defer file.Close()
37 |
38 | hash := md5.New()
39 |
40 | if _, error = io.Copy(hash, file); error != nil {
41 | return
42 | }
43 |
44 | result = hex.EncodeToString(hash.Sum(nil)[:16])
45 |
46 | return
47 | }
48 |
49 | func (checksums *Checksums) GetChecksum(targetFilename string) (result string, error error) {
50 | relativeFilename := targetFilename[len(checksums.baseDirectory):]
51 |
52 | checksumsCache, _errorCache := os.Stat(checksums.filename)
53 | checksumsTarget, _errorTarget := os.Stat(targetFilename)
54 |
55 | if _errorCache == nil && _errorTarget == nil && checksumsTarget.ModTime().Before(checksumsCache.ModTime()) {
56 | if checksum, ok := checksums.checksums[relativeFilename]; ok {
57 | return checksum.value, nil
58 | }
59 | }
60 |
61 | if checksum, ok := checksums.checksums[relativeFilename]; ok && checksum.updated {
62 | return checksum.value, nil
63 | }
64 |
65 | result, _error := checksums.md5(targetFilename)
66 | if _error != nil {
67 | return "", _error
68 | }
69 |
70 | log.WithFields(log.Fields{"file": targetFilename, "checksum": result}).Debug("Updated checksum")
71 |
72 | checksums.checksums[relativeFilename] = Checksum{value: result, updated: true}
73 |
74 | return
75 | }
76 |
77 | func (checksums *Checksums) Save() error {
78 | buffer := ""
79 |
80 | for filename, value := range checksums.checksums {
81 | buffer += fmt.Sprintf("%s %s\n", value.value, filename)
82 | }
83 |
84 | if _error := os.WriteFile(checksums.filename, []byte(buffer), 0644); _error != nil {
85 | return errors.Wrapf(_error, "Could not write to %s", checksums.filename)
86 | }
87 |
88 | return nil
89 | }
90 |
91 | func (checksums *Checksums) Load() error {
92 | if checksums.loaded {
93 | return nil
94 | }
95 |
96 | checksums.loaded = true
97 |
98 | content, _error := os.ReadFile(checksums.filename)
99 | if _error != nil {
100 | return _error
101 | }
102 |
103 | lines := strings.Split(string(content), "\n")
104 |
105 | for _, line := range lines {
106 | if len(line) < 35 {
107 | continue
108 | }
109 |
110 | filename := line[33:]
111 | checksum := line[:32]
112 |
113 | checksums.checksums[filename] = Checksum{value: checksum, updated: false}
114 | }
115 |
116 | return nil
117 | }
118 |
--------------------------------------------------------------------------------
/pkg/utils/limiter.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | type Limiter struct {
4 | channel chan bool
5 | }
6 |
7 | func NewLimiter(limit int) *Limiter {
8 | return &Limiter{channel: make(chan bool, limit)}
9 | }
10 |
11 | func (limiter Limiter) Lock() {
12 | limiter.channel <- true
13 | }
14 |
15 | func (limiter Limiter) Unlock() {
16 | <-limiter.channel
17 | }
18 |
--------------------------------------------------------------------------------
/pkg/utils/logger.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "path"
5 |
6 | log "github.com/sirupsen/logrus"
7 | )
8 |
9 | var debug bool
10 |
11 | func SetDebug(_debug bool) {
12 | debug = _debug
13 |
14 | // Turn logging debug info on/off
15 | if debug {
16 | log.SetLevel(log.DebugLevel)
17 | } else {
18 | log.SetLevel(log.InfoLevel)
19 | }
20 | }
21 |
22 | type logrusHook struct{}
23 |
24 | func (hook logrusHook) Fire(entry *log.Entry) error {
25 | show := _progressShow
26 |
27 | HideProgress()
28 |
29 | fields := log.Fields{}
30 |
31 | for key, value := range entry.Data {
32 | // Keys that start with _ denote debug information
33 | // and are hidden for info messages as long as debugging
34 | // is turned off. The underscore is removed.
35 | if len(key) > 0 && key[0] == '_' {
36 | if entry.Level != log.InfoLevel || debug {
37 | fields[key[1:]] = value
38 | }
39 | } else {
40 | fields[key] = value
41 | }
42 | }
43 |
44 | entry.Data = fields
45 |
46 | if show {
47 | ShowProgress()
48 | }
49 |
50 | return nil
51 | }
52 |
53 | func (hook logrusHook) Levels() []log.Level {
54 | return log.AllLevels
55 | }
56 |
57 | func SetupLogger() {
58 | log.AddHook(logrusHook{})
59 | }
60 |
61 | func LogFilename(message, filename string) {
62 | name := path.Base(filename)
63 |
64 | log.WithFields(log.Fields{"name": name, "_filename": filename}).Info(message)
65 | }
66 |
67 | func LogDebugFilename(message, filename string) {
68 | name := path.Base(filename)
69 |
70 | log.WithFields(log.Fields{"name": name, "_filename": filename}).Debug(message)
71 | }
72 |
73 | func LogURL(message, url string) {
74 | name := path.Base(url)
75 |
76 | log.WithFields(log.Fields{"name": name, "_url": url}).Info(message)
77 | }
78 |
--------------------------------------------------------------------------------
/pkg/utils/progress.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "fmt"
5 | "sync"
6 | "time"
7 |
8 | "github.com/briandowns/spinner"
9 | )
10 |
11 | var _spinner *spinner.Spinner
12 | var _progressSteps int
13 | var _progressStep int
14 | var _progressShow bool
15 | var _mutex sync.Mutex
16 | var _supressProgress bool
17 |
18 | func init() {
19 | _mutex = sync.Mutex{}
20 |
21 | _spinner = spinner.New(spinner.CharSets[9], 100*time.Millisecond)
22 | }
23 |
24 | func SupressProgress(hide bool) {
25 | _supressProgress = hide
26 | }
27 |
28 | func ShowProgress() {
29 | if _supressProgress {
30 | return
31 | }
32 |
33 | _progressPercentage := 0
34 |
35 | if _progressSteps > 0 {
36 | _progressPercentage = int(float32(_progressStep+1) * 100.0 / float32(_progressSteps))
37 |
38 | if _progressPercentage > 100 {
39 | _progressPercentage = 100
40 | }
41 | }
42 |
43 | _spinner.Prefix = "["
44 | _spinner.Suffix = fmt.Sprintf("] Progress: %d%%", _progressPercentage)
45 |
46 | _spinner.Start()
47 |
48 | _progressShow = true
49 | }
50 |
51 | func HideProgress() {
52 | _spinner.Stop()
53 |
54 | _progressShow = false
55 | }
56 |
57 | func IncreaseProgressStep() {
58 | _mutex.Lock()
59 | defer _mutex.Unlock()
60 |
61 | _progressStep++
62 | }
63 |
64 | func SetProgressSteps(steps int) {
65 | _progressSteps = steps
66 | }
67 |
--------------------------------------------------------------------------------
/pkg/utils/tasks.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import "sync"
4 |
5 | type Task func() error
6 | type Tasks []Task
7 | type Errors []error
8 |
9 | func RunParallelTasks(tasks Tasks, parallel bool) (errors Errors) {
10 | if !parallel {
11 | for _, task := range tasks {
12 | if error := task(); error != nil {
13 | errors = append(errors, error)
14 |
15 | return
16 | }
17 | }
18 |
19 | return
20 | }
21 |
22 | waitGroup := sync.WaitGroup{}
23 |
24 | waitGroup.Add(len(tasks))
25 |
26 | errorChannel := make(chan error, 1)
27 | finishedChannel := make(chan bool, 1)
28 |
29 | // Schedule tasks to be executed
30 | for _, task := range tasks {
31 | go func(_task Task) {
32 | if error := _task(); error != nil {
33 | errorChannel <- error
34 | }
35 |
36 | waitGroup.Done()
37 | }(task)
38 | }
39 |
40 | // Wait for all tasks to be done and send notification
41 | go func() {
42 | waitGroup.Wait()
43 |
44 | close(finishedChannel)
45 | }()
46 |
47 | done := false
48 |
49 | // Collect errors and wait for all tasks to be done
50 | for !done {
51 | select {
52 | case <-finishedChannel:
53 | done = true
54 | case error := <-errorChannel:
55 | errors = append(errors, error)
56 | }
57 | }
58 |
59 | return
60 | }
61 |
--------------------------------------------------------------------------------
/pkg/utils/templates.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "path"
5 |
6 | "github.com/darxkies/k8s-tew/data"
7 | log "github.com/sirupsen/logrus"
8 | )
9 |
10 | func GetTemplate(name string) string {
11 | content, error := data.Templates.ReadFile(path.Join("templates", name))
12 | if error != nil {
13 | log.WithFields(log.Fields{"name": name, "error": error}).Panic("Template failure")
14 | }
15 |
16 | return string(content)
17 | }
18 |
--------------------------------------------------------------------------------
/pkg/version/version.go:
--------------------------------------------------------------------------------
1 | package version
2 |
3 | var Version string
4 |
--------------------------------------------------------------------------------
/setup/centos-multi-node/Makefile:
--------------------------------------------------------------------------------
1 | .EXPORT_ALL_VARIABLES:
2 |
3 | VAGRANT_VAGRANTFILE=../../Vagrantfile
4 | OS=centos
5 | IP_PREFIX=192.168.140
6 | MULTI_NODE=true
7 |
8 | run: destroy up deploy
9 |
10 | deploy:
11 | k8s-tew initialize -f
12 | k8s-tew configure --controller-virtual-ip=$(IP_PREFIX).20
13 | k8s-tew configure --controller-virtual-ip-interface=eth1
14 | k8s-tew configure --worker-virtual-ip=$(IP_PREFIX).10
15 | k8s-tew configure --worker-virtual-ip-interface=eth1
16 | k8s-tew configure --public-network=$(IP_PREFIX).0/24
17 | k8s-tew node-add -n controller00 -i $(IP_PREFIX).200 -l controller
18 | k8s-tew node-add -n controller01 -i $(IP_PREFIX).201 -l controller
19 | k8s-tew node-add -n controller02 -i $(IP_PREFIX).202 -l controller
20 | k8s-tew node-add -n worker00 -i $(IP_PREFIX).100 -l worker,storage
21 | k8s-tew node-add -n worker01 -i $(IP_PREFIX).101 -l worker,storage
22 | k8s-tew generate
23 | k8s-tew deploy
24 |
25 | destroy:
26 | vagrant destroy -f
27 |
28 | up:
29 | vagrant up
30 |
31 | halt:
32 | vagrant halt
33 |
34 | dashboard:
35 | k8s-tew dashboard kubernetes -o
36 |
37 | ssh-controller00:
38 | vagrant ssh controller00
39 |
40 | ssh-controller01:
41 | vagrant ssh controller01
42 |
43 | ssh-controller02:
44 | vagrant ssh controller02
45 |
46 | ssh-worker00:
47 | vagrant ssh worker00
48 |
49 | ssh-worker01:
50 | vagrant ssh worker01
51 |
52 | forward-80:
53 | sudo socat -d -v TCP-LISTEN:80,fork TCP:$(IP_PREFIX).20:80
54 |
55 | forward-443:
56 | sudo socat -d -v TCP-LISTEN:443,fork TCP:$(IP_PREFIX).20:443
57 |
--------------------------------------------------------------------------------
/setup/centos-single-node/Makefile:
--------------------------------------------------------------------------------
1 | .EXPORT_ALL_VARIABLES:
2 |
3 | VAGRANT_VAGRANTFILE=../../Vagrantfile
4 | OS=centos
5 | IP_PREFIX=192.168.130
6 |
7 | run: destroy up deploy
8 |
9 | deploy:
10 | k8s-tew initialize -f
11 | k8s-tew configure --public-network=$(IP_PREFIX).0/24
12 | k8s-tew node-add -n single-node -i $(IP_PREFIX).50 -x 0 -l controller,worker,storage
13 | k8s-tew generate
14 | k8s-tew deploy
15 |
16 | destroy:
17 | vagrant destroy -f
18 |
19 | up:
20 | vagrant up
21 |
22 | halt:
23 | vagrant halt
24 |
25 | dashboard:
26 | k8s-tew dashboard kubernetes -o
27 |
28 | ssh:
29 | vagrant ssh
30 |
31 | forward-80:
32 | sudo socat -d -v TCP-LISTEN:80,fork TCP:$(IP_PREFIX).50:80
33 |
34 | forward-443:
35 | sudo socat -d -v TCP-LISTEN:443,fork TCP:$(IP_PREFIX).50:443
36 |
--------------------------------------------------------------------------------
/setup/checks/backup.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | function wait_for_pod()
4 | {
5 | echo "waiting for pod $1"
6 |
7 | while [[ $(kubectl get pods -n showcase $1 -o 'jsonpath={..status.conditions[?(@.type=="Ready")].status}') != "True" ]]; do
8 | sleep 1
9 | done
10 | }
11 |
12 | function get_wordpress_pod_name()
13 | {
14 | kubectl get pods --selector tier=frontend -n showcase -o 'jsonpath={..metadata.name}'
15 | }
16 |
17 | function get_mysql_pod_name()
18 | {
19 | kubectl get pods --selector tier=mysql -n showcase -o 'jsonpath={..metadata.name}'
20 | }
21 |
22 | function write_to_pod()
23 | {
24 | kubectl exec -t -i -n showcase $1 -- sh -c "echo $2 > $3; sync"
25 | }
26 |
27 | function read_from_pod()
28 | {
29 | kubectl exec -t -i -n showcase $1 -- sh -c "cat $2" | tr -d '\r' | tr -d '\n'
30 | }
31 |
32 | function check_content()
33 | {
34 | local content=$(read_from_pod $1 $3)
35 |
36 | if [ "$content" == "$2" ]
37 | then
38 | echo "content of pod $1 is OK"
39 | else
40 | echo "***content of pod $1 is wrong: got '$content' but expected '$2'***"
41 | fi
42 | }
43 |
44 | function check_showcase()
45 | {
46 | local wordpress_content="wordpress-check"
47 | local wordpress_file="/var/www/html/check"
48 | local mysql_content="mysql-check"
49 | local mysql_file="/var/lib/mysql/check"
50 | local wordpress_pod_name=$(get_wordpress_pod_name)
51 | local mysql_pod_name=$(get_mysql_pod_name)
52 |
53 | write_to_pod $wordpress_pod_name $wordpress_content $wordpress_file
54 | write_to_pod $mysql_pod_name $mysql_content $mysql_file
55 |
56 | velero backup delete showcase --confirm
57 |
58 | velero backup create showcase --include-namespaces showcase --wait
59 |
60 | kubectl delete namespace showcase
61 |
62 | velero restore create --from-backup showcase --wait
63 |
64 | wait_for_pod $wordpress_pod_name
65 | wait_for_pod $mysql_pod_name
66 |
67 | check_content $wordpress_pod_name $wordpress_content $wordpress_file
68 | check_content $mysql_pod_name $mysql_content $mysql_file
69 | }
70 |
71 | check_showcase
72 |
--------------------------------------------------------------------------------
/setup/checks/ceph.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | VERSION=devel
4 |
5 | function wait_for_pod()
6 | {
7 | echo "waiting for pod $1"
8 |
9 | while [[ $(kubectl get pods $1 -o 'jsonpath={..status.conditions[?(@.type=="Ready")].status}') != "True" ]]; do
10 | sleep 1
11 | done
12 | }
13 |
14 | function write_to_pod()
15 | {
16 | kubectl exec -t -i $1 -- sh -c "echo $2 > /var/lib/www/$3/index.html;sync"
17 | }
18 |
19 | function read_from_pod()
20 | {
21 | kubectl exec -t -i $1 -- sh -c "cat /var/lib/www/$2/index.html" | tr -d '\r'
22 | }
23 |
24 | function check_content()
25 | {
26 | local content=$(read_from_pod $1 $3)
27 |
28 | if [ "$content" == "$2" ]
29 | then
30 | echo "content of pod $1 is [OK]"
31 | else
32 | echo "***content of pod $1 is wrong: got '$content' but expected '$2'***"
33 | fi
34 | }
35 |
36 | function wait_for_snapshot()
37 | {
38 | echo "waiting for snapshot $1"
39 |
40 | while [[ $(kubectl get volumesnapshots $1 -o 'jsonpath={..status.readyToUse}') != "true" ]]
41 | do
42 | sleep 1
43 | done
44 | }
45 |
46 | function create_pod()
47 | {
48 | kubectl apply -f https://raw.githubusercontent.com/ceph/ceph-csi/$VERSION/examples/$1/$2.yaml
49 | }
50 |
51 | function create_pvc()
52 | {
53 | curl -sSL https://raw.githubusercontent.com/ceph/ceph-csi/$VERSION/examples/$1/$2.yaml | sed "s/csi-$1-sc/csi-$1/" | kubectl apply -f -
54 | }
55 |
56 | function create_snapshot()
57 | {
58 | kubectl apply -f https://raw.githubusercontent.com/ceph/ceph-csi/$VERSION/examples/$1/$2.yaml
59 | }
60 |
61 | function clean_up_rbd()
62 | {
63 | kubectl delete pod pod-with-raw-block-volume csi-rbd-demo-pod csi-rbd-clone-demo-app csi-rbd-restore-demo-pod
64 | kubectl delete pvc raw-block-pvc rbd-pvc rbd-pvc-clone rbd-pvc-restore
65 | kubectl delete volumesnapshot.snapshot.storage.k8s.io rbd-pvc-snapshot
66 | }
67 |
68 | function clean_up_cephfs()
69 | {
70 | kubectl delete pod csi-cephfs-demo-pod csi-cephfs-clone-demo-app csi-cephfs-restore-demo-pod
71 | kubectl delete pvc csi-cephfs-pvc cephfs-pvc-clone cephfs-pvc-restore
72 | kubectl delete volumesnapshot.snapshot.storage.k8s.io cephfs-pvc-snapshot
73 | }
74 |
75 | function check_rbd()
76 | {
77 | clean_up_rbd
78 |
79 | # Raw - /dev/xvda
80 | create_pvc rbd raw-block-pvc
81 | create_pod rbd raw-block-pod
82 | wait_for_pod pod-with-raw-block-volume
83 |
84 | # PVC
85 | create_pvc rbd pvc
86 | create_pod rbd pod
87 | wait_for_pod csi-rbd-demo-pod
88 |
89 | # Write
90 | write_to_pod csi-rbd-demo-pod "xxx" "html"
91 |
92 | # PVC clone
93 | create_pvc rbd pvc-clone
94 | create_pod rbd pod-clone
95 | wait_for_pod csi-rbd-clone-demo-app
96 |
97 | # Write
98 | write_to_pod csi-rbd-demo-pod "yyy" "html"
99 |
100 | # Snapshot
101 | create_snapshot rbd snapshot
102 | wait_for_snapshot rbd-pvc-snapshot
103 |
104 | # Write
105 | write_to_pod csi-rbd-demo-pod "zzz" "html"
106 |
107 | # PVC restore
108 | create_pvc rbd pvc-restore
109 | create_pod rbd pod-restore
110 | wait_for_pod csi-rbd-restore-demo-pod
111 |
112 | # Check content
113 | check_content csi-rbd-clone-demo-app "xxx" "html"
114 | check_content csi-rbd-restore-demo-pod "yyy" "html"
115 | check_content csi-rbd-demo-pod "zzz" "html"
116 |
117 | clean_up_rbd
118 | }
119 |
120 | function check_cephfs()
121 | {
122 | clean_up_cephfs
123 |
124 | # PVC
125 | create_pvc cephfs pvc
126 | create_pod cephfs pod
127 | wait_for_pod csi-cephfs-demo-pod
128 |
129 | # Write
130 | write_to_pod csi-cephfs-demo-pod "xxx"
131 |
132 | # PVC clone
133 | create_pvc cephfs pvc-clone
134 | create_pod cephfs pod-clone
135 | wait_for_pod csi-cephfs-clone-demo-app
136 |
137 | # Write
138 | write_to_pod csi-cephfs-demo-pod "yyy"
139 |
140 | # Snapshot
141 | create_snapshot cephfs snapshot
142 | wait_for_snapshot cephfs-pvc-snapshot
143 |
144 | # Write
145 | write_to_pod csi-cephfs-demo-pod "zzz"
146 |
147 | # PVC restore
148 | create_pvc cephfs pvc-restore
149 | create_pod cephfs pod-restore
150 | wait_for_pod csi-cephfs-restore-demo-pod
151 |
152 | # Check content
153 | check_content csi-cephfs-clone-demo-app "xxx" "html"
154 | check_content csi-cephfs-restore-demo-pod "yyy" "html"
155 | check_content csi-cephfs-demo-pod "zzz"
156 |
157 | clean_up_cephfs
158 | }
159 |
160 | function check_rados_gateway()
161 | {
162 | local access_key=$(k8s-tew dashboard ceph-rados-gateway -q | cut -d ' ' -f 1)
163 | local secret_key=$(k8s-tew dashboard ceph-rados-gateway -q | cut -d ' ' -f 2)
164 | local url=$(k8s-tew dashboard ceph-rados-gateway -q | cut -d ' ' -f 3)
165 | local config=/tmp/s3cfg
166 | local put_file=/tmp/s3put
167 | local get_file=/tmp/s3get
168 | local content="xxx"
169 | local cmd="s3cmd -c $config --no-check-certificate"
170 |
171 | cat < $config
172 | # Setup endpoint
173 | host_base = $url
174 | host_bucket = $url
175 | bucket_location = us-east-1
176 | use_https = True
177 |
178 | # Setup access keys
179 | access_key = $access_key
180 | secret_key = $secret_key
181 |
182 | EOF
183 |
184 | echo $content > $put_file
185 |
186 | [ -f $get_file ] && rm $get_file
187 |
188 | $cmd mb s3://checks
189 | $cmd ls s3://
190 | $cmd put $put_file s3://checks/content
191 | $cmd get s3://checks/content $get_file
192 | $cmd del s3://checks/content
193 | $cmd rb s3://checks
194 |
195 | if [ "$(cat $put_file)" == "$(cat $get_file)" ]
196 | then
197 | echo "Rados Gateway check [OK]"
198 | else
199 | echo "***Rados Gateway content is not ok***"
200 | fi
201 |
202 | rm $config $put_file $get_file
203 | }
204 |
205 | check_rbd
206 | check_cephfs
207 | check_rados_gateway
208 |
--------------------------------------------------------------------------------
/setup/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:20.04
2 |
3 | ENV container docker
4 | ENV LC_ALL C
5 | ENV DEBIAN_FRONTEND noninteractive
6 |
7 | EXPOSE 22
8 |
9 | VOLUME ["/sys/fs/cgroup"]
10 |
11 | STOPSIGNAL SIGRTMIN+3
12 |
13 | RUN sed -i 's/# deb/deb/g' /etc/apt/sources.list
14 |
15 | RUN apt-get update \
16 | && apt-get install -y systemd systemd-sysv openssh-server iproute2 iputils-ping vim less iptables kmod ca-certificates curl libseccomp2 conntrack ethtool socat util-linux mount ebtables udev \
17 | && apt-get clean \
18 | && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
19 |
20 | RUN cd /lib/systemd/system/sysinit.target.wants/ \
21 | && ls | grep -v systemd-tmpfiles-setup | xargs rm -fv $1 \
22 | && mv /etc/systemd/system/multi-user.target.wants/ssh.service /tmp/ \
23 | && rm -fv /lib/systemd/system/multi-user.target.wants/* \
24 | /etc/systemd/system/*.wants/* \
25 | /lib/systemd/system/local-fs.target.wants/* \
26 | /lib/systemd/system/sockets.target.wants/*udev* \
27 | /lib/systemd/system/sockets.target.wants/*initctl* \
28 | /lib/systemd/system/basic.target.wants/* \
29 | /lib/systemd/system/anaconda.target.wants/* \
30 | /lib/systemd/system/plymouth* \
31 | /lib/systemd/system/systemd-update-utmp* \
32 | && echo "ReadKMsg=no" >> /etc/systemd/journald.conf \
33 | && mv /tmp/ssh.service /etc/systemd/system/multi-user.target.wants/
34 |
35 | RUN mkdir /var/run/sshd
36 |
37 | RUN echo 'root:root' | chpasswd
38 |
39 | RUN sed -ri 's/^#?PermitRootLogin\s+.*/PermitRootLogin yes/' /etc/ssh/sshd_config
40 | RUN sed -ri 's/UsePAM yes/#UsePAM yes/g' /etc/ssh/sshd_config
41 |
42 | RUN mkdir /root/.ssh && \
43 | touch /root/.ssh/authorized_keys && \
44 | chmod 700 /root/.ssh && \
45 | chmod 600 /root/.ssh/authorized_keys
46 |
47 | COPY files/ssh_public_key /root/.ssh/authorized_keys
48 |
49 | CMD ["/lib/systemd/systemd"]
50 |
--------------------------------------------------------------------------------
/setup/docker/Makefile:
--------------------------------------------------------------------------------
1 | CONTAINER ?= k8s-tew-in-docker
2 | NAME ?= $(CONTAINER)
3 | SSH_PUBLIC_KEY ?= ~/.ssh/id_rsa.pub
4 | IP ?= $(shell docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(NAME))
5 |
6 | build:
7 | mkdir -p files
8 | cp $(SSH_PUBLIC_KEY) files/ssh_public_key
9 | docker build -t $(CONTAINER) .
10 |
11 | start:
12 | docker run \
13 | -d \
14 | --name $(NAME) \
15 | --hostname $(NAME) \
16 | -v /lib/modules:/lib/modules:ro \
17 | -v /sys/fs/cgroup:/sys/fs/cgroup \
18 | -v /var/lib/k8s-tew \
19 | -v /var/log/k8s-tew \
20 | --privileged \
21 | --security-opt seccomp=unconfined \
22 | --security-opt apparmor=unconfined \
23 | --rm $(CONTAINER)
24 |
25 |
26 | attach:
27 | docker exec -ti $(NAME) bash
28 |
29 | stop:
30 | docker kill $(NAME)
31 |
32 | shell:
33 | ssh -o StrictHostKeyChecking=no root@$(IP)
34 |
35 | deploy:
36 | k8s-tew initialize -f
37 | k8s-tew configure --public-network=$(IP)/24
38 | k8s-tew configure --metallb-addresses=$(IP)/32
39 | k8s-tew node-add -n $(NAME) -i $(IP) -x 0 -l controller,worker,storage
40 | k8s-tew generate --pull-images --parallel
41 | k8s-tew deploy --import-images --parallel
42 |
--------------------------------------------------------------------------------
/setup/local/Makefile:
--------------------------------------------------------------------------------
1 |
2 | run:
3 | k8s-tew initialize -f
4 | # Remove this line if you do not use Ubuntu
5 | k8s-tew configure --resolv-conf=/run/systemd/resolve/resolv.conf
6 | k8s-tew node-add -s
7 | k8s-tew generate
8 | sudo $$(which k8s-tew) run
9 |
10 | dashboard:
11 | k8s-tew dashboard -o
12 |
--------------------------------------------------------------------------------
/setup/multipass/Makefile:
--------------------------------------------------------------------------------
1 | .EXPORT_ALL_VARIABLES:
2 |
3 | NAME=single-node
4 | IP=$(shell multipass info $(NAME) 2> /dev/null | grep IPv4 | sed -e "s/IPv4://" | xargs)
5 | SSH_KEY=$(shell cat ~/.ssh/id_rsa.pub)
6 | CLOUD_INIT="disable_root: 0\\nssh_authorized_keys:\\n - $(SSH_KEY)"
7 |
8 | run: destroy create deploy
9 |
10 | deploy:
11 | k8s-tew initialize -f
12 | k8s-tew configure --ingress-domain=darxkies.duckdns.org
13 | k8s-tew configure --email=darxkies@gmail.com
14 | k8s-tew configure --public-network=$(IP)/24
15 | k8s-tew configure --resolv-conf=/run/systemd/resolve/resolv.conf
16 | k8s-tew configure --metallb-addresses=$(IP)/32
17 | k8s-tew node-add -n single-node -i $(IP) -l controller,worker,storage
18 | k8s-tew generate
19 | k8s-tew deploy --parallel --wait=5
20 |
21 | refresh:
22 | k8s-tew generate --parallel
23 | k8s-tew deploy --parallel --wait
24 |
25 | destroy:
26 | -multipass delete $(NAME) -p
27 |
28 | create:
29 | echo $(CLOUD_INIT) | multipass launch -n $(NAME) -c 4 -m 8G -d 40G --cloud-init - 20.04
30 |
31 | start:
32 | multipass start $(NAME)
33 |
34 | stop:
35 | multipass stop $(NAME)
36 |
37 | info:
38 | multipass info $(NAME)
39 |
40 | reboot: halt up
41 |
42 | dashboard:
43 | k8s-tew dashboard -o
44 |
45 | open-web-browser-all:
46 | k8s-tew open-web-browser --all
47 |
48 | shell:
49 | multipass shell $(NAME)
50 |
51 | forward-80:
52 | sudo socat -d -v TCP-LISTEN:80,fork TCP:$(IP):80
53 |
54 | forward-443:
55 | sudo socat -d -v TCP-LISTEN:443,fork TCP:$(IP):443
56 |
57 | k9s:
58 | KUBECONFIG=assets/etc/k8s-tew/k8s/kubeconfig/admin.kubeconfig k9s
59 |
--------------------------------------------------------------------------------
/setup/ubuntu-multi-node/Makefile:
--------------------------------------------------------------------------------
1 | .EXPORT_ALL_VARIABLES:
2 |
3 | VAGRANT_VAGRANTFILE=../../Vagrantfile
4 | OS=ubuntu
5 | IP_PREFIX=192.168.120
6 | MULTI_NODE=true
7 |
8 | run: destroy up deploy
9 |
10 | deploy:
11 | k8s-tew initialize -f
12 | k8s-tew configure --controller-virtual-ip=$(IP_PREFIX).20
13 | k8s-tew configure --controller-virtual-ip-interface=eth1
14 | k8s-tew configure --worker-virtual-ip=$(IP_PREFIX).10
15 | k8s-tew configure --worker-virtual-ip-interface=eth1
16 | k8s-tew configure --public-network=$(IP_PREFIX).0/24
17 | k8s-tew configure --resolv-conf=/run/systemd/resolve/resolv.conf
18 | k8s-tew configure --metallb-addresses=$(IP_PREFIX).200/32
19 | k8s-tew node-add -n controller00 -i $(IP_PREFIX).200 -l controller
20 | k8s-tew node-add -n controller01 -i $(IP_PREFIX).201 -l controller
21 | k8s-tew node-add -n controller02 -i $(IP_PREFIX).202 -l controller
22 | k8s-tew node-add -n worker00 -i $(IP_PREFIX).100 -l worker,storage
23 | k8s-tew node-add -n worker01 -i $(IP_PREFIX).101 -l worker,storage
24 | k8s-tew generate
25 | k8s-tew deploy
26 |
27 | destroy:
28 | vagrant destroy -f
29 |
30 | up:
31 | vagrant up
32 |
33 | recreate: destroy up save-clean
34 |
35 | save-clean:
36 | vagrant halt
37 | vagrant snapshot save controller00 clean
38 | vagrant snapshot save controller01 clean
39 | vagrant snapshot save controller02 clean
40 | vagrant snapshot save worker00 clean
41 | vagrant snapshot save worker01 clean
42 | vagrant up --parallel
43 |
44 | restore-clean:
45 | vagrant halt -f
46 | vagrant snapshot restore controller00 clean
47 | vagrant snapshot restore controller01 clean
48 | vagrant snapshot restore controller02 clean
49 | vagrant snapshot restore worker00 clean
50 | vagrant snapshot restore worker01 clean
51 | vagrant up --parallel
52 |
53 | halt:
54 | vagrant halt
55 |
56 | dashboard:
57 | k8s-tew dashboard kubernetes -o
58 |
59 | ssh-controller00:
60 | vagrant ssh controller00
61 |
62 | ssh-controller01:
63 | vagrant ssh controller01
64 |
65 | ssh-controller02:
66 | vagrant ssh controller02
67 |
68 | ssh-worker00:
69 | vagrant ssh worker00
70 |
71 | ssh-worker01:
72 | vagrant ssh worker01
73 |
74 | forward-80:
75 | sudo socat -d -v TCP-LISTEN:80,fork TCP:$(IP_PREFIX).20:80
76 |
77 | forward-443:
78 | sudo socat -d -v TCP-LISTEN:443,fork TCP:$(IP_PREFIX).20:443
79 |
--------------------------------------------------------------------------------
/setup/ubuntu-single-node/Makefile:
--------------------------------------------------------------------------------
1 | .EXPORT_ALL_VARIABLES:
2 |
3 | VAGRANT_VAGRANTFILE=../../Vagrantfile
4 | OS=ubuntu
5 | IP_PREFIX=192.168.110
6 |
7 | run: destroy up deploy
8 |
9 | deploy:
10 | k8s-tew initialize -f
11 | k8s-tew configure --public-network=$(IP_PREFIX).0/24
12 | k8s-tew configure --resolv-conf=/run/systemd/resolve/resolv.conf
13 | k8s-tew configure --metallb-addresses=$(IP_PREFIX).50/32
14 | k8s-tew node-add -n single-node -i $(IP_PREFIX).50 -x 0 -l controller,worker,storage
15 | k8s-tew generate
16 | k8s-tew deploy
17 |
18 | destroy:
19 | vagrant destroy -f
20 |
21 | up:
22 | vagrant up
23 |
24 | halt:
25 | vagrant halt
26 |
27 | dashboard:
28 | k8s-tew dashboard kubernetes -o
29 |
30 | ssh:
31 | vagrant ssh
32 |
33 | forward-80:
34 | sudo socat -d -v TCP-LISTEN:80,fork TCP:$(IP_PREFIX).50:80
35 |
36 | forward-443:
37 | sudo socat -d -v TCP-LISTEN:443,fork TCP:$(IP_PREFIX).50:443
38 |
39 | forward-minio-30800:
40 | sudo socat -d -v TCP-LISTEN:30800,fork TCP:$(IP_PREFIX).50:30800
41 |
42 | forward-grafana-30900:
43 | sudo socat -d -v TCP-LISTEN:30900,fork TCP:$(IP_PREFIX).50:30900
44 |
45 | forward-dashboard-32443:
46 | sudo socat -d -v TCP-LISTEN:32443,fork TCP:$(IP_PREFIX).50:32443
47 |
48 | forward-ceph-manager-30700:
49 | sudo socat -d -v TCP-LISTEN:30700,fork TCP:$(IP_PREFIX).50:30700
50 |
51 | forward-kibana-30980:
52 | sudo socat -d -v TCP-LISTEN:30980,fork TCP:$(IP_PREFIX).50:30980
53 |
54 | forward-cerebro-30990:
55 | sudo socat -d -v TCP-LISTEN:30990,fork TCP:$(IP_PREFIX).50:30990
56 |
--------------------------------------------------------------------------------