Skip to content

Commit 85c300b

Browse files
committed
Deploy kind cluster with golang
1 parent 4cf41af commit 85c300b

File tree

9 files changed

+137
-34
lines changed

9 files changed

+137
-34
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,14 @@ jobs:
8989
steps:
9090
- uses: actions/checkout@v4
9191
- uses: ./.github/actions/setup-go-for-project
92+
# TODO(marun) Encapsulate execution with nix and monitoring configuration in a custom action
93+
- uses: ./.github/actions/install-nix
94+
- run: nix develop --command echo "dependencies installed"
9295
- name: Run e2e tests
9396
shell: bash
9497
run: bash -x ./scripts/tests.e2e.kube.sh
9598
env:
99+
TMPNET_CHECK_MONITORING: true
96100
PROMETHEUS_USERNAME: ${{ secrets.PROMETHEUS_ID || '' }}
97101
PROMETHEUS_PASSWORD: ${{ secrets.PROMETHEUS_PASSWORD || '' }}
98102
LOKI_USERNAME: ${{ secrets.LOKI_ID || '' }}

bin/tmpnetctl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ cd "${AVALANCHE_PATH}"
1010
if [[ ! -f ./build/tmpnetctl ]]; then
1111
./scripts/build_tmpnetctl.sh
1212
fi
13-
./build/tmpnetctl
13+
./build/tmpnetctl "${@}"

scripts/tests.e2e.bootstrap_monitor.sh

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,6 @@ if ! [[ "$0" =~ scripts/tests.e2e.bootstrap_monitor.sh ]]; then
99
exit 255
1010
fi
1111

12-
CMD=kind-with-registry.sh
13-
14-
if ! command -v "${CMD}" &> /dev/null; then
15-
echo "kind-with-registry.sh not found, have you run 'nix develop'?"
16-
echo "To install nix: https://github.com/DeterminateSystems/nix-installer?tab=readme-ov-file#install-nix"
17-
exit 1
18-
fi
19-
20-
"${CMD}"
12+
./bin/tmpnetctl start-kind-cluster
2113

2214
KUBECONFIG="$HOME/.kube/config" ./bin/ginkgo -v ./tests/fixture/bootstrapmonitor/e2e

scripts/tests.e2e.kube.sh

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,14 @@ set -euo pipefail
66

77
# TODO(marun)
88
# - Support testing against a remote cluster
9-
# - Convert to golang to simplify reuse
10-
# - Make idempotent to simplify development and debugging
9+
# - Convert deloyment of collectors to golang to simplify reuse
1110

1211
if ! [[ "$0" =~ scripts/tests.e2e.kube.sh ]]; then
1312
echo "must be run from repository root"
1413
exit 255
1514
fi
1615

17-
./scripts/ensure_kube_cluster.sh
16+
./bin/tmpnetctl start-kind-cluster
1817

1918
KUBE_CONTEXT="${KUBE_CONTEXT:-kind-kind}"
2019
# TODO(marun) Make the namespace configurable

tests/e2e/c/dynamic_fees.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ var _ = e2e.DescribeCChain("[Dynamic Fees]", func() {
6060
env := e2e.GetEnv(tc)
6161
publicNetwork := env.GetNetwork()
6262

63-
privateNetwork := tmpnet.NewDefaultNetwork("avalanchego-e2e-dynamic-fees")
63+
privateNetwork := tmpnet.NewDefaultNetwork(tc.Log(), "avalanchego-e2e-dynamic-fees")
6464
// Copy over the defaults from the normal test suite to include settings
6565
// like the upgrade config.
6666
privateNetwork.DefaultFlags = tmpnet.FlagsMap{}

tests/fixture/tmpnet/cmd/main.go

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"path/filepath"
1313

1414
"github.com/spf13/cobra"
15+
"github.com/spf13/pflag"
1516
"go.uber.org/zap"
1617

1718
"github.com/ava-labs/avalanchego/tests"
@@ -59,10 +60,12 @@ func main() {
5960
runtime string
6061
avalancheGoPath string
6162
pluginDir string
62-
kubeconfig string
63-
namespace string
64-
imageName string
65-
nodeCount uint8
63+
// TODO(marun) Update to camel-case?
64+
kubeconfig string
65+
kubeContext string
66+
namespace string
67+
imageName string
68+
nodeCount uint8
6669
)
6770
startNetworkCmd := &cobra.Command{
6871
Use: "start-network",
@@ -151,7 +154,7 @@ func main() {
151154
tmpnet.GetEnvWithDefault(tmpnet.AvalancheGoPluginDirEnvName, os.ExpandEnv("$HOME/.avalanchego/plugins")),
152155
"[optional] the dir containing VM plugins",
153156
)
154-
startNetworkCmd.PersistentFlags().StringVar(&kubeconfig, "kubeconfig", os.Getenv("KUBECONFIG"), "The path to a kubernetes configuration file for the target cluster")
157+
SetKubeConfigFlags(startNetworkCmd.PersistentFlags(), &kubeconfig, &kubeContext)
155158
startNetworkCmd.PersistentFlags().StringVar(&namespace, "namespace", "tmpnet", "The namespace in the target cluster to create nodes in")
156159
startNetworkCmd.PersistentFlags().StringVar(&imageName, "image-name", "avaplatform/avalanchego:latest", "The name of the docker image to use for creating nodes")
157160
startNetworkCmd.PersistentFlags().Uint8Var(&nodeCount, "node-count", tmpnet.DefaultNodeCount, "Number of nodes the network should initially consist of")
@@ -268,9 +271,40 @@ func main() {
268271
)
269272
rootCmd.AddCommand(checkLogsCmd)
270273

274+
startKindClusterCmd := &cobra.Command{
275+
Use: "start-kind-cluster",
276+
Short: "Starts a local kind cluster with an integrated registry",
277+
RunE: func(*cobra.Command, []string) error {
278+
ctx, cancel := context.WithTimeout(context.Background(), tmpnet.DefaultNetworkTimeout)
279+
defer cancel()
280+
log, err := tests.LoggerForFormat("", rawLogFormat)
281+
if err != nil {
282+
return err
283+
}
284+
return tmpnet.StartKindCluster(ctx, log, kubeconfig, kubeContext)
285+
},
286+
}
287+
SetKubeConfigFlags(startKindClusterCmd.PersistentFlags(), &kubeconfig, &kubeContext)
288+
rootCmd.AddCommand(startKindClusterCmd)
289+
271290
if err := rootCmd.Execute(); err != nil {
272291
fmt.Fprintf(os.Stderr, "tmpnetctl failed: %v\n", err)
273292
os.Exit(1)
274293
}
275294
os.Exit(0)
276295
}
296+
297+
func SetKubeConfigFlags(flagSet *pflag.FlagSet, kubeConfig *string, kubeContext *string) {
298+
flagSet.StringVar(
299+
kubeConfig,
300+
"kubeconfig",
301+
os.Getenv("KUBECONFIG"),
302+
"The path to a kubernetes configuration file for the target cluster",
303+
)
304+
flagSet.StringVar(
305+
kubeContext,
306+
"",
307+
"",
308+
"The path to a kubernetes configuration file for the target cluster",
309+
)
310+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
2+
// See the file LICENSE for licensing terms.
3+
4+
package tmpnet
5+
6+
import (
7+
"context"
8+
"fmt"
9+
"os"
10+
"os/exec"
11+
12+
"go.uber.org/zap"
13+
14+
"github.com/ava-labs/avalanchego/utils/logging"
15+
)
16+
17+
// TODO(marun) Maybe differentiate between configuration and endpoint errors
18+
func CheckClusterRunning(configPath string, configContext string) error {
19+
clientset, err := GetClientset(configPath, configContext)
20+
if err != nil {
21+
return err
22+
}
23+
// Check if the configured context can reach a cluster endpoint
24+
_, err = clientset.Discovery().ServerVersion()
25+
return err
26+
}
27+
28+
func StartKindCluster(ctx context.Context, log logging.Logger, configPath string, configContext string) error {
29+
err := CheckClusterRunning(configPath, configContext)
30+
if err == nil {
31+
log.Info("kubernetes cluster already running",
32+
zap.String("config", configPath),
33+
zap.String("context", configContext),
34+
)
35+
return nil
36+
}
37+
38+
log.Debug("kubernetes cluster not running",
39+
zap.String("config", configPath),
40+
zap.String("context", configContext),
41+
zap.Error(err),
42+
)
43+
44+
// Start a new kind cluster
45+
ctx, cancel := context.WithTimeout(ctx, DefaultNetworkTimeout)
46+
defer cancel()
47+
cmd := exec.CommandContext(ctx, "kind-with-registry.sh")
48+
cmd.Stdout = os.Stdout
49+
cmd.Stderr = os.Stderr
50+
if err := cmd.Run(); err != nil {
51+
return fmt.Errorf("failed to run kind-with-registry.sh: %w", err)
52+
}
53+
return nil
54+
}

tests/fixture/tmpnet/kube.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"k8s.io/apimachinery/pkg/util/intstr"
2020
"k8s.io/apimachinery/pkg/util/wait"
2121
"k8s.io/client-go/kubernetes"
22+
"k8s.io/client-go/tools/clientcmd"
2223
"k8s.io/client-go/tools/portforward"
2324
"k8s.io/client-go/transport/spdy"
2425
"k8s.io/utils/pointer"
@@ -344,3 +345,34 @@ func enableLocalForwardForPod(
344345
}
345346
return forwardedPorts[0].Local, stopChan, nil
346347
}
348+
349+
func GetClientConfig(path string, context string) (*restclient.Config, error) {
350+
if len(context) == 0 {
351+
// BuildConfigFromFlags uses the default context and will attempt to use an
352+
// in-cluster config with the path as a fallback
353+
return clientcmd.BuildConfigFromFlags("", path)
354+
}
355+
356+
config := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
357+
&clientcmd.ClientConfigLoadingRules{
358+
ExplicitPath: path,
359+
},
360+
&clientcmd.ConfigOverrides{
361+
CurrentContext: context,
362+
},
363+
)
364+
return config.ClientConfig()
365+
}
366+
367+
func GetClientset(path string, context string) (*kubernetes.Clientset, error) {
368+
clientConfig, err := GetClientConfig(path, context)
369+
if err != nil {
370+
return nil, fmt.Errorf("failed to get client config: %w", err)
371+
}
372+
373+
clientset, err := kubernetes.NewForConfig(clientConfig)
374+
if err != nil {
375+
return nil, fmt.Errorf("failed to create clientset: %w", err)
376+
}
377+
return clientset, nil
378+
}

tests/fixture/tmpnet/node_pod.go

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import (
1919
"k8s.io/apimachinery/pkg/types"
2020
"k8s.io/apimachinery/pkg/util/wait"
2121
"k8s.io/client-go/kubernetes"
22-
"k8s.io/client-go/tools/clientcmd"
2322

2423
"github.com/ava-labs/avalanchego/config"
2524

@@ -375,23 +374,12 @@ func (p *NodePod) IsHealthy(ctx context.Context) (bool, error) {
375374

376375
func (p *NodePod) getKubeconfig() (*restclient.Config, error) {
377376
// TODO(marun) inClusterConfig requires an empty path. How best to ensure this?
378-
kubeconfigPath := os.Getenv("KUBECONFIG")
379-
if kubeconfigPath == "" {
380-
kubeconfigPath = p.runtimeConfig().Kubeconfig
381-
}
382-
return clientcmd.BuildConfigFromFlags("", kubeconfigPath)
377+
return GetClientConfig(p.runtimeConfig().Kubeconfig, "")
383378
}
384379

385380
func (p *NodePod) getClientset() (*kubernetes.Clientset, error) {
386-
kubeconfig, err := p.getKubeconfig()
387-
if err != nil {
388-
return nil, err
389-
}
390-
clientset, err := kubernetes.NewForConfig(kubeconfig)
391-
if err != nil {
392-
return nil, fmt.Errorf("failed to create clientset: %w", err)
393-
}
394-
return clientset, nil
381+
// TODO(marun) Support supplying a context
382+
return GetClientset(p.runtimeConfig().Kubeconfig, "")
395383
}
396384

397385
func (p *NodePod) runtimeConfig() *KubeRuntimeConfig {

0 commit comments

Comments
 (0)