Skip to content

Commit 2f74ca8

Browse files
authored
Merge pull request #14 from Azure/jose/ci-init
ci: Create initial CI workflow
2 parents 26740a6 + e5dacb8 commit 2f74ca8

File tree

4 files changed

+319
-0
lines changed

4 files changed

+319
-0
lines changed

.github/workflows/kubectl-az.yml

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
name: Microsoft Azure CLI kubectl plugin CI
2+
env:
3+
GO_VERSION: 1.17 # TODO: Update
4+
AZURE_PREFIX: kubectl-az-ci
5+
concurrency:
6+
# Only one workflow can run at a time unless
7+
# we create a new AKS cluster per github_ref (branch)
8+
group: kubectl-az-ci
9+
10+
on:
11+
pull_request:
12+
push:
13+
branches:
14+
- main
15+
16+
jobs:
17+
build:
18+
name: Build kubectl-az
19+
runs-on: ubuntu-latest
20+
strategy:
21+
matrix:
22+
os: [ linux, darwin, windows ]
23+
arch: [ amd64, arm64 ]
24+
exclude:
25+
- os: windows
26+
arch: arm64
27+
steps:
28+
- name: Set up Go
29+
uses: actions/setup-go@v4
30+
with:
31+
go-version: ${{ env.GO_VERSION }}
32+
check-latest: true
33+
- name: Cache Go
34+
uses: actions/cache@v3
35+
with:
36+
path: |
37+
~/.cache/go-build
38+
~/go/pkg/mod
39+
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
40+
restore-keys: |
41+
${{ runner.os }}-go-
42+
- name: Check out code
43+
uses: actions/checkout@v3
44+
- name: Build and generate tarball
45+
run: |
46+
target=kubectl-az-${{ matrix.os }}-${{ matrix.arch }}
47+
48+
make $target
49+
50+
binary_name=kubectl-az
51+
if [ ${{ matrix.os }} = "windows" ]; then
52+
binary_name=kubectl-az.exe
53+
fi
54+
55+
# Prepare binary as artifact, it will be used by other jobs
56+
mv $target $binary_name
57+
tar --sort=name --owner=root:0 --group=root:0 \
58+
-czf ${target}.tar.gz \
59+
$binary_name LICENSE
60+
- name: Add kubectl-az-${{ matrix.os }}-${{ matrix.arch }}.tar.gz as artifact
61+
uses: actions/upload-artifact@v3
62+
with:
63+
name: kubectl-az-${{ matrix.os }}-${{ matrix.arch }}
64+
path: kubectl-az-${{ matrix.os }}-${{ matrix.arch }}.tar.gz
65+
66+
unit-tests:
67+
name: Run unit tests
68+
runs-on: ubuntu-latest
69+
steps:
70+
- name: Set up Go
71+
uses: actions/setup-go@v4
72+
with:
73+
go-version: ${{ env.GO_VERSION }}
74+
check-latest: true
75+
- name: Check out code
76+
uses: actions/checkout@v3
77+
- name: Cache Go
78+
uses: actions/cache@v3
79+
with:
80+
path: |
81+
~/.cache/go-build
82+
~/go/pkg/mod
83+
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
84+
restore-keys: |
85+
${{ runner.os }}-go-
86+
- name: Run tests
87+
run: make unit-test
88+
89+
create-aks-cluster:
90+
name: Create AKS cluster
91+
needs: [ build, unit-tests ]
92+
runs-on: ubuntu-latest
93+
strategy:
94+
fail-fast: false
95+
matrix:
96+
arch: [ amd64 ]
97+
steps:
98+
- name: Login to Azure
99+
uses: azure/login@v1
100+
with:
101+
creds: ${{ secrets.AZURE_CREDENTIALS }}
102+
- name: Create AKS cluster ${{ env.AZURE_PREFIX }}-${{ matrix.arch }}-cluster
103+
shell: bash
104+
run: |
105+
az aks create \
106+
--resource-group ${{ env.AZURE_PREFIX }}-rg \
107+
--name ${{ env.AZURE_PREFIX }}-${{ matrix.arch }}-cluster \
108+
--node-count 1 \
109+
--generate-ssh-keys
110+
111+
delete-aks-cluster:
112+
name: Delete AKS cluster
113+
if: always()
114+
needs: [ integration-tests ]
115+
runs-on: ubuntu-latest
116+
strategy:
117+
fail-fast: false
118+
matrix:
119+
arch: [ amd64 ]
120+
steps:
121+
- name: Login to Azure
122+
uses: azure/login@v1
123+
with:
124+
creds: ${{ secrets.AZURE_CREDENTIALS }}
125+
- name: Delete AKS cluster ${{ env.AZURE_PREFIX }}-${{ matrix.arch }}-cluster
126+
shell: bash
127+
run: |
128+
az aks delete \
129+
--resource-group ${{ env.AZURE_PREFIX }}-rg \
130+
--name ${{ env.AZURE_PREFIX }}-${{ matrix.arch }}-cluster \
131+
--yes
132+
133+
integration-tests:
134+
name: Run integration tests
135+
needs: [ build, unit-tests , create-aks-cluster ]
136+
runs-on: ${{ matrix.os }}
137+
strategy:
138+
fail-fast: false
139+
# 'run-command' is not supported in parallel for a given node
140+
# (don't want to create a new cluster for each OS)
141+
max-parallel: 1
142+
matrix:
143+
os: [ ubuntu-latest, macOS-latest, windows-latest ]
144+
arch: [ amd64 ] # TODO: Support ARM
145+
steps:
146+
- name: Set up Go
147+
uses: actions/setup-go@v4
148+
with:
149+
go-version: ${{ env.GO_VERSION }}
150+
check-latest: true
151+
- name: Check out code
152+
uses: actions/checkout@v3
153+
- name: Set environment variables
154+
shell: bash
155+
run: |
156+
case ${{ matrix.os }} in
157+
ubuntu-latest)
158+
echo "os=linux" >> $GITHUB_ENV
159+
;;
160+
macOS-latest)
161+
echo "os=darwin" >> $GITHUB_ENV
162+
;;
163+
windows-latest)
164+
echo "os=windows" >> $GITHUB_ENV
165+
;;
166+
*)
167+
echo "Not supported OS: ${{ matrix.os }}"
168+
exit 1
169+
;;
170+
esac
171+
- name: Get kubectl-az from artifact
172+
uses: actions/download-artifact@v3
173+
with:
174+
name: kubectl-az-${{ env.os }}-${{ matrix.arch }}
175+
- name: Prepare kubectl-az binary
176+
shell: bash
177+
run: |
178+
tar zxvf kubectl-az-${{ env.os }}-${{ matrix.arch }}.tar.gz
179+
chmod +x kubectl-az
180+
ls -la
181+
- name: Login to Azure
182+
uses: azure/login@v1
183+
with:
184+
creds: ${{ secrets.AZURE_CREDENTIALS }}
185+
- name: Set AKS cluster context
186+
uses: azure/aks-set-context@v3
187+
with:
188+
cluster-name: ${{ env.AZURE_PREFIX }}-${{ matrix.arch }}-cluster
189+
resource-group: ${{ env.AZURE_PREFIX }}-rg
190+
admin: false
191+
- if: matrix.os != 'ubuntu-latest'
192+
# kubectl is already installed in Linux runners
193+
uses: azure/setup-kubectl@v3
194+
- name: Run integration tests
195+
shell: bash
196+
run: |
197+
make integration-test -o kubectl-az

Makefile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ LDFLAGS := "-X github.com/Azure/kubectl-az/cmd.version=$(VERSION) -extldflags '-
1616

1717
.DEFAULT_GOAL := kubectl-az
1818

19+
# Build
1920
KUBECTL_AZ_TARGETS = \
2021
kubectl-az-linux-amd64 \
2122
kubectl-az-linux-arm64 \
@@ -48,11 +49,24 @@ kubectl-az-%: phony_explicit
4849
-o kubectl-az-$${GOOS}-$${GOARCH} \
4950
github.com/Azure/kubectl-az
5051

52+
# Install
5153
.PHONY: install
5254
install: kubectl-az
5355
mkdir -p ~/.local/bin/
5456
cp kubectl-az ~/.local/bin/
5557

58+
# Run unit tests
59+
.PHONY: unit-test
60+
unit-test:
61+
go test -v ./...
62+
63+
# Run integration tests
64+
.PHONY: integration-test
65+
integration-test: kubectl-az
66+
KUBECTL_AZ="$(shell pwd)/kubectl-az" \
67+
go test -v ./test/integration/... -integration
68+
69+
# Clean
5670
.PHONY: clean
5771
clean:
5872
rm -f kubectl-az

test/integration/helpers.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
package integration
5+
6+
import (
7+
"bytes"
8+
"context"
9+
"os"
10+
"os/exec"
11+
"testing"
12+
13+
"github.com/kinvolk/inspektor-gadget/pkg/k8sutil"
14+
"github.com/stretchr/testify/require"
15+
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
16+
"k8s.io/cli-runtime/pkg/genericclioptions"
17+
)
18+
19+
func runKubectlAZ(t *testing.T, args ...string) string {
20+
t.Helper()
21+
22+
cmd := exec.Command(os.Getenv("KUBECTL_AZ"), append(nodeFlag(t), args...)...)
23+
stdout := &bytes.Buffer{}
24+
stderr := &bytes.Buffer{}
25+
cmd.Stdout = stdout
26+
cmd.Stderr = stderr
27+
28+
t.Logf("Running command: %s", cmd.String())
29+
err := cmd.Run()
30+
require.Empty(t, stderr.String(), "stderr.String() = %v, want empty", stderr.String())
31+
require.Nil(t, err, "cmd.Run() = %v, want nil", err)
32+
t.Logf("Command output: \n%s", stdout.String())
33+
34+
return stdout.String()
35+
}
36+
37+
func nodeFlag(t *testing.T) []string {
38+
t.Helper()
39+
40+
clientset, err := k8sutil.NewClientsetFromConfigFlags(genericclioptions.NewConfigFlags(false))
41+
require.Nil(t, err, "k8sutil.NewClientsetFromConfigFlags() = %v, want nil", err)
42+
43+
nodes, err := clientset.CoreV1().Nodes().List(context.TODO(), metaV1.ListOptions{})
44+
require.Nil(t, err, "clientset.CoreV1().Nodes().List() = %v, want nil", err)
45+
require.NotEmpty(t, nodes.Items, "nodes.Items = %v, want not empty", nodes.Items)
46+
47+
return []string{"--node", nodes.Items[0].Name}
48+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
package integration
5+
6+
import (
7+
"flag"
8+
"fmt"
9+
"os"
10+
"regexp"
11+
"strings"
12+
"testing"
13+
14+
"github.com/stretchr/testify/require"
15+
)
16+
17+
var integration = flag.Bool("integration", false, "run integration tests")
18+
19+
func TestMain(m *testing.M) {
20+
flag.Parse()
21+
if !*integration {
22+
fmt.Println("Skipping integration test.")
23+
os.Exit(0)
24+
}
25+
26+
if os.Getenv("KUBECTL_AZ") == "" {
27+
fmt.Fprintf(os.Stderr, "KUBECTL_AZ environment variable must be set to the path of the kubectl-az binary\n")
28+
os.Exit(1)
29+
}
30+
31+
fmt.Println("Running integration tests")
32+
m.Run()
33+
}
34+
35+
func TestCheckAPIServerConnectivity(t *testing.T) {
36+
out := runKubectlAZ(t, "check-apiserver-connectivity")
37+
require.Contains(t, out, "Connectivity check: succeeded")
38+
}
39+
40+
func TestRunCommand(t *testing.T) {
41+
// test stdout
42+
out := runKubectlAZ(t, "run-command", "echo test")
43+
stdout, stderr := parseRunCommand(t, out)
44+
require.Equal(t, stdout, "test", "parseRunCommand() = %v, want %v", stdout, "test")
45+
require.Empty(t, stderr, "parseRunCommand() = %v, want %v", stderr, "")
46+
47+
// test stderr
48+
out = runKubectlAZ(t, "run-command", "echo test >&2")
49+
stdout, stderr = parseRunCommand(t, out)
50+
require.Empty(t, stdout, "parseRunCommand() = %v, want %v", stdout, "")
51+
require.Equal(t, stderr, "test", "parseRunCommand() = %v, want %v", stderr, "test")
52+
}
53+
54+
func parseRunCommand(t *testing.T, out string) (string, string) {
55+
split := regexp.MustCompile(`(\[(stdout|stderr)\])`).Split(out, -1)
56+
require.Len(t, split, 3, "couldn't parse response message:\n%s", out)
57+
stdOutput := strings.TrimSpace(split[1])
58+
stdError := strings.TrimSpace(split[2])
59+
return stdOutput, stdError
60+
}

0 commit comments

Comments
 (0)