diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..3dbbcf3 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,25 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/bin +**/charts +**/docker-compose* +**/compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 0000000..58a68ab --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,44 @@ +name: "Security Scan" + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + schedule: + - cron: '0 0 * * 0' # Run weekly + +jobs: + security: + name: Security Scan + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Run Gosec Security Scanner + uses: securego/gosec@master + with: + args: ./... + + analyze: + name: CodeQL Analysis + runs-on: ubuntu-latest + permissions: + security-events: write + actions: read + contents: read + + steps: + - uses: actions/checkout@v3 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: go + + - name: Build Go Code + run: go build ./... + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..da28136 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,46 @@ +name: Test + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + test: + name: Run Tests + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: '1.20' + check-latest: true + + - name: Install dependencies + run: go mod download + + - name: Run tests + run: go test -v ./... + + - name: Build binary + run: go build -v ./... + + - name: Run golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + version: latest + args: --timeout=5m + + - name: Run basic checks + run: | + go vet ./... + if [ -z "$(go fmt ./...)" ]; then + echo "Code is formatted correctly" + else + echo "Code is not formatted correctly" + exit 1 + fi \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..39775ab --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +#build stage +FROM golang:alpine AS builder +RUN apk add --no-cache git +WORKDIR /go/src/app +COPY . . +RUN go get -d -v ./... +RUN go build -o /go/bin/app -v ./... + +#final stage +FROM alpine:latest +RUN apk --no-cache add ca-certificates +COPY --from=builder /go/bin/app /app +ENTRYPOINT /app +LABEL Name=nodesafedrainer Version=0.0.1 +EXPOSE 3000 diff --git a/README.md b/README.md index ff01105..c6efa24 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,12 @@ +# Node Safe Drainer + +[![Tests](https://github.com/angelabad/node-safe-drainer/actions/workflows/test.yml/badge.svg)](https://github.com/angelabad/node-safe-drainer/actions/workflows/test.yml) +[![Security Scan](https://github.com/angelabad/node-safe-drainer/actions/workflows/security.yml/badge.svg)](https://github.com/angelabad/node-safe-drainer/actions/workflows/security.yml) + # Warning This repository contains experimental code. Use it at your own risk! -# Node Safe Drainer - Tool to safely drain Kubernetes nodes. ## The problem @@ -40,38 +43,46 @@ Download binary from [relases](https://github.com/angelabad/node-safe-drainer/re ### Source code +```bash +git clone git@github.com:angelabad/node-safe-drainer.git +cd node-safe-dainer +go build ``` -$ git clone git@github.com:angelabad/node-safe-drainer.git -$ cd node-safe-dainer -$ go build + +### Docker (recommended) + +```powershell +# Build and run tests +.\build-and-test.ps1 + +# Run the application (mounts your kubeconfig) +.\run-in-docker.ps1 -ExtraArgs "--kubeconfig /root/.kube/config --max-jobs 5" ``` +See [README_DOCKER.md](README_DOCKER.md) for Docker setup details. + ## Usage -``` +```shell usage: ./node-safe-drainer [OPTIONS] Simple tool for safe draining nodes, rolling out deployments without downtime. Options: - -all-nodes - cordon and empty all nodes (use with caution) - -kubeconfig string - abslute path to the kubeconfig file (default "/home/angel/.kube/config") - -max-jobs int - max concurrent rollouts. (default 10) - -timeout duration - deployment rollouts timeout. (default 20m0s) + -all-nodes cordon and empty all nodes (use with caution) + -kubeconfig str absolute path to the kubeconfig file (default "/home/angel/.kube/config") + -max-jobs int max concurrent rollouts (default 10) + -timeout duration deployment rollouts timeout (default 20m0s) ``` ### For all nodes on cluster -``` -$ ./node-safe-drainer -all-nodes +```shell +./node-safe-drainer -all-nodes ``` ### For custom nodes -``` -$ ./node-safe-drainer k3d-k3s-default-server-0,k3d-k3s-default-agent-0 +```shell +./node-safe-drainer k3d-k3s-default-server-0,k3d-k3s-default-agent-0 ``` diff --git a/README_DOCKER.md b/README_DOCKER.md new file mode 100644 index 0000000..e3284bc --- /dev/null +++ b/README_DOCKER.md @@ -0,0 +1,19 @@ +This repository includes a small Docker setup so you can run tests and the application without installing Go locally. + +Quick steps (Windows PowerShell / pwsh): + +1. Build the Docker image and run tests: + +```powershell +.\build-and-test.ps1 +``` + +2. Run the application and mount your kubeconfig (read-only): + +```powershell +.\run-in-docker.ps1 -ExtraArgs "--kubeconfig /root/.kube/config --max-jobs 5" +``` + +Notes: +- The Docker image uses `golang:1.20-alpine` and will download modules defined in `go.mod`. +- If Docker isn't available on your machine, you can still install Go from https://go.dev/dl/ and run locally. diff --git a/build-and-test.ps1 b/build-and-test.ps1 new file mode 100644 index 0000000..193d627 --- /dev/null +++ b/build-and-test.ps1 @@ -0,0 +1,9 @@ +param( + [string]$ImageName = "node-safe-drainer:test" +) + +Write-Host "Building Docker image $ImageName..." +docker build -t $ImageName . + +Write-Host "Running tests inside container..." +docker run --rm $ImageName diff --git a/compose.debug.yaml b/compose.debug.yaml new file mode 100644 index 0000000..cf6df83 --- /dev/null +++ b/compose.debug.yaml @@ -0,0 +1,8 @@ +services: + nodesafedrainer: + image: nodesafedrainer + build: + context: . + dockerfile: ./Dockerfile + ports: + - 3000:3000 diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..cf6df83 --- /dev/null +++ b/compose.yaml @@ -0,0 +1,8 @@ +services: + nodesafedrainer: + image: nodesafedrainer + build: + context: . + dockerfile: ./Dockerfile + ports: + - 3000:3000 diff --git a/run-in-docker.ps1 b/run-in-docker.ps1 new file mode 100644 index 0000000..fa95258 --- /dev/null +++ b/run-in-docker.ps1 @@ -0,0 +1,17 @@ +param( + [string]$ImageName = "node-safe-drainer:test", + [string]$KubeconfigPath = "$env:USERPROFILE\.kube\config", + [string]$ExtraArgs = "" +) + +if (-not (Test-Path $KubeconfigPath)) { + Write-Host "Kubeconfig not found at $KubeconfigPath. You can pass a path with -KubeconfigPath" +} + +$mount = "" +if (Test-Path $KubeconfigPath) { + $mount = "-v ${KubeconfigPath}:/root/.kube/config:ro" +} + +Write-Host "Running container (will use kubeconfig: $KubeconfigPath)..." +docker run --rm $mount -it $ImageName sh -c "go run main.go $ExtraArgs"