Skip to content

Commit

Permalink
Merge pull request #6 from VioletCranberry/refactoring
Browse files Browse the repository at this point in the history
Refactor error handling and testing, optimize
  • Loading branch information
VioletCranberry committed Nov 10, 2023
2 parents 97d194e + 4fdb24d commit 899a548
Show file tree
Hide file tree
Showing 22 changed files with 1,821 additions and 637 deletions.
22 changes: 10 additions & 12 deletions .github/workflows/test.yaml → .github/workflows/lint_test.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
---
name: Test

name: Test and Lint
permissions:
contents: read

on:
pull_request:
types:
Expand All @@ -14,25 +12,25 @@ on:
branches:
- master
- main

paths:
- '**.go'
jobs:
test:
name: Lint/Test
name: lint/test
runs-on: "ubuntu-latest"
steps:
- uses: actions/checkout@v3
name: Checkout repository

- uses: actions/setup-go@v3
- uses: actions/checkout@v4
name: Checkout repository
- uses: actions/setup-go@v4
name: Setup go environment
with:
go-version: "1.19"
go-version: "1.20"

- name: Run tests
run: go test -v ./...
- uses: golangci/golangci-lint-action@v3
name: Run golangci-lint
with:
version: "latest"
args: --timeout=2m --verbose

- name: Run GO tests
run: go test -v ./...
62 changes: 24 additions & 38 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -1,54 +1,40 @@
---
name: Release

permissions:
contents: write
packages: write

on:
release:
types:
- published

jobs:

release:
name: release binary
name: release binaries
runs-on: "ubuntu-latest"
strategy:
matrix:
goos:
- linux
- darwin
- windows
goarch:
- amd64
- arm64
exclude:
- goos: windows
goarch: ""
steps:
- uses: actions/checkout@v3
name: Checkout repository

- uses: wangyoucao577/go-release-action@v1
name: Release go binaries
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
goversion: "https://go.dev/dl/go1.19.8.linux-amd64.tar.gz"
goos: ${{ matrix.goos }}
goarch: ${{ matrix.goarch }}
binary_name: "kubectl-node_ssm"
build_flags: -v
extra_files: LICENSE README.md
sha256sum: TRUE

update:
needs: release
name: update krew-index
runs-on: "ubuntu-latest"
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
name: Checkout repository
- uses: actions/setup-go@v4
name: Setup go environment
with:
go-version: "1.20"
- name: Install GoReleaser
run: |
curl -sfL https://goreleaser.com/static/run | bash
- uses: rajatjindal/krew-release-bot@v0.0.46
name: Update new version in krew-index
- name: Build libraries
run: |
./build.sh
- name: Upload release assets
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
LATEST_RELEASE_TAG: ${{ env.LATEST_RELEASE_TAG }}
run: |
gh release upload "$LATEST_RELEASE_TAG" \
./dist/kubectl-node_ssm-"$LATEST_RELEASE_TAG"-*.tar.gz
gh release upload "$LATEST_RELEASE_TAG" \
./dist/kubectl-node_ssm-"$LATEST_RELEASE_TAG"-*.zip
gh release upload "$LATEST_RELEASE_TAG" \
./dist/kubectl-node_ssm-"$LATEST_RELEASE_TAG"-*.sha256
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
kubectl-node_ssm
.DS_Store
.idea
.idea
dist/
kubectl-node_ssm
24 changes: 24 additions & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
version: 1
project_name: kubectl-node_ssm
dist: ./dist

builds:
- env:
- CGO_ENABLED=0
goos:
- linux
- windows
- darwin
goarch:
- amd64
- arm64

archives:
- format: tar.gz
name_template: "{{ .ProjectName }}-v{{ .Version }}-{{ .Os }}-{{ .Arch }}"
files:
- LICENSE
- README.md
format_overrides:
- goos: windows
format: zip
69 changes: 34 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
# kubectl-node-ssm

### Description:
## Description

`node-ssm` is a dead simple `kubectl` plugin that allows direct connections to AWS EKS cluster Systems Manager managed nodes relying on local AWS CLI and session-manager-plugin installed. Provided EKS node name is automatically resolved to instance ID.
`node-ssm` is a straightforward `kubectl` plugin designed for establishing direct connections to EKS cluster nodes managed by AWS Systems Manager. It operates by utilizing the locally installed AWS CLI and session-manager-plugin. The plugin simplifies the process by automatically converting the provided EKS node name into its corresponding instance ID.

### Usage:
Using current `kubectl` context:
### Install with Krew plugin manager

```shell
# see https://krew.sigs.k8s.io/
kubectl krew update
kubectl krew install node-ssm
```

### Usage

```shell
❯ kubectl get nodes --no-headers | head -n 1
ip-10-10-10-10.ec2.internal Ready <none> 8d v1.22.17-eks-48e63af
❯ kubectl node-ssm start-session --target ip-10-10-10-10.ec2.internal
Expand All @@ -14,56 +23,46 @@ Starting session with SessionId: <username>@<domain>-0480532656ed795d8
sh-4.2$
```

All global `kubectl` options are supported, for example:
```
All global global command-line flags listed in `kubectl options` are supported, for example:

```shell
❯ kubectl config current-context
<my-current-context>
❯ kubectl get nodes --context <my-another-context> --no-headers | head -n 1
ip-20-20-20-20.ec2.internal Ready <none> 4d19h v1.22.17-eks-48e63af
k node-ssm start-session --context <my-another-context> --target ip-20-20-20-20.ec2.internal
kubectl node-ssm start-session --context <my-another-context> --target ip-20-20-20-20.ec2.internal

Starting session with SessionId: <username>@<domain>-0dd10b4b84087dff4
sh-4.2$
```

SSM `start-session` [parameters](https://docs.aws.amazon.com/cli/latest/reference/ssm/start-session.html) can be set with optional `--session-params` flag:
```

```shell
kubectl node-ssm start-session --target ip-30-30-30-30.ec2.internal --session-params '--reason=test' --session-params '--debug'
2023-04-10 00:54:45,509 - MainThread - awscli.clidriver - DEBUG - CLI version: aws-cli/2.11.0 Python/3.11.2 Darwin/22.3.0 source/arm64
2023-04-10 00:54:45,509 - MainThread - awscli.clidriver - DEBUG - Arguments entered to CLI: ['ssm', 'start-session', '--target', 'i-057750d42936e468a', '--reason=test', '--debug']
...
```

### Build and install manually:
```
❯ go build -o kubectl-node_ssm
❯ cp kubectl-node_ssm /usr/local/bin
❯ kubectl plugin list | grep node_ssm
/usr/local/bin/kubectl-node_ssm
❯ kubectl node-ssm --help
start AWS systems manager session using local AWS CLI and session-manager-plugin
### Build and install manually

Usage:
start-session [flags]
Flags:
...
```shell
go build -o kubectl-node_ssm \
&& sudo cp kubectl-node_ssm /usr/local/bin \
&& kubectl plugin list | grep node_ssm \
&& kubectl node-ssm --help
# rm -f /usr/local/bin/node_ssm
```

### Requirements:
### Requirements

1. [AWS CLI installed](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html)
2. [AWS session-manager-plugin installed](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html)
3. [AWS Systems Manager Session Manager configured](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-getting-started.html)
4. IAM Permissions to perform `ec2:DescribeInstances`
1. Installed [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html) and [AWS session-manager-plugin](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html)
2. Configured [AWS Systems Manager Session Manager](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-getting-started.html)
3. IAM Permissions to perform `ec2:DescribeInstances`

### Logic:
### Logic

1. Return [kubeconfig](https://docs.aws.amazon.com/eks/latest/userguide/create-kubeconfig.html) REST client from current context.
2. Parse [Config.Host](https://pkg.go.dev/k8s.io/client-go@v0.26.3/rest#Config.Host) for `AWS_REGION`.
3. Parse [[]ExecEnvVar](https://pkg.go.dev/k8s.io/client-go/tools/clientcmd/api#ExecConfig.Env) for `AWS_PROFILE`.
If `AWS_PROFILE` was not found we assume [AWS session](https://pkg.go.dev/github.com/aws/aws-sdk-go/aws/session) is to be created with a Shared Configuration file.
4. Resolve EKS node `private-dns-name` to instance ID using [AWS describe-instances](https://pkg.go.dev/github.com/aws/aws-sdk-go@v1.44.239/service/ec2#EC2.DescribeInstances).
5. Build `aws ssm start-session --target <instance id>` [command](https://pkg.go.dev/os/exec#Command) with specified parameters.
6. Specify environment of the command with `AWS_REGION` and optional `AWS_PROFILE` variables.
7. Start the command and wait for it to complete.
1. Extract `AWS_REGION` and `AWS_PROFILE` from [Config.Host](https://pkg.go.dev/k8s.io/client-go@v0.26.3/rest#Config.Host) and [[]ExecEnvVar](https://pkg.go.dev/k8s.io/client-go/tools/clientcmd/api#ExecConfig.Env) array of current kubeconfig context.
2. Create [AWS session](https://pkg.go.dev/github.com/aws/aws-sdk-go/aws/session) and resolve EKS node `private-dns-name` to instance ID using [(*EC2) DescribeInstances](https://docs.aws.amazon.com/sdk-for-go/api/service/ec2/#EC2.DescribeInstances) API operation.
3. Build `aws ssm start-session --target <instance id>` [command](https://pkg.go.dev/os/exec#Command) with specified parameters and environment and execute it.
20 changes: 20 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/bin/bash
set -euxo pipefail

checksum_command=""
# common on macOS
if command -v shasum >/dev/null 2>&1; then
checksum_command="shasum -a 256"
# common on Linux
elif command -v sha256sum >/dev/null 2>&1; then
checksum_command="sha256sum"
fi

goreleaser release --timeout=1m --auto-snapshot --fail-fast --clean --skip=publish
cd dist || exit 1

for archive in *.tar.gz *.zip; do
if [ -f "$archive" ]; then
$checksum_command "$archive" > "$archive.sha256"
fi
done
64 changes: 0 additions & 64 deletions cmd/newSession.go

This file was deleted.

36 changes: 36 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package cmd

import (
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericclioptions"
)

type cliOptions struct {
flags *genericclioptions.ConfigFlags
genericclioptions.IOStreams
}

func newCliOptions(streams genericclioptions.IOStreams) *cliOptions {
return &cliOptions{
flags: genericclioptions.NewConfigFlags(true),
IOStreams: streams,
}
}

func NewRootCmd(streams genericclioptions.IOStreams) *cobra.Command {
var target string
var params []string
cliOptions := newCliOptions(streams)

rootCmd := &cobra.Command{Use: "node-ssm", SilenceUsage: true}
rootCmd.PersistentFlags().StringVar(&target, "target", "", "EKS node name (private-dns-name)")
_ = rootCmd.MarkPersistentFlagRequired("target")

rootCmd.PersistentFlags().StringSliceVar(&params, "session-params",
[]string{}, "SSM session parameters")

cliOptions.flags.AddFlags(rootCmd.Flags())
rootCmd.AddCommand(newStartSessionCmd(cliOptions, &target, &params))

return rootCmd
}
Loading

0 comments on commit 899a548

Please sign in to comment.