diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9736f00..d60c9f8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,11 +39,20 @@ jobs: cache: true - name: Install FUSE run: sudo apt-get update && sudo apt-get install -y fuse3 + - name: Install krew + run: | + cd "$(mktemp -d)" + curl -fsSLO "https://github.com/kubernetes-sigs/krew/releases/latest/download/krew-linux_amd64.tar.gz" + tar zxf krew-linux_amd64.tar.gz + ./krew-linux_amd64 install krew + echo "${HOME}/.krew/bin" >> "$GITHUB_PATH" - uses: helm/kind-action@ef37e7f390d99f746eb8b610417061a60e82a6cc # v1.14.0, kind v0.31.0, kubectl v1.35.0 with: cluster_name: vifal-test node_image: kindest/node:v1.35.0@sha256:452d707d4862f52530247495d180205e029056831160e22870e37e3f6c1ac31f - run: make e2e-test-shell + env: + VIFAL_E2E_KREW: "1" # With race detector and no attr caching. - run: FUSE_TIMEOUT=5 make e2e-test-shell-nocache env: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d17a582..057db85 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,8 +7,58 @@ on: permissions: {} jobs: - release: + create-release: runs-on: ubuntu-24.04 + permissions: + contents: write + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + - name: Create draft release + env: + GH_TOKEN: ${{ github.token }} + run: gh release create "${GITHUB_REF_NAME}" --draft --generate-notes + + build: + runs-on: ubuntu-24.04 + needs: create-release + permissions: + contents: write + strategy: + matrix: + include: + - goos: linux + goarch: amd64 + - goos: linux + goarch: arm64 + - goos: darwin + goarch: amd64 + - goos: darwin + goarch: arm64 + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 + with: + go-version-file: go.mod + + - name: Build and package + env: + GOOS: ${{ matrix.goos }} + GOARCH: ${{ matrix.goarch }} + CGO_ENABLED: "0" + run: make release-archive VERSION="${GITHUB_REF_NAME}" GOOS="${GOOS}" GOARCH="${GOARCH}" + + - name: Upload asset + env: + GH_TOKEN: ${{ github.token }} + run: gh release upload "${GITHUB_REF_NAME}" "kubectl-vifal_${GITHUB_REF_NAME}_${{ matrix.goos }}_${{ matrix.goarch }}.tar.gz" + + publish: + runs-on: ubuntu-24.04 + needs: build permissions: contents: write steps: @@ -24,7 +74,19 @@ jobs: GOPROXY: proxy.golang.org run: go list -m "github.com/machine424/vifal@${GITHUB_REF_NAME}" - - name: Create GitHub release + - name: Publish release env: GH_TOKEN: ${{ github.token }} - run: gh release create "${GITHUB_REF_NAME}" + run: gh release edit "${GITHUB_REF_NAME}" --draft=false + + update-krew-index: + runs-on: ubuntu-24.04 + needs: publish + permissions: + contents: read + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + - name: Update krew-index + uses: rajatjindal/krew-release-bot@c970b8a8f6dbc2f2285a26e3ae160903b87002c3 # v0.0.51 diff --git a/.gitignore b/.gitignore index d898006..4fb3bab 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ # Build output vifal +kubectl-vifal +kubectl-vifal_*.tar.gz # macOS .DS_Store diff --git a/.krew.yaml b/.krew.yaml new file mode 100644 index 0000000..bfa7696 --- /dev/null +++ b/.krew.yaml @@ -0,0 +1,48 @@ +apiVersion: krew.googlecontainertools.github.com/v1alpha2 +kind: Plugin +metadata: + name: vifal +spec: + version: {{ .TagName }} + homepage: https://github.com/machine424/vifal + platforms: + - selector: + matchLabels: + os: linux + arch: amd64 + {{addURIAndSha "https://github.com/machine424/vifal/releases/download/{{ .TagName }}/kubectl-vifal_{{ .TagName }}_linux_amd64.tar.gz" .TagName }} + bin: kubectl-vifal + - selector: + matchLabels: + os: linux + arch: arm64 + {{addURIAndSha "https://github.com/machine424/vifal/releases/download/{{ .TagName }}/kubectl-vifal_{{ .TagName }}_linux_arm64.tar.gz" .TagName }} + bin: kubectl-vifal + - selector: + matchLabels: + os: darwin + arch: amd64 + {{addURIAndSha "https://github.com/machine424/vifal/releases/download/{{ .TagName }}/kubectl-vifal_{{ .TagName }}_darwin_amd64.tar.gz" .TagName }} + bin: kubectl-vifal + - selector: + matchLabels: + os: darwin + arch: arm64 + {{addURIAndSha "https://github.com/machine424/vifal/releases/download/{{ .TagName }}/kubectl-vifal_{{ .TagName }}_darwin_arm64.tar.gz" .TagName }} + bin: kubectl-vifal + shortDescription: exposing and unifying Kubernetes container filesystems locally + description: | + FUSE mount that exposes and unifies Kubernetes container filesystems locally. + Browse namespaces, pods, and containers as directories, read files, and diff + configs across pods using standard shell tools. + caveats: | + Requires FUSE: + - Linux: install fuse3 + - macOS: install macFUSE (https://github.com/macfuse/macfuse) + + Containers must have sh, stat, find, and dd available. + + Usage: + kubectl vifal /mnt/my-cluster + ls /mnt/my-cluster//// + kubectl vifal unmount /mnt/my-cluster diff --git a/Makefile b/Makefile index 34bb53f..b588ce2 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,10 @@ -BINARY = vifal +BINARY ?= vifal GOLANGCI_LINT_VERSION = v2.12.1 GO_LICENSES_VERSION = v2.0.1 GO_BUILD_FLAGS ?= GO_LDFLAGS ?= -s -w TIMEOUT_CMD = timeout --foreground -.PHONY: build build-debug build-race clean test unit-test e2e-test e2e-test-go e2e-test-shell e2e-test-shell-debug e2e-test-shell-nocache lint shellcheck licenses-check +.PHONY: build build-debug build-race release-archive clean test unit-test e2e-test e2e-test-go e2e-test-shell e2e-test-shell-debug e2e-test-shell-nocache lint shellcheck licenses-check build: go build $(GO_BUILD_FLAGS) -ldflags="$(GO_LDFLAGS)" -o $(BINARY) . @@ -15,6 +15,10 @@ build-debug: build-race: $(MAKE) --no-print-directory build GO_BUILD_FLAGS=-race GO_LDFLAGS= +release-archive: + $(MAKE) --no-print-directory build BINARY=kubectl-vifal + tar czf "kubectl-vifal_$(VERSION)_$(GOOS)_$(GOARCH).tar.gz" kubectl-vifal LICENSE LICENSES/ + test: unit-test e2e-test unit-test: diff --git a/README.md b/README.md index 7f94901..bff1289 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,14 @@ Works on Linux and macOS. - Linux: install `fuse3`. - macOS: install [macFUSE](https://github.com/macfuse/macfuse/wiki/Getting-Started). The mount appears as a network volume, so your terminal may need [access to network volumes](https://github.com/macfuse/macfuse/issues/690#issuecomment-1527424231). +Via [Krew](https://krew.sigs.k8s.io/) (kubectl plugin manager): + +```bash +$ kubectl krew install vifal +``` + +Via `go install`: + ```bash $ go install github.com/machine424/vifal@latest ``` diff --git a/e2e/krew-test-manifest.yaml b/e2e/krew-test-manifest.yaml new file mode 100644 index 0000000..9c93600 --- /dev/null +++ b/e2e/krew-test-manifest.yaml @@ -0,0 +1,18 @@ +# Static manifest for testing krew install locally (with --archive). +# The test script replaces SHA256SUM with the real checksum at runtime. +apiVersion: krew.googlecontainertools.github.com/v1alpha2 +kind: Plugin +metadata: + name: vifal +spec: + version: v0.0.0-ci + platforms: + - selector: + matchLabels: + os: linux + arch: amd64 + uri: https://example.com/placeholder.tar.gz + sha256: SHA256SUM + bin: kubectl-vifal + shortDescription: test + description: test diff --git a/e2e/tests/krew.sh b/e2e/tests/krew.sh new file mode 100644 index 0000000..31b327e --- /dev/null +++ b/e2e/tests/krew.sh @@ -0,0 +1,56 @@ +# Test vifal as a kubectl plugin installed via krew: install, mount, read, unmount, uninstall. +# Skipped unless VIFAL_E2E_KREW=1 (set in CI after installing krew). + +if [ "${VIFAL_E2E_KREW:-}" != "1" ]; then + log_step "SKIP: VIFAL_E2E_KREW not set" + return 0 +fi + +NAMESPACE="vifal-krew" +TEST_DIR=$(mktemp -d /tmp/vifal-krew-XXXXXX) +MOUNT="$TEST_DIR/mount" +LOGS="$TEST_DIR" + +setup() { + kubectl delete namespace "$NAMESPACE" --ignore-not-found + mkdir -p "$MOUNT" + kubectl create namespace "$NAMESPACE" + kubectl run nginx --image="$IMAGE" -n "$NAMESPACE" --overrides="$FAST_TERM" --command -- sleep infinity + kubectl wait --for=condition=Ready pod/nginx -n "$NAMESPACE" --timeout="${KUBECTL_TIMEOUT}s" +} + +teardown() { + dump_vifal_logs "$?" + kubectl vifal unmount "$MOUNT" 2>/dev/null || true + kubectl krew uninstall vifal 2>/dev/null || true + rm -rf "$TEST_DIR" + kubectl delete namespace "$NAMESPACE" --ignore-not-found --wait=false +} + +trap teardown EXIT +setup + +log_step "build and install via krew" +MANIFEST="$TEST_DIR/manifest.yaml" +ARCHIVE="kubectl-vifal_v0.0.0-ci_linux_amd64.tar.gz" +make -s release-archive VERSION=v0.0.0-ci GOOS=linux GOARCH=amd64 +# krew validates sha256 even with --archive, patch the placeholder in the manifest. +sed "s/SHA256SUM/$(sha256sum "$ARCHIVE" | awk '{print $1}')/" "$DIR/krew-test-manifest.yaml" > "$MANIFEST" +kubectl krew install --manifest="$MANIFEST" --archive="$ARCHIVE" +rm -f "$ARCHIVE" kubectl-vifal + +log_step "mount" +kubectl vifal --fsname "$VIFAL_SHELL_TEST" --attr-ttl "$CACHE_TTL" "$MOUNT" 2>"$LOGS/vifal.log" & +VIFAL_PID=$! +wait_for_fuse_sync "$MOUNT/$NAMESPACE/nginx/nginx" + +log_step "read file" +diff <(fuse cat "$MOUNT/$NAMESPACE/nginx/nginx/etc/hostname") <(kubectl exec nginx -n "$NAMESPACE" -c nginx -- cat /etc/hostname) + +log_step "unmount" +kubectl vifal unmount "$MOUNT" +wait_process_dead "$VIFAL_PID" +check_clean_unmount "$MOUNT" + +log_step "krew uninstall" +kubectl krew uninstall vifal