Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/codeql.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
runs-on: "['default']"
language: "['go']"
go-check: true
go-version: "['1.23']"
go-version: "['1.25']"
node-check: false
# node-version : "['node']"
# fail-fast: false
Expand Down
103 changes: 103 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
name: Release binaries

on:
push:
tags:
- "v*"

permissions:
contents: write

jobs:
build-and-release:
name: Build and attach binaries
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
include:
- goos: linux
goarch: amd64
- goos: linux
goarch: arm64
- goos: darwin
goarch: amd64
- goos: darwin
goarch: arm64
- goos: windows
goarch: amd64
- goos: windows
goarch: arm64

steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.25.x"

- name: Verify build
run: |
go version
go mod download
go build ./...

- name: Build binary
env:
CGO_ENABLED: "0"
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
run: |
BIN_NAME=cloudctl
OUT_DIR=dist
mkdir -p "${OUT_DIR}"
if [ "${GOOS}" = "windows" ]; then
OUT_BIN="${OUT_DIR}/${BIN_NAME}_${GOOS}_${GOARCH}.exe"
else
OUT_BIN="${OUT_DIR}/${BIN_NAME}_${GOOS}_${GOARCH}"
fi
echo "Building ${OUT_BIN}"
go build -trimpath -ldflags="-s -w" -o "${OUT_BIN}" ./cmd

- name: Package artifact
run: |
set -euo pipefail
BIN_NAME=cloudctl
OUT_DIR=dist
ARCHIVE_DIR=pkg
mkdir -p "${ARCHIVE_DIR}"

FILE_BASE="${BIN_NAME}_${{ matrix.goos }}_${{ matrix.goarch }}"
if [ "${{ matrix.goos }}" = "windows" ]; then
BIN_PATH="${OUT_DIR}/${FILE_BASE}.exe"
ARCHIVE_PATH="${ARCHIVE_DIR}/${FILE_BASE}.zip"
(cd "${OUT_DIR}" && zip -9 "../${ARCHIVE_PATH}" "${FILE_BASE}.exe")
else
BIN_PATH="${OUT_DIR}/${FILE_BASE}"
ARCHIVE_PATH="${ARCHIVE_DIR}/${FILE_BASE}.tar.gz"
(cd "${OUT_DIR}" && tar -czf "../${ARCHIVE_PATH}" "${FILE_BASE}")
fi

echo "ARCHIVE_PATH=${ARCHIVE_PATH}" >> $GITHUB_ENV

- name: Create GitHub Release (if not exists)
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name }}
name: ${{ github.ref_name }}
draft: false
prerelease: ${{ contains(github.ref_name, '-rc') || contains(github.ref_name, '-beta') || contains(github.ref_name, '-alpha') }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
continue-on-error: true

- name: Upload artifact to release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name }}
files: ${{ env.ARCHIVE_PATH }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
67 changes: 67 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: Tests

on:
pull_request:
push:
branches: [ main ]

jobs:
unit:
name: Unit tests
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.25'

- name: Show Go env
run: |
go version
go env

- name: Tidy modules
run: make tidy

- name: Run unit tests
run: make test

e2e:
name: E2E tests (k3d)
needs: unit
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.25'

- name: Install kubectl
uses: azure/setup-kubectl@v4
with:
version: 'v1.29.0' # any modern kubectl compatible with k3d's default

- name: Install k3d
run: |
curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash
k3d version

- name: Build binary
run: make build

- name: Run E2E tests
env:
# Optional: customize e2e settings via Makefile variables if needed
# E2E_CLUSTER_NAME: cloudctl-e2e
# E2E_TAGS: e2e
# E2E_KUBECONFIG: ${{ github.workspace }}/e2e/e2e-kubeconfig
# E2E_BIN: ${{ github.workspace }}/bin/cloudctl
# Increase verbosity by adding GOFLAGS=-v or similar if desired
GOFLAGS: ""
run: make e2e
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/bin/
/e2e/e2e-kubeconfig
/e2e/cloudctl
133 changes: 133 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# Project variables
BIN ?= cloudctl
PKG ?= github.com/cloudoperators/cloudctl
CMD_PKG ?= .
BUILD_DIR ?= bin

# Versioning (overridable)
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev)
GIT_COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo unknown)
BUILD_DATE ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ)

# Go options
GO ?= go
GOFLAGS ?=
TAGS ?=
LDFLAGS ?= -X '$(PKG)/cmd.Version=$(VERSION)' -X '$(PKG)/cmd.GitCommit=$(GIT_COMMIT)' -X '$(PKG)/cmd.BuildDate=$(BUILD_DATE)'
GCFLAGS ?=
ASMFLAGS ?=
RACE ?=

# E2E options
E2E_CLUSTER_NAME ?= cloudctl-e2e
E2E_KUBECONFIG ?= $(CURDIR)/e2e/e2e-kubeconfig
E2E_PKG ?= ./e2e
E2E_TAGS ?= e2e
# Use absolute path so tests can find the binary regardless of working directory
E2E_BIN ?= $(CURDIR)/$(BUILD_DIR)/$(BIN)

# Extra args
ARGS ?=

# Derived
BUILD_FLAGS := $(if $(RACE),-race,) $(GOFLAGS) -tags '$(TAGS)' -ldflags "$(LDFLAGS)" -gcflags '$(GCFLAGS)' -asmflags '$(ASMFLAGS)'

.PHONY: all build install run test cover cover-html fmt vet tidy clean version print-vars \
e2e-up e2e-down e2e-build e2e-test e2e

all: build

build:
@mkdir -p $(BUILD_DIR)
$(GO) build $(BUILD_FLAGS) -o $(BUILD_DIR)/$(BIN) $(CMD_PKG)

install:
$(GO) install $(BUILD_FLAGS) $(CMD_PKG)

run:
$(GO) run $(BUILD_FLAGS) $(CMD_PKG) $(ARGS)

test:
$(GO) test $(GOFLAGS) -tags '$(TAGS)' $(if $(RACE),-race,) ./...

cover:
$(GO) test $(GOFLAGS) -tags '$(TAGS)' -coverprofile=coverage.out ./...
@echo "Coverage summary:"
$(GO) tool cover -func=coverage.out

cover-html: cover
$(GO) tool cover -html=coverage.out -o coverage.html
@echo "Open coverage.html in your browser."

fmt:
$(GO) fmt ./...

fmt-check:
@echo "Checking formatting with gofmt and gofumpt..."
@set -e; \
out1="$$(gofmt -l .)"; \
out2="$$( $(GO) run mvdan.cc/gofumpt@latest -l .)"; \
files=""; \
if [ -n "$$out1" ]; then files="$$files\n$$out1"; fi; \
if [ -n "$$out2" ]; then files="$$files\n$$out2"; fi; \
if [ -n "$$files" ]; then \
echo "The following files need formatting:"; \
printf "%b\n" "$$files" | sed '/^$$/d' | sort -u; \
echo "Run: make fmt"; \
exit 1; \
fi

vet:
$(GO) vet ./...

tidy:
$(GO) mod tidy

clean:
@rm -rf $(BUILD_DIR) coverage.out coverage.html ./bin/cloudctl ./e2e/cloudctl ./e2e/e2e-kubeconfig

version:
@echo "Version: $(VERSION)"
@echo "Git commit: $(GIT_COMMIT)"
@echo "Build date: $(BUILD_DATE)"

print-vars:
@echo "BIN=$(BIN)"
@echo "PKG=$(PKG)"
@echo "CMD_PKG=$(CMD_PKG)"
@echo "BUILD_DIR=$(BUILD_DIR)"
@echo "VERSION=$(VERSION)"
@echo "GIT_COMMIT=$(GIT_COMMIT)"
@echo "BUILD_DATE=$(BUILD_DATE)"
@echo "GOFLAGS=$(GOFLAGS)"
@echo "TAGS=$(TAGS)"
@echo "LDFLAGS=$(LDFLAGS)"
@echo "RACE=$(RACE)"
@echo "E2E_CLUSTER_NAME=$(E2E_CLUSTER_NAME)"
@echo "E2E_KUBECONFIG=$(E2E_KUBECONFIG)"
@echo "E2E_BIN=$(E2E_BIN)"
@echo "E2E_PKG=$(E2E_PKG)"
@echo "E2E_TAGS=$(E2E_TAGS)"

# --- E2E ---

e2e-up:
@./e2e/k3d-up.sh "$(E2E_CLUSTER_NAME)" "$(E2E_KUBECONFIG)"

e2e-down:
@./e2e/k3d-down.sh "$(E2E_CLUSTER_NAME)"

e2e-build: build
@echo "Using E2E binary: $(E2E_BIN)"

# Ensure kubeconfig exists before running tests; if not, bring the cluster up and write it.
e2e-test: e2e-build
@if [ ! -f "$(E2E_KUBECONFIG)" ]; then \
echo "Kubeconfig $(E2E_KUBECONFIG) not found; creating via k3d-up..."; \
./e2e/k3d-up.sh "$(E2E_CLUSTER_NAME)" "$(E2E_KUBECONFIG)"; \
fi
@echo "Running e2e tests against kubeconfig: $(E2E_KUBECONFIG)"
E2E_KUBECONFIG="$(E2E_KUBECONFIG)" E2E_BIN="$(E2E_BIN)" $(GO) test -v -tags '$(E2E_TAGS)' $(E2E_PKG)

# Convenience: bring up cluster, run tests, then tear down.
e2e: e2e-up e2e-test e2e-down
29 changes: 22 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,40 @@

# cloudctl

## About this project

Unified Kubernetes cli for the cloud.
Unified Kubernetes CLI for the cloud.

```
cloudctl is a command line interface that helps:

1) Fetch and merge kubeconfigs from central Greenhouse cluster
1) Fetch and merge kubeconfigs from the central Greenhouse cluster into your local kubeconfig
2) Sync contexts and credentials for seamless kubectl usage
3) Inspect the Kubernetes version of a target cluster
4) Print the cloudctl version and build information

Examples:
- Merge/refresh kubeconfigs from Greenhouse:
cloudctl sync

- Show Kubernetes version for a specific context:
cloudctl cluster-version --context my-cluster

- Show cloudctl version:
cloudctl version

Usage:
cloudctl [command]

Available Commands:
completion Generate the autocompletion script for the specified shell
help Help about any command
sync Fetches remote kubeconfigs from Greenhouse cluster and merges them into your local config
cluster-version Prints the cluster version of the context in kubeconfig
completion Generate the autocompletion script for the specified shell
help Help about any command
sync Fetches kubeconfigs of remote clusters from Greenhouse cluster and merges them into your local config
version Print the cloudctl version information

Flags:
-h, --help help for cloudctl

Use "cloudctl [command] --help" for more information about a command.
```

## Requirements and Setup
Expand Down
5 changes: 0 additions & 5 deletions cmd/cluster-version.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ var (
)

func runClusterVersion(cmd *cobra.Command, args []string) error {

cfg, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
if err != nil {
return fmt.Errorf("failed to build kubeconfig: %w", err)
Expand Down Expand Up @@ -83,10 +82,6 @@ func hasAuth(cfg *rest.Config) bool {
if len(cfg.TLSClientConfig.CertData) > 0 || cfg.TLSClientConfig.CertFile != "" {
return true
}
if cfg.ExecProvider != nil {
return true
}

if cfg.ExecProvider != nil {
return true
}
Expand Down
Loading
Loading