From aafcad57d329cc9c083b292b65eed861f80b9869 Mon Sep 17 00:00:00 2001 From: Aaron Siddhartha Mondal Date: Wed, 17 Apr 2024 05:21:07 +0200 Subject: [PATCH] Introduce the `native` CLI (#851) This command line tool overhauls the initial setup for Kubernetes deployments. The new tool gives us more information when something doesn't work out as intended and brings the the cluster setup considerably closer to what one would expect in a production grade system. The nativelink image and worker containers are now fully built in the cluster via Tekton Pipelines. Rebuilds may be triggered with curl requests instead of the old `nix run .#xxx.copyTo` workflow. This makes the setup more generic and provides clearer pointers on how to bring the system into continuously updating production workflows. The `native` tool is technically fully self-contained. The examples still make use of some local paths, but it's now possible to set up the cluster and deploy NativeLink in it without cloning the nativelink repository. This requires slightly modified `01_operations.sh` scripts which we'll add as a new example. --- .../vocabularies/TraceMachina/accept.txt | 2 + .github/workflows/lre.yaml | 3 +- .gitignore | 1 + .golangci.yaml | 21 + Pulumi.yaml | 11 + deployment-examples/chromium/00_infra.sh | 1 - deployment-examples/chromium/01_operations.sh | 33 +- deployment-examples/chromium/README.md | 11 +- deployment-examples/kubernetes/00_infra.sh | 131 ------ .../kubernetes/01_operations.sh | 51 ++- deployment-examples/kubernetes/README.md | 11 +- deployment-examples/kubernetes/gateway.yaml | 24 - flake.nix | 15 +- native-cli/clusters/localcluster.go | 230 ++++++++++ native-cli/cmd/down.go | 34 ++ native-cli/cmd/root.go | 35 ++ native-cli/cmd/up.go | 106 +++++ native-cli/components/cilium.go | 195 ++++++++ native-cli/components/components.go | 42 ++ .../embedded/nativelink-gateways.yaml | 0 .../embedded/rebuild-nativelink.yaml | 259 +++++++++++ native-cli/components/embedded/trigger.yaml | 155 +++++++ native-cli/components/nativelink-gateways.go | 49 ++ native-cli/components/rebuild-nativelink.go | 86 ++++ native-cli/components/registry.go | 51 +++ native-cli/components/tekton.go | 123 +++++ native-cli/components/volumes.go | 80 ++++ native-cli/default.nix | 26 ++ native-cli/go.mod | 124 ++++++ native-cli/go.sum | 419 ++++++++++++++++++ native-cli/main.go | 9 + native-cli/programs/local.go | 108 +++++ tools/pre-commit-hooks.nix | 56 ++- 33 files changed, 2305 insertions(+), 197 deletions(-) create mode 100644 .golangci.yaml create mode 100644 Pulumi.yaml delete mode 120000 deployment-examples/chromium/00_infra.sh delete mode 100755 deployment-examples/kubernetes/00_infra.sh delete mode 100644 deployment-examples/kubernetes/gateway.yaml create mode 100644 native-cli/clusters/localcluster.go create mode 100644 native-cli/cmd/down.go create mode 100644 native-cli/cmd/root.go create mode 100644 native-cli/cmd/up.go create mode 100644 native-cli/components/cilium.go create mode 100644 native-cli/components/components.go rename deployment-examples/chromium/gateway.yaml => native-cli/components/embedded/nativelink-gateways.yaml (100%) create mode 100644 native-cli/components/embedded/rebuild-nativelink.yaml create mode 100644 native-cli/components/embedded/trigger.yaml create mode 100644 native-cli/components/nativelink-gateways.go create mode 100644 native-cli/components/rebuild-nativelink.go create mode 100644 native-cli/components/registry.go create mode 100644 native-cli/components/tekton.go create mode 100644 native-cli/components/volumes.go create mode 100644 native-cli/default.nix create mode 100644 native-cli/go.mod create mode 100644 native-cli/go.sum create mode 100644 native-cli/main.go create mode 100644 native-cli/programs/local.go diff --git a/.github/styles/config/vocabularies/TraceMachina/accept.txt b/.github/styles/config/vocabularies/TraceMachina/accept.txt index 16d5a5738..3377e9e33 100755 --- a/.github/styles/config/vocabularies/TraceMachina/accept.txt +++ b/.github/styles/config/vocabularies/TraceMachina/accept.txt @@ -27,3 +27,5 @@ rebase remoteable Chromium namespace +Pulumi +Tekton diff --git a/.github/workflows/lre.yaml b/.github/workflows/lre.yaml index 550bd692e..e8cfc8dcb 100644 --- a/.github/workflows/lre.yaml +++ b/.github/workflows/lre.yaml @@ -67,8 +67,7 @@ jobs: - name: Start Kubernetes cluster (Infra) run: > - nix develop --impure --command - bash -c "./deployment-examples/kubernetes/00_infra.sh" + nix run .#native up - name: Start Kubernetes cluster (Operations) run: > diff --git a/.gitignore b/.gitignore index 365e7a12b..ec1bac05a 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ result .bazelrc.user MODULE.bazel.lock trivy-results.sarif +Pulumi.dev.yaml diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 000000000..b1f578f35 --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,21 @@ +--- +linters: + enable-all: true + disable: + # Deprecated. + - nosnakecase + - interfacer + - exhaustivestruct + - ifshort + - deadcode + - varcheck + - golint + - maligned + - scopelint + - structcheck + + # Allow all packages for now. + - depguard + + # TODO(aaronmondal): Fix these at some point. + - exhaustruct diff --git a/Pulumi.yaml b/Pulumi.yaml new file mode 100644 index 000000000..a4c4a92e5 --- /dev/null +++ b/Pulumi.yaml @@ -0,0 +1,11 @@ +--- +name: nativelink +org: TraceMachina +runtime: go +description: The development cluster for NativeLink. +organization: + pulumi:tags: + company: "Trace Machina, Inc." +backend: + # Only intended to run locally. + url: file://~ diff --git a/deployment-examples/chromium/00_infra.sh b/deployment-examples/chromium/00_infra.sh deleted file mode 120000 index 6183bee37..000000000 --- a/deployment-examples/chromium/00_infra.sh +++ /dev/null @@ -1 +0,0 @@ -../kubernetes/00_infra.sh \ No newline at end of file diff --git a/deployment-examples/chromium/01_operations.sh b/deployment-examples/chromium/01_operations.sh index 6cd0b699f..7f0ce19be 100755 --- a/deployment-examples/chromium/01_operations.sh +++ b/deployment-examples/chromium/01_operations.sh @@ -7,16 +7,29 @@ set -xeuo pipefail SRC_ROOT=$(git rev-parse --show-toplevel) -kubectl apply -f ${SRC_ROOT}/deployment-examples/chromium/gateway.yaml +EVENTLISTENER=$(kubectl get gtw eventlistener -o=jsonpath='{.status.addresses[0].value}') # The image for the scheduler and CAS. -nix run .#image.copyTo \ - docker://localhost:5001/nativelink:local \ - -- \ - --dest-tls-verify=false +curl -v \ + -H 'content-Type: application/json' \ + -d '{ + "flakeOutput": "./src_root#image", + "imageTagOverride": "local" + }' \ + http://${EVENTLISTENER}:8080 -# Wrap it with nativelink to turn it into a worker. -nix run .#nativelink-worker-siso-chromium.copyTo \ - docker://localhost:5001/nativelink-worker-siso-chromium:local \ - -- \ - --dest-tls-verify=false +# Wrap it nativelink to turn it into a worker. +curl -v \ + -H 'content-Type: application/json' \ + -d '{ + "flakeOutput": "./src_root#nativelink-worker-siso-chromium", + "imageTagOverride": "local" + }' \ + http://${EVENTLISTENER}:8080 + +# Wait for the pipelines to finish. +kubectl wait \ + --for=condition=Succeeded \ + --timeout=30m \ + pipelinerun \ + -l tekton.dev/pipeline=rebuild-nativelink diff --git a/deployment-examples/chromium/README.md b/deployment-examples/chromium/README.md index a410f5af0..6b19b3182 100644 --- a/deployment-examples/chromium/README.md +++ b/deployment-examples/chromium/README.md @@ -19,9 +19,13 @@ In this example we're using `kind` to set up the cluster `cilium` to provide a First set up a local development cluster: ```bash -./00_infra.sh +native up ``` +> [!TIP] +> The `native up` command uses Pulumi under the hood. You can view and delete +> the stack with `pulumi stack` and `pulumi destroy`. + Next start a few standard deployments. This part also builds the remote execution containers and makes them available to the cluster: @@ -29,6 +33,11 @@ execution containers and makes them available to the cluster: ./01_operations.sh ``` +> [!TIP] +> The operations invoke cluster-internal Tekton Pipelines to build and push the +> `nativelink` and worker images. You can view the state of the pipelines with +> `tkn pr ls` and `tkn pr logs`/`tkn pr logs --follow`. + Finally, deploy NativeLink: ```bash diff --git a/deployment-examples/kubernetes/00_infra.sh b/deployment-examples/kubernetes/00_infra.sh deleted file mode 100755 index 7d5765df5..000000000 --- a/deployment-examples/kubernetes/00_infra.sh +++ /dev/null @@ -1,131 +0,0 @@ -# This script sets up a local development cluster. It's roughly equivalent to -# a managed K8s setup. - -# For ease of development and to save disk space we pipe a local container -# registry through to kind. -# -# See https://kind.sigs.k8s.io/docs/user/local-registry/. - -set -xeuo pipefail - -reg_name='kind-registry' -reg_port='5001' -if [ "$(docker inspect -f '{{.State.Running}}' "${reg_name}" 2>/dev/null || true)" != 'true' ]; then - docker run \ - -d --restart=always -p "127.0.0.1:${reg_port}:5000" --network bridge --name "${reg_name}" \ - registry:2 -fi - -# Start a basic cluster. We use cilium's CNI and eBPF kube-proxy replacement. -SRC_ROOT=$(git rev-parse --show-toplevel) - -cat < [!TIP] +> The `native up` command uses Pulumi under the hood. You can view and delete +> the stack with `pulumi stack` and `pulumi destroy`. + Next start a few standard deployments. This part also builds the remote execution containers and makes them available to the cluster: @@ -19,6 +23,11 @@ execution containers and makes them available to the cluster: ./01_operations.sh ``` +> [!TIP] +> The operations invoke cluster-internal Tekton Pipelines to build and push the +> `nativelink` and worker images. You can view the state of the pipelines with +> `tkn pr ls` and `tkn pr logs`/`tkn pr logs --follow`. + Finally, deploy NativeLink: ```bash diff --git a/deployment-examples/kubernetes/gateway.yaml b/deployment-examples/kubernetes/gateway.yaml deleted file mode 100644 index bc6bf5450..000000000 --- a/deployment-examples/kubernetes/gateway.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# TODO(aaronmondal): There should just be a single gateway. But that's currently -# bugged: https://github.com/cilium/cilium/issues/29099 ---- -apiVersion: gateway.networking.k8s.io/v1 -kind: Gateway -metadata: - name: cache -spec: - gatewayClassName: cilium - listeners: - - name: cache - protocol: HTTP - port: 50051 ---- -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: Gateway -metadata: - name: scheduler -spec: - gatewayClassName: cilium - listeners: - - name: scheduler - protocol: HTTP - port: 50052 diff --git a/flake.nix b/flake.nix index d435dee57..6de07ebf6 100644 --- a/flake.nix +++ b/flake.nix @@ -135,9 +135,7 @@ generate-toolchains = import ./tools/generate-toolchains.nix {inherit pkgs;}; - # inherit (nix2container.packages.${system}.nix2container) buildImage; - # rbe-autogen = import ./local-remote-execution/rbe-autogen.nix {inherit pkgs nativelink buildImage;}; - # createWorker = import ./tools/create-worker.nix {inherit pkgs nativelink buildImage;}; + native-cli = import ./native-cli/default.nix {inherit pkgs;}; inherit (nix2container.packages.${system}.nix2container) pullImage; inherit (nix2container.packages.${system}.nix2container) buildImage; @@ -165,9 +163,13 @@ type = "app"; program = "${nativelink}/bin/nativelink"; }; + native = { + type = "app"; + program = "${native-cli}/bin/native"; + }; }; packages = rec { - inherit publish-ghcr local-image-test nativelink nativelink-debug; + inherit publish-ghcr local-image-test nativelink nativelink-debug native-cli; default = nativelink; lre-cc = import ./local-remote-execution/lre-cc.nix {inherit pkgs buildImage;}; @@ -222,11 +224,16 @@ pkgs.vale pkgs.trivy pkgs.docker-client + pkgs.kind + pkgs.tektoncd-cli + (pkgs.pulumi.withPackages (ps: [ps.pulumi-language-go])) + pkgs.go # Additional tools from within our development environment. local-image-test generate-toolchains customClang + native-cli ] ++ maybeDarwinDeps; shellHook = '' diff --git a/native-cli/clusters/localcluster.go b/native-cli/clusters/localcluster.go new file mode 100644 index 000000000..0f8c888cd --- /dev/null +++ b/native-cli/clusters/localcluster.go @@ -0,0 +1,230 @@ +package clusters + +import ( + "bytes" + "context" + "errors" + "fmt" + "log" + "text/template" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/client" + git "github.com/go-git/go-git/v5" + "sigs.k8s.io/kind/pkg/cluster" +) + +var errKind = errors.New("kind error") + +// gitSrcRoot returns the absolute path to the root of the git repository where +// this function is invoked from. +func gitSrcRoot() string { + repo, err := git.PlainOpenWithOptions( + ".", + &git.PlainOpenOptions{DetectDotGit: true}, + ) + if err != nil { + log.Fatalf("Failed to open git repository: %v", err) + } + + worktree, err := repo.Worktree() + if err != nil { + log.Fatalf("Failed to get worktree: %v", err) + } + + return worktree.Filesystem.Root() +} + +// CreateLocalKindConfig creates a kind configuration with several tweaks for +// local development. +// +// 1. The Git repository where the cluster is created from is mounted at +// `/mnt/src_root` so that jobs running inside the cluster always use the +// latest sources which may have been modified locally. +// 2. The host's `/nix` store is mounted as readonly into the cluster nodes at +// `/nix`. This allows creating `PersistentVolumes` that make the hosts nix +// store available to e.g. pipelines running in the cluster. This way such +// pipelines don't need to build rebuild anything that was built on the host +// already. +// 3. Nodes are set up to be compatible with CubeFS. By default these configs +// are unused. However, if CubeFS is deployed to the cluster, the data in +// these volumes persists beyond cluster destruction. Combined with CubeFS's +// CSI driver this allows creating realistic PersistentVolumes on the fly and +// in combination with the S3 endpoints allows simulating S3 storage buckets. +// 4. Containerd in the nodes is patched to be compatible with a pass-through +// container registry. This allows creating a registry that attaches to both +// the hosts default "bridge" network and the cluster's "kind" network so +// that images copied to the host's local registry become available to the +// kind nodes as well. +func CreateLocalKindConfig() bytes.Buffer { + kindConfigTemplate, err := template.New("kind-config.yaml").Parse(` +--- +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +nodes: +- role: control-plane +- role: worker + extraMounts: + - hostPath: {{ .GitSrcRoot }} + containerPath: /mnt/src_root + - hostPath: /nix + containerPath: /nix + readOnly: true +- role: worker + extraMounts: + - hostPath: {{ .GitSrcRoot }} + containerPath: /mnt/src_root + - hostPath: /nix + containerPath: /nix + readOnly: true +- role: worker + extraMounts: + - hostPath: {{ .GitSrcRoot }} + containerPath: /mnt/src_root + - hostPath: /nix + containerPath: /nix + readOnly: true +networking: + disableDefaultCNI: true + kubeProxyMode: none +containerdConfigPatches: + - |- + [plugins."io.containerd.grpc.v1.cri".registry] + config_path = "/etc/containerd/certs.d" +`) + if err != nil { + log.Fatal(err) + } + + var kindConfig bytes.Buffer + if err = kindConfigTemplate.Execute( + &kindConfig, + map[string]interface{}{ + "GitSrcRoot": gitSrcRoot(), + }, + ); err != nil { + log.Fatal(err) + } + + return kindConfig +} + +// CreateLocalCluster creates a local kind cluster with the config from +// CreateLocalKindConfig. +func CreateLocalCluster( + provider *cluster.Provider, + internalRegistryPort int, + externalRegistryPort int, +) error { + log.Printf("Creating kind cluster.") + + kindConfig := CreateLocalKindConfig() + + log.Println("Instantiating Kind Cluster with the following config:") + log.Print(kindConfig.String()) + + if err := provider.Create( + "kind", + cluster.CreateWithRawConfig(kindConfig.Bytes()), + ); err != nil { + return fmt.Errorf("%w: %w", errKind, err) + } + + if err := configureLocalRegistry("kind-registry", internalRegistryPort, externalRegistryPort); err != nil { + return fmt.Errorf("%w: %w", errKind, err) + } + + return nil +} + +// DeleteLocalCluster removes a local kind cluster. +func DeleteLocalCluster(provider *cluster.Provider) error { + log.Printf("Deleting cluster...") + + if err := provider.Delete("kind", ""); err != nil { + return fmt.Errorf("%w: %w", errKind, err) + } + + return nil +} + +// configureLocalRegistry adjusts all nodes in a kind cluster nodes to be +// compatible with a local passthrough registry. This function configures the +// nodes but doesn't start an actual registry. +func configureLocalRegistry( + registryName string, + internalPort int, + externalPort int, +) error { + cli, err := client.NewClientWithOpts( + client.FromEnv, + client.WithAPIVersionNegotiation(), + ) + if err != nil { + return fmt.Errorf("error creating Docker client: %w", err) + } + + ctx := context.Background() + + nodeNames := []string{ + "kind-control-plane", + "kind-worker", + "kind-worker2", + "kind-worker3", + } + + for _, nodeName := range nodeNames { + if err := createRegistryConfigInNode(ctx, cli, nodeName, registryName, internalPort, externalPort); err != nil { + return fmt.Errorf( + "error configuring kind node %s: %w", + nodeName, + err, + ) + } + } + + return nil +} + +// createRegistryConfigInNode configures a single kind node to be compatible +// with a local passthrough registry. This function configures the node but +// doesn't start any actual registry. +func createRegistryConfigInNode( + ctx context.Context, + cli *client.Client, + nodeName string, regName string, internalPort int, externalPort int, +) error { + config := fmt.Sprintf("[host.\"http://%s:%d\"]", regName, internalPort) + regDir := fmt.Sprintf("/etc/containerd/certs.d/localhost:%d", externalPort) + execConfig := types.ExecConfig{ + Cmd: []string{ + "sh", + "-c", + fmt.Sprintf( + "mkdir -p %s && echo '%s' > %s/hosts.toml", + regDir, + config, + regDir, + ), + }, + } + + execID, err := cli.ContainerExecCreate(ctx, nodeName, execConfig) + if err != nil { + return fmt.Errorf( + "error creating exec instance for node %s: %w", + nodeName, + err, + ) + } + + if err := cli.ContainerExecStart(ctx, execID.ID, types.ExecStartCheck{}); err != nil { + return fmt.Errorf( + "error starting exec command on node %s: %w", + nodeName, + err, + ) + } + + return nil +} diff --git a/native-cli/cmd/down.go b/native-cli/cmd/down.go new file mode 100644 index 000000000..47a0a4eb6 --- /dev/null +++ b/native-cli/cmd/down.go @@ -0,0 +1,34 @@ +package cmd + +import ( + "log" + "os" + + "github.com/TraceMachina/nativelink/native-cli/clusters" + "github.com/spf13/cobra" + "sigs.k8s.io/kind/pkg/cluster" + kindcmd "sigs.k8s.io/kind/pkg/cmd" +) + +// NewDownCmd constructs a new down command. +func NewDownCmd() *cobra.Command { + return &cobra.Command{ + Use: "down", + Short: "Immediately destroy the cluster", + Long: `Immediately tear down the cluster by deleting the kind nodes. + +This command doesn't gracefully shutdown. Instead, removes the kind nodes. Since +the cluster is self-contained this is safe to do and faster than a graceful +shutdown.`, + Run: func(_ *cobra.Command, _ []string) { + kindProvider := cluster.NewProvider( + cluster.ProviderWithLogger(kindcmd.NewLogger()), + ) + if err := clusters.DeleteLocalCluster(kindProvider); err != nil { + log.Println(err) + os.Exit(1) + } + os.Exit(0) + }, + } +} diff --git a/native-cli/cmd/root.go b/native-cli/cmd/root.go new file mode 100644 index 000000000..73797c0ee --- /dev/null +++ b/native-cli/cmd/root.go @@ -0,0 +1,35 @@ +package cmd + +import ( + "os" + + "github.com/spf13/cobra" +) + +// NewRootCmd constructs a new root command. +func NewRootCmd() *cobra.Command { + return &cobra.Command{ + Use: "native", + Short: "NativeLink CLI", + Long: `The NativeLink CLI lets you test NativeLink in a local Kubernetes setup. + +For users this tool showcases running NativeLink in Kubernetes. + +This demo cluster is aggressively optimized for rapid development of NativeLink +itself. It implements its own internal CI system which leverages cache reuse +between of the local nix store so that out-of-cluster nativelink builds become +immediately available to in-cluster deployments.`, + } +} + +// Execute adds all child commands to the root command and sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the rootCmd. +func Execute() { + rootCmd := NewRootCmd() + rootCmd.AddCommand(NewUpCmd()) + rootCmd.AddCommand(NewDownCmd()) + + if err := rootCmd.Execute(); err != nil { + os.Exit(1) + } +} diff --git a/native-cli/cmd/up.go b/native-cli/cmd/up.go new file mode 100644 index 000000000..55b48a1d5 --- /dev/null +++ b/native-cli/cmd/up.go @@ -0,0 +1,106 @@ +package cmd + +import ( + "context" + "log" + "os" + + "github.com/TraceMachina/nativelink/native-cli/clusters" + "github.com/TraceMachina/nativelink/native-cli/programs" + "github.com/pulumi/pulumi/sdk/v3/go/auto" + "github.com/pulumi/pulumi/sdk/v3/go/auto/optup" + "github.com/spf13/cobra" + "sigs.k8s.io/kind/pkg/cluster" + kindcmd "sigs.k8s.io/kind/pkg/cmd" +) + +// runUpCmd implements the Run function of the up command. +func runUpCmd(_ *cobra.Command, _ []string) { + kindProvider := cluster.NewProvider( + cluster.ProviderWithLogger(kindcmd.NewLogger()), + ) + + //nolint:gomnd + if err := clusters.CreateLocalCluster(kindProvider, 5000, 5001); err != nil { + log.Println(err) + log.Println("Skipping kind cluster creation") + } + + ctx := context.Background() + stackName := auto.FullyQualifiedStackName( + "organization", + "nativelink", + "dev", + ) + + // Only use this for local development. + envvars := auto.EnvVars( + map[string]string{"PULUMI_CONFIG_PASSPHRASE": ""}, + ) + + workDir := "." + + stack, err := auto.UpsertStackLocalSource( + ctx, + stackName, + workDir, + auto.Program(programs.ProgramForLocalCluster), + envvars, + ) + if err != nil { + log.Println(err) + os.Exit(1) + } + + log.Printf("Operating on stack %q. Refreshing...\n", stackName) + + _, err = stack.Refresh(ctx) + if err != nil { + log.Println(err) + os.Exit(1) + } + + log.Println("Refresh succeeded. Updating stack...") + + stdoutStreamer := optup.ProgressStreams(os.Stdout) + + _, err = stack.Up(ctx, stdoutStreamer) + if err != nil { + log.Println(err) + os.Exit(1) + } + + log.Println( + "Cluster is running. Use `kubectl` to interact with it.", + ) +} + +// NewUpCmd constructs a new up command. +func NewUpCmd() *cobra.Command { + return &cobra.Command{ + Use: "up", + Short: "Start the development cluster", + Long: `Start a kind cluster with the following configuration: + +- A control pland and three worker nodes. +- A container registry which runs on the host and is passed through to the kind + cluster. +- Cilium as CNI with a configuration that allows creating Gateways with the + "cilium" gatewayClassName. The Gateways map to IPs on the host's local + container network. +- Passthrough from the git root of the current repository to the kind nodes and + then into the cluster via a PersistentVolume and PersistentVolumeClaim called + "local-sources-pv" and "local-sources-pvc". +- Passthrough from the hosts "/nix/store" into the kind nodes and then into + the cluster via a PersistentVolume and PersistentVolumeClaim called + "nix-store-pv" and "nix-store-pvc". + +If this command is called when the cluster is already running the cluster +creation is skipped and instead only a refresh of the deployments is applied. + +All resources apart from the kind nodes themselves are managed by Pulumi. You +can invoke "pulumi stack" with an empty password to get the resource tree. +`, + Run: runUpCmd, + } +} diff --git a/native-cli/components/cilium.go b/native-cli/components/cilium.go new file mode 100644 index 000000000..ed617451e --- /dev/null +++ b/native-cli/components/cilium.go @@ -0,0 +1,195 @@ +package components + +import ( + "context" + "fmt" + "log" + "regexp" + "slices" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/client" + "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/apiextensions" + helmv3 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v3" + metav1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/meta/v1" + "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/yaml" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +) + +// Configuration for a Cilium deployment. +type Cilium struct { + Version string +} + +// Install installs Cilium on the cluster. +func (component *Cilium) Install( + ctx *pulumi.Context, + name string, +) ([]pulumi.Resource, error) { + gatewayAPI, err := yaml.NewConfigFile(ctx, name, &yaml.ConfigFileArgs{ + File: "https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/experimental-install.yaml", + }) + if err != nil { + return nil, fmt.Errorf("%w: %w", errPulumi, err) + } + + cilium, err := helmv3.NewRelease(ctx, name, &helmv3.ReleaseArgs{ + Chart: pulumi.String("cilium"), + Version: pulumi.String(component.Version), + Namespace: pulumi.String("kube-system"), + RepositoryOpts: helmv3.RepositoryOptsArgs{ + Repo: pulumi.String("https://helm.cilium.io/"), + }, + Values: pulumi.Map{ + // Name of the `control-plane` node in `kubectl get nodes`. + "k8sServiceHost": pulumi.String("kind-control-plane"), + + // Forwarded port in `docker ps` for the control plane. + "k8sServicePort": pulumi.String("6443"), + + // Required for proper Cilium operation. + "kubeProxyReplacement": pulumi.String("strict"), + + // Use the Gateway API instead of the older Ingress resource. + "gatewayAPI": pulumi.Map{"enabled": pulumi.Bool(true)}, + + // Use L2-IPAM. + "l2announcements": pulumi.Map{"enabled": pulumi.Bool(true)}, + + "image": pulumi.Map{"pullPolicy": pulumi.String("IfNotPresent")}, + + "hubble": pulumi.Map{ + "relay": pulumi.Map{"enabled": pulumi.Bool(true)}, + "ui": pulumi.Map{"enabled": pulumi.Bool(true)}, + }, + }, + }, pulumi.DependsOn([]pulumi.Resource{gatewayAPI})) + if err != nil { + return nil, fmt.Errorf("%w: %w", errPulumi, err) + } + + l2Announcements, err := l2Announcements(ctx, []pulumi.Resource{cilium}) + if err != nil { + return nil, fmt.Errorf("%w: %w", errPulumi, err) + } + + defaultPool, err := defaultPool(ctx, []pulumi.Resource{cilium}) + if err != nil { + return nil, fmt.Errorf("%w: %w", errPulumi, err) + } + + return slices.Concat( + []pulumi.Resource{cilium}, + l2Announcements, + defaultPool, + ), nil +} + +// l2Announcements creates the CiliumL2AnnouncementPolicy for the cluster. +func l2Announcements( + ctx *pulumi.Context, + ciliumResources []pulumi.Resource, +) ([]pulumi.Resource, error) { + l2Announcements, err := apiextensions.NewCustomResource( + ctx, + "l2-announcements", + &apiextensions.CustomResourceArgs{ + ApiVersion: pulumi.String("cilium.io/v2alpha1"), + Kind: pulumi.String("CiliumL2AnnouncementPolicy"), + // Metadata... + Metadata: &metav1.ObjectMetaArgs{ + Name: pulumi.StringPtr("l2-announcements"), + }, + + OtherFields: map[string]interface{}{ + "spec": pulumi.Map{ + "externalIPs": pulumi.Bool(true), + "loadBalancerIPs": pulumi.Bool(true), + }, + }, + }, + pulumi.DependsOn(ciliumResources), + ) + if err != nil { + return nil, fmt.Errorf("%w: %w", errPulumi, err) + } + + return []pulumi.Resource{l2Announcements}, nil +} + +// kindCIDRs returns the container id range of the kind network. +func kindCIDRs() (string, error) { + dockerCtx := context.Background() + + cli, err := client.NewClientWithOpts( + client.FromEnv, + client.WithAPIVersionNegotiation(), + ) + if err != nil { + return "", fmt.Errorf("%w: %w", errPulumi, err) + } + + networks, err := cli.NetworkList(dockerCtx, types.NetworkListOptions{}) + if err != nil { + return "", fmt.Errorf("%w: %w", errPulumi, err) + } + + for _, network := range networks { + if network.Name == "kind" { + if len(network.IPAM.Config) > 0 { + kindNetCIDR := network.IPAM.Config[0].Subnet + + return kindNetCIDR, nil + } + } + } + + return "", fmt.Errorf("%w: %s", errPulumi, "no kind network found") +} + +// defaultPool creates a CiliumLoadBalancerIPPool which allocates IPs on the +// local kind network which is available to the host. Usually this will be +// something like the 172.20.255.x ip range from the docker network. +func defaultPool( + ctx *pulumi.Context, + ciliumResources []pulumi.Resource, +) ([]pulumi.Resource, error) { + kindNetCIDR, err := kindCIDRs() + if err != nil { + return nil, fmt.Errorf("%w: %w", errPulumi, err) + } + + // This regex replaces the last octet block from "0.0/16" to "255.0/28". + re := regexp.MustCompile(`0\.0/16$`) + ciliumIPCIDR := re.ReplaceAllString(kindNetCIDR, "255.0/28") + + log.Println("KIND Network CIDR:", kindNetCIDR) + log.Println("Modified CIDR for Cilium:", ciliumIPCIDR) + + defaultPool, err := apiextensions.NewCustomResource( + ctx, + "default-pool", + &apiextensions.CustomResourceArgs{ + ApiVersion: pulumi.String("cilium.io/v2alpha1"), + Kind: pulumi.String("CiliumLoadBalancerIPPool"), + Metadata: &metav1.ObjectMetaArgs{ + Name: pulumi.StringPtr("default-pool"), + }, + OtherFields: map[string]interface{}{ + "spec": pulumi.Map{ + "cidrs": pulumi.Array{ + pulumi.Map{ + "cidr": pulumi.String(ciliumIPCIDR), + }, + }, + }, + }, + }, + pulumi.DependsOn(ciliumResources), + ) + if err != nil { + return nil, fmt.Errorf("%w: %w", errPulumi, err) + } + + return []pulumi.Resource{defaultPool}, nil +} diff --git a/native-cli/components/components.go b/native-cli/components/components.go new file mode 100644 index 000000000..19a926bae --- /dev/null +++ b/native-cli/components/components.go @@ -0,0 +1,42 @@ +package components + +import ( + "errors" + "fmt" + "log" + "os" + + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +) + +var ( + errPulumi = errors.New("pulumi error") + errComponent = errors.New("component error") +) + +// Check may be usedd as convenience wrapper to error-check component creation. +func Check(_ []pulumi.Resource, err error) { + if err != nil { + log.Println(err) + os.Exit(1) + } +} + +// A generic Component type used by all components. +type Component interface { + Install(ctx *pulumi.Context, name string) ([]pulumi.Resource, error) +} + +// AddComponent adds a component to the cluster. +func AddComponent[C Component]( + ctx *pulumi.Context, + name string, + component C, +) ([]pulumi.Resource, error) { + resources, err := component.Install(ctx, name) + if err != nil { + return nil, fmt.Errorf("%w: %w", errComponent, err) + } + + return resources, nil +} diff --git a/deployment-examples/chromium/gateway.yaml b/native-cli/components/embedded/nativelink-gateways.yaml similarity index 100% rename from deployment-examples/chromium/gateway.yaml rename to native-cli/components/embedded/nativelink-gateways.yaml diff --git a/native-cli/components/embedded/rebuild-nativelink.yaml b/native-cli/components/embedded/rebuild-nativelink.yaml new file mode 100644 index 000000000..b3576d285 --- /dev/null +++ b/native-cli/components/embedded/rebuild-nativelink.yaml @@ -0,0 +1,259 @@ +--- +# This Task allows running something similar to this natively in K8s: +# +# docker run \ +# --cap-add SYS_ADMIN \ +# --net=host \ +# -v /nix:/mnt/nix:ro \ +# nixpkgs/nix-flakes:latest \ +# nix run \ +# --store 'unix:///mnt/nix/var/nix/daemon-socket/socket?root=/mnt' \ +# --option sandbox false \ +# -L .#image.copyTo \ +# docker://localhost:5001/nativelink:$(nix eval \ +# .#image.imageTag --raw)local \ +# -- \ +# --dest-tls-verify=false +apiVersion: tekton.dev/v1beta1 +kind: Task +metadata: + name: nix2container-copyto + labels: + app.kubernetes.io/versions: "0.1" +spec: + description: > + This task invokes a nix command. + workspaces: + - name: optional-src + optional: true + description: | + The source repository to invoke from. + mountPath: /mnt + readOnly: true + - name: nix-store + description: | + An workspace containing a nix store. Appended via a `--store` argument. + readOnly: true + mountPath: /workspace/nix-store/nix + params: + - name: flakeOutput + type: string + description: | + The output of a Nix Flake. This output must refer to an image built with + nix2container. + + See: + - https://nixos.wiki/wiki/Flakes for more in formation on the Flake + URL schema. + - https://github.com/nlewo/nix2container for more information on the + required builder for the flake outputs. + + Examples: + - "/mnt/src_root#someoutput" for a flake output in the `output-src` + directory. + - "github:TraceMachina/nativelink#image" for the latest nativelink + image. + - "github:/?ref=pull//head#" to use + an image from a pull request of a repository on GitHub. + - name: imageNameOverride + type: string + description: | + An optional override for the image name when pushing to the registry. + + By default the tag is derived from the flake output via + `$(nix eval /mnt/src_root#.imageName --raw)` making + invocations reproducible. This reproducibility may be broken by + overriding the name with some arbitrary value. + default: "" + - name: imageTagOverride + type: string + description: | + An optional override for the image tag when pushing to the registry. + + By default the tag is derived from the flake output via + `$(nix eval /mnt/src_root#.imageTag --raw)` making + invocations reproducible. This reproducibility may be broken by + overriding this tag with some arbitrary value. + + Production deployments shouldn't make use of this value to ensure + reproducibility and "roll-back-ability" of the corresponding pipelinerun. + default: "" + - name: registry + type: string + description: | + The target registry. + + By default, attempts to push to "kind-registry:5000". + default: "kind-registry:5000" + - name: insecureDisableTlsVerify + type: string + description: | + Whether to add `--dest-tls-verify=false` to the copy command. + + This should only be set when working with local registries. + default: "false" + - name: enableNixSandbox + type: string + description: | + Boolean to configure the nix sandbox. + + In the default configuration this Task doesn't use the nix sandbox. + default: "false" + steps: + - name: invoke + image: nixpkgs/nix-flakes:latest + env: + - name: FLAKE_OUTPUT + value: "$(params.flakeOutput)" + - name: IMAGE_NAME_OVERRIDE + value: "$(params.imageNameOverride)" + - name: IMAGE_TAG_OVERRIDE + value: "$(params.imageTagOverride)" + - name: REGISTRY + value: "$(params.registry)" + - name: ENABLE_NIX_SANDBOX + value: "$(params.enableNixSandbox)" + - name: INSECURE_DISABLE_TLS_VERIFY + value: "$(params.insecureDisableTlsVerify)" + securityContext: + capabilities: + # TODO(aaronmondal): This is necessary to allow nix builds which make + # use of containers. This is a security concern and should not be used + # in production grade deployments. Find a better solution. + add: ["SYS_ADMIN"] + script: | + #!/usr/bin/env sh + + if [ "$(workspaces.optional-src.bound)" = "true" ]; then + cd "$(workspaces.optional-src.path)" + fi + + IMAGE_NAME=${IMAGE_NAME_OVERRIDE:-$(nix eval \ + "${FLAKE_OUTPUT}".imageName --raw)} + + IMAGE_TAG=${IMAGE_TAG_OVERRIDE:-$(nix eval \ + "${FLAKE_OUTPUT}".imageTag --raw)} + + # Configuration for Nix. + NIX_SOCKET="unix:///workspace/nix-store/nix/var/nix/daemon-socket/socket" + NIX_ROOT="/workspace/nix-store" + + if [ "${INSECURE_DISABLE_TLS_VERIFY}" = "true" ]; then + nix run \ + --store "${NIX_SOCKET}?root=${NIX_ROOT}" \ + --option sandbox "${ENABLE_NIX_SANDBOX}" \ + --print-build-logs \ + "${FLAKE_OUTPUT}".copyTo \ + docker://"${REGISTRY}"/"${IMAGE_NAME}":"${IMAGE_TAG}" \ + -- \ + --dest-tls-verify=false + else + nix run \ + --store "${NIX_SOCKET}?root=${NIX_ROOT}" \ + --option sandbox "${ENABLE_NIX_SANDBOX}" \ + --print-build-logs \ + "${FLAKE_OUTPUT}".copyTo \ + docker://"${REGISTRY}"/"${IMAGE_NAME}":"${IMAGE_TAG}" + fi +--- +apiVersion: tekton.dev/v1beta1 +kind: Pipeline +metadata: + name: rebuild-nativelink +spec: + description: > + Rebuild the nativelink repository using a specified Nix command. + params: + - name: flakeOutput + type: string + description: | + The output of a Nix Flake. This output must refer to an image built with + nix2container. + + See: + - https://nixos.wiki/wiki/Flakes for more in formation on the Flake + URL schema. + - https://github.com/nlewo/nix2container for more information on the + required builder for the flake outputs. + + Examples: + - "/mnt/src_root#someoutput" for a flake output in the `output-src` + directory. + - "github:TraceMachina/nativelink#image" for the latest nativelink + image. + - "github:/?ref=pull//head#" to use + an image from a pull request of a repository on GitHub. + - name: imageNameOverride + type: string + description: | + An optional override for the image name when pushing to the registry. + + By default the tag is derived from the flake output via + `$(nix eval /mnt/src_root#.imageName --raw)` making + invocations reproducible. This reproducibility may be broken by + overriding the name with some arbitrary value. + default: "" + - name: imageTagOverride + type: string + description: | + An optional override for the image tag when pushing to the registry. + + By default the tag is derived from the flake output via + `$(nix eval /mnt/src_root#.imageTag --raw)` making + invocations reproducible. This reproducibility may be broken by + overriding this tag with some arbitrary value. + + Production deployments shouldn't make use of this value to ensure + reproducibility and "roll-back-ability" of the corresponding pipelinerun. + default: "" + - name: registry + type: string + description: | + The target registry. + + By default, attempts to push to "kind-registry:5000". + default: "kind-registry:5000" + - name: insecureDisableTlsVerify + type: string + description: | + Whether to add `--dest-tls-verify=false` to the copy command. + + This should only be set when working with local registries. + default: "false" + - name: enableNixSandbox + type: string + description: | + Boolean to configure the nix sandbox. + + In the default configuration this Task doesn't use the nix sandbox. + default: "false" + workspaces: + - name: optional-src + optional: true + description: | + The source repository to invoke from. + - name: nix-store + description: | + An workspace containing a nix store. Appended via a `--store` argument. + tasks: + - name: invoke-nix-command + taskRef: + name: nix2container-copyto + workspaces: + - name: optional-src + workspace: optional-src + - name: nix-store + workspace: nix-store + params: + - name: flakeOutput + value: "$(params.flakeOutput)" + - name: imageNameOverride + value: "$(params.imageNameOverride)" + - name: imageTagOverride + value: "$(params.imageTagOverride)" + - name: registry + value: "$(params.registry)" + - name: insecureDisableTlsVerify + value: "$(params.insecureDisableTlsVerify)" + - name: enableNixSandbox + value: "$(params.enableNixSandbox)" diff --git a/native-cli/components/embedded/trigger.yaml b/native-cli/components/embedded/trigger.yaml new file mode 100644 index 000000000..d96939392 --- /dev/null +++ b/native-cli/components/embedded/trigger.yaml @@ -0,0 +1,155 @@ +--- +apiVersion: triggers.tekton.dev/v1beta1 +kind: TriggerTemplate +metadata: + name: nativelink-rebuild-tt +spec: + params: + - name: flakeOutput + description: | + The output of a Nix Flake. This output must refer to an image built with + nix2container. + + See: + - https://nixos.wiki/wiki/Flakes for more in formation on the Flake + URL schema. + - https://github.com/nlewo/nix2container for more information on the + required builder for the flake outputs. + + Examples: + - "/mnt/src_root#someoutput" for a flake output in the `output-src` + directory. + - "github:TraceMachina/nativelink#image" for the latest nativelink + image. + - "github:/?ref=pull//head#" to use + an image from a pull request of a repository on GitHub. + - name: imageNameOverride + description: | + An optional override for the image name when pushing to the registry. + + By default the tag is derived from the flake output via + `$(nix eval /mnt/src_root#.imageName --raw)` making + invocations reproducible. This reproducibility may be broken by + overriding the name with some arbitrary value. + default: "" + - name: imageTagOverride + description: | + An optional override for the image tag when pushing to the registry. + + By default the tag is derived from the flake output via + `$(nix eval /mnt/src_root#.imageTag --raw)` making + invocations reproducible. This reproducibility may be broken by + overriding this tag with some arbitrary value. + + Production deployments shouldn't make use of this value to ensure + reproducibility and "roll-back-ability" of the corresponding pipelinerun. + default: "" + resourcetemplates: + - apiVersion: tekton.dev/v1beta1 + kind: PipelineRun + metadata: + generateName: rebuild-nativelink-run- + spec: + pipelineRef: + name: rebuild-nativelink + workspaces: + - name: optional-src + persistentVolumeClaim: + claimName: local-sources-pvc + - name: nix-store + persistentVolumeClaim: + claimName: nix-store-pvc + params: + - name: flakeOutput + value: "$(tt.params.flakeOutput)" + - name: imageNameOverride + value: "$(tt.params.imageNameOverride)" + - name: imageTagOverride + value: "$(tt.params.imageTagOverride)" + - name: registry + value: "kind-registry:5000" + - name: insecureDisableTlsVerify + value: "true" + - name: enableNixSandbox + value: "false" +--- +apiVersion: triggers.tekton.dev/v1beta1 +kind: TriggerBinding +metadata: + name: nativelink-rebuild-tb +spec: + params: + - name: flakeOutput + value: "$(body.flakeOutput)" + - name: imageNameOverride + value: "$(body.imageNameOverride)" + - name: imageTagOverride + value: "$(body.imageTagOverride)" +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: tekton-robot +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: triggers-eventlistener-binding +subjects: + - kind: ServiceAccount + name: tekton-robot +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: tekton-triggers-eventlistener-roles +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: triggers-eventlistener-clusterbinding +subjects: + - kind: ServiceAccount + name: tekton-robot + namespace: default +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: tekton-triggers-eventlistener-clusterroles +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: eventlistener +spec: + gatewayClassName: cilium + listeners: + - name: eventlistener + protocol: HTTP + port: 8080 +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: eventlistener-route +spec: + parentRefs: + - sectionName: eventlistener + name: eventlistener + rules: + - backendRefs: + # This name is generated by the the EventListener config below. + - name: el-nativelink-rebuild + port: 8080 +--- +apiVersion: triggers.tekton.dev/v1beta1 +kind: EventListener +metadata: + name: nativelink-rebuild +spec: + serviceAccountName: tekton-robot + triggers: + - name: nativelink-rebuild-trigger + bindings: + - ref: nativelink-rebuild-tb + template: + ref: nativelink-rebuild-tt diff --git a/native-cli/components/nativelink-gateways.go b/native-cli/components/nativelink-gateways.go new file mode 100644 index 000000000..1675bf452 --- /dev/null +++ b/native-cli/components/nativelink-gateways.go @@ -0,0 +1,49 @@ +package components + +import ( + _ "embed" + "fmt" + + "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/yaml" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +) + +type NativeLinkGateways struct { + Dependencies []pulumi.Resource +} + +// These are vendored yaml files which we don't port to Pulumi so that we can +// potentially adjust/reuse them in more generic contexts. We embed them in the +// executable to keep the cli portable. +// +//go:embed embedded/nativelink-gateways.yaml +var nativeLinkGatewaysYaml string + +// Install sets up the Gateways for the NativeLink deployment. +// +// Contrary to the rest of the NativeLink setup, these gateways aren't part of +// the regular deployment. Recreating the Gateways would change their local IPs +// which makes development less convenient. Instead, we create them once and +// take the IPs for granted to be fixed after initial creation. +// +// It's unclear whether this indirection is the right approach and we might add +// them to the regular deployments when more infrastructure is in place to +// support changing Gateway IPs. +func (component *NativeLinkGateways) Install( + ctx *pulumi.Context, + name string, +) ([]pulumi.Resource, error) { + nativeLinkGateways, err := yaml.NewConfigGroup( + ctx, + name, + &yaml.ConfigGroupArgs{ + YAML: []string{nativeLinkGatewaysYaml}, + }, + pulumi.DependsOn(component.Dependencies), + ) + if err != nil { + return nil, fmt.Errorf("%w: %w", errPulumi, err) + } + + return []pulumi.Resource{nativeLinkGateways}, nil +} diff --git a/native-cli/components/rebuild-nativelink.go b/native-cli/components/rebuild-nativelink.go new file mode 100644 index 000000000..5b48ae38b --- /dev/null +++ b/native-cli/components/rebuild-nativelink.go @@ -0,0 +1,86 @@ +package components + +import ( + _ "embed" + "fmt" + + "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/yaml" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +) + +type RebuildNativeLink struct { + Dependencies []pulumi.Resource +} + +// These are vendored yaml files which we don't port to Pulumi so that we can +// potentially adjust/reuse them in more generic contexts. We embed them in the +// executable to keep the cli portable. +// +//go:embed embedded/rebuild-nativelink.yaml +var rebuildNativeLinkYaml string + +//go:embed embedded/trigger.yaml +var triggerYaml string + +// Install installs a Tekton Task, Pipeline and EventListener and some +// supporting resources which ultimately allow querying the cluster at a Gateway +// named `eventlistener` with requests like so: +// +// ``` +// EVENTLISTENER=$(kubectl get gtw eventlistener -o=jsonpath='{.status.addresses[0].value}') +// +// # If imageNameOverride and imageTagOverride are unset, they default to: +// # $(nix eval .imageName --raw) +// # $(nix eval .imageTag --raw) +// +// curl -v \ +// -H 'content-Type: application/json' \ +// -d '{ +// "flakeOutput": "./src_root#image", +// "imageNameOverride": "nativelink", +// "imageTagOverride": "local" +// }' \ +// http://${EVENTLISTENER}:8080 +// +// ``` +// +// This pipeline only works with the specific local setup for the NativeLink +// development cluster. The Task makes use of the double-pipe through volumes +// `host -> kind -> K8s` to reuse the host's nix store and local nativelink git +// repository. It then pushes the container image to the container registry +// which previous infrastructure setups configured to pass through from host to +// the cluster. The result is that these Pipelines can complete in <15sec as +// opposed to ~10min without these optimizations. +// +// WARNING: At the moment the Task makes use of `SYS_ADMIN` privilege escalation +// to interact with the host's nix socket and the kind node's container daemon. +func (component *RebuildNativeLink) Install( + ctx *pulumi.Context, + name string, +) ([]pulumi.Resource, error) { + rebuildNativeLink, err := yaml.NewConfigGroup( + ctx, + name, + &yaml.ConfigGroupArgs{ + YAML: []string{rebuildNativeLinkYaml}, + }, + pulumi.DependsOn(component.Dependencies), + ) + if err != nil { + return nil, fmt.Errorf("%w: %w", errPulumi, err) + } + + ciTrigger, err := yaml.NewConfigGroup( + ctx, + name+"-triggers", + &yaml.ConfigGroupArgs{ + YAML: []string{triggerYaml}, + }, + pulumi.DependsOn(component.Dependencies), + ) + if err != nil { + return nil, fmt.Errorf("%w: %w", errPulumi, err) + } + + return []pulumi.Resource{rebuildNativeLink, ciTrigger}, nil +} diff --git a/native-cli/components/registry.go b/native-cli/components/registry.go new file mode 100644 index 000000000..b1b24d315 --- /dev/null +++ b/native-cli/components/registry.go @@ -0,0 +1,51 @@ +package components + +import ( + "fmt" + + "github.com/pulumi/pulumi-docker/sdk/v3/go/docker" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +) + +// A local container registry. +type Registry struct { + InternalPort int + ExternalPort int +} + +// Install installs a local registry on the host and makes it available to a +// kind cluster. +func (component *Registry) Install( + ctx *pulumi.Context, + name string, +) ([]pulumi.Resource, error) { + registry, err := docker.NewContainer( + ctx, + name, + &docker.ContainerArgs{ + Image: pulumi.String("registry:2"), + Ports: docker.ContainerPortArray{ + &docker.ContainerPortArgs{ + Internal: pulumi.Int(component.InternalPort), + External: pulumi.Int(component.ExternalPort), + }, + }, + NetworksAdvanced: docker.ContainerNetworksAdvancedArray{ + // By default the registry will connect to the `bridge` network. + // We add it to the `kind` network as well so that we can push + // images locally to the registry and have them accessible by + // kind. + &docker.ContainerNetworksAdvancedArgs{ + Name: pulumi.String("kind"), + }, + }, + Restart: pulumi.String("always"), + Name: pulumi.String("kind-registry"), + }, + ) + if err != nil { + return nil, fmt.Errorf("%w: %w", errPulumi, err) + } + + return []pulumi.Resource{registry}, nil +} diff --git a/native-cli/components/tekton.go b/native-cli/components/tekton.go new file mode 100644 index 000000000..08e375a97 --- /dev/null +++ b/native-cli/components/tekton.go @@ -0,0 +1,123 @@ +package components + +import ( + "fmt" + + "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/yaml" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +) + +// The configuration for Tekton Pipelines. +type TektonPipelines struct { + Version string +} + +// Install installs Tekton Pipelines on the cluster. +// +// This function performs an additional feature-flag transformation to set +// `disable-affinity-assistant=true` and `coscheduling=pipelineruns`. +func (component *TektonPipelines) Install( + ctx *pulumi.Context, + name string, +) ([]pulumi.Resource, error) { + // This transformation disables the affinity assistant switches coscheduling + // from "workspace" to "pipelineruns". This allows us to use multiple PVCs + // in the same task. Usually this is discouraged for storage locality + // reasons, but since we're running on a local kind cluster that doesn't + // matter to us. + transformation := func(state map[string]interface{}, _ ...pulumi.ResourceOption) { + if kind, kindExists := state["kind"].(string); kindExists && + kind == "ConfigMap" { + metadata, metadataExists := state["metadata"].(map[string]interface{}) + if !metadataExists { + return + } + + name, nameExists := metadata["name"].(string) + + namespace, namespaceExists := metadata["namespace"].(string) + + if nameExists && namespaceExists && name == "feature-flags" && + namespace == "tekton-pipelines" { + data, dataExists := state["data"].(map[string]interface{}) + if dataExists { + data["disable-affinity-assistant"] = "true" // Your specific configuration change + data["coschedule"] = "pipelineruns" + } + } + } + } + + tektonPipelines, err := yaml.NewConfigFile(ctx, name, &yaml.ConfigFileArgs{ + File: fmt.Sprintf( + "https://storage.googleapis.com/tekton-releases/pipeline/previous/v%s/release.yaml", + component.Version, + ), + Transformations: []yaml.Transformation{transformation}, + }) + if err != nil { + return nil, fmt.Errorf("%w: %w", errPulumi, err) + } + + return []pulumi.Resource{tektonPipelines}, nil +} + +// The configuration for Tekton Triggers. +type TektonTriggers struct { + Version string +} + +// Install installs the Tekton Triggers release and interceptors on the cluster. +func (component *TektonTriggers) Install( + ctx *pulumi.Context, + name string, +) ([]pulumi.Resource, error) { + tektonTriggers, err := yaml.NewConfigFile(ctx, name, &yaml.ConfigFileArgs{ + File: fmt.Sprintf( + "https://storage.googleapis.com/tekton-releases/triggers/previous/v%s/release.yaml", + component.Version, + ), + }) + if err != nil { + return nil, fmt.Errorf("%w: %w", errPulumi, err) + } + + tektonTriggersInterceptors, err := yaml.NewConfigFile( + ctx, + name+"-interceptors", + &yaml.ConfigFileArgs{ + File: fmt.Sprintf( + "https://storage.googleapis.com/tekton-releases/triggers/previous/v%s/interceptors.yaml", + component.Version, + ), + }, + ) + if err != nil { + return nil, fmt.Errorf("%w: %w", errPulumi, err) + } + + return []pulumi.Resource{tektonTriggers, tektonTriggersInterceptors}, nil +} + +// Configuration for the Tekton Dashboard. +type TektonDashboard struct { + Version string +} + +// Install installs the Tekton Dashboard on the cluster. +func (component *TektonDashboard) Install( + ctx *pulumi.Context, + name string, +) ([]pulumi.Resource, error) { + tektonDashboard, err := yaml.NewConfigFile(ctx, name, &yaml.ConfigFileArgs{ + File: fmt.Sprintf( + "https://storage.googleapis.com/tekton-releases/dashboard/previous/v%s/release.yaml", + component.Version, + ), + }) + if err != nil { + return nil, fmt.Errorf("%w: %w", errPulumi, err) + } + + return []pulumi.Resource{tektonDashboard}, nil +} diff --git a/native-cli/components/volumes.go b/native-cli/components/volumes.go new file mode 100644 index 000000000..82587e98d --- /dev/null +++ b/native-cli/components/volumes.go @@ -0,0 +1,80 @@ +package components + +import ( + "fmt" + + corev1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/core/v1" + metav1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/meta/v1" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +) + +// Configuration to create a PV and PVC which mount a node-local directory via +// `hostPath`. +type LocalPVAndPVC struct { + Size string + HostPath string +} + +// Install installs the PV and PVC from a `LocalPVAndPVC` config. +func (component *LocalPVAndPVC) Install( + ctx *pulumi.Context, + name string, +) ([]pulumi.Resource, error) { + pvName := name + "-pv" + pvcName := name + "-pvc" + + persistentVolumeClaim, err := corev1.NewPersistentVolumeClaim( + ctx, + pvcName, + &corev1.PersistentVolumeClaimArgs{ + ApiVersion: pulumi.String("v1"), + Kind: pulumi.String("PersistentVolumeClaim"), + Metadata: &metav1.ObjectMetaArgs{ + Name: pulumi.String(pvcName), + }, + Spec: &corev1.PersistentVolumeClaimSpecArgs{ + StorageClassName: pulumi.String("manual"), + VolumeName: pulumi.String(pvName), + AccessModes: pulumi.StringArray{ + pulumi.String("ReadWriteMany"), + }, + Resources: &corev1.VolumeResourceRequirementsArgs{ + Requests: pulumi.StringMap{ + "storage": pulumi.String(component.Size), + }, + }, + }, + }, + ) + if err != nil { + return nil, fmt.Errorf("%w: %w", errPulumi, err) + } + + persistentVolume, err := corev1.NewPersistentVolume( + ctx, + pvName, + &corev1.PersistentVolumeArgs{ + ApiVersion: pulumi.String("v1"), + Kind: pulumi.String("PersistentVolume"), + Metadata: &metav1.ObjectMetaArgs{ + Name: pulumi.String(pvName), + Labels: pulumi.StringMap{"type": pulumi.String("local")}, + }, + Spec: &corev1.PersistentVolumeSpecArgs{ + StorageClassName: pulumi.String("manual"), + Capacity: pulumi.StringMap{ + "storage": pulumi.String(component.Size), + }, + AccessModes: pulumi.StringArray{pulumi.String("ReadWriteMany")}, + HostPath: &corev1.HostPathVolumeSourceArgs{ + Path: pulumi.String(component.HostPath), + }, + }, + }, + ) + if err != nil { + return nil, fmt.Errorf("%w: %w", errPulumi, err) + } + + return []pulumi.Resource{persistentVolume, persistentVolumeClaim}, nil +} diff --git a/native-cli/default.nix b/native-cli/default.nix new file mode 100644 index 000000000..e1b9d845c --- /dev/null +++ b/native-cli/default.nix @@ -0,0 +1,26 @@ +{pkgs, ...}: +pkgs.buildGoModule { + pname = "native-cli"; + version = "0.3.0"; + src = ./.; + vendorHash = "sha256-HL407aegfvZ8UcziWNgmAxPveHXYf4KcBTolYGVBd4w="; + buildInputs = [pkgs.makeWrapper]; + installPhase = '' + runHook preInstall + install -D $GOPATH/bin/native-cli $out/bin/native + runHook postInstall + ''; + postInstall = let + pulumiPath = pkgs.lib.makeBinPath [ + (pkgs.pulumi.withPackages (ps: [ps.pulumi-language-go])) + ]; + in '' + wrapProgram $out/bin/native --prefix PATH : ${pulumiPath} + ''; + meta = with pkgs.lib; { + description = "NativeLink development cluster."; + homepage = "https://github.com/TraceMachina/nativelink"; + license = licenses.asl20; + maintainers = [maintainers.aaronmondal]; + }; +} diff --git a/native-cli/go.mod b/native-cli/go.mod new file mode 100644 index 000000000..c5dfe4eee --- /dev/null +++ b/native-cli/go.mod @@ -0,0 +1,124 @@ +module github.com/TraceMachina/nativelink/native-cli + +go 1.22.1 + +require ( + github.com/docker/docker v26.0.1+incompatible + github.com/go-git/go-git/v5 v5.12.0 + github.com/pulumi/pulumi-docker/sdk/v3 v3.6.1 + github.com/pulumi/pulumi-kubernetes/sdk/v4 v4.10.0 + github.com/pulumi/pulumi/sdk/v3 v3.113.0 + github.com/spf13/cobra v1.8.0 + sigs.k8s.io/kind v0.22.0 +) + +require ( + dario.cat/mergo v1.0.0 // indirect + github.com/BurntSushi/toml v1.3.2 // indirect + github.com/Masterminds/semver v1.5.0 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/ProtonMail/go-crypto v1.0.0 // indirect + github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect + github.com/agext/levenshtein v1.2.3 // indirect + github.com/alessio/shellescape v1.4.2 // indirect + github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect + github.com/atotto/clipboard v0.1.4 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/blang/semver v3.5.1+incompatible // indirect + github.com/charmbracelet/bubbles v0.18.0 // indirect + github.com/charmbracelet/bubbletea v0.25.0 // indirect + github.com/charmbracelet/lipgloss v0.10.0 // indirect + github.com/cheggaaa/pb v1.0.29 // indirect + github.com/cloudflare/circl v1.3.7 // indirect + github.com/containerd/console v1.0.4 // indirect + github.com/containerd/log v0.1.0 // indirect + github.com/cyphar/filepath-securejoin v0.2.4 // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/djherbis/times v1.6.0 // indirect + github.com/docker/go-connections v0.5.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/evanphx/json-patch/v5 v5.9.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect + github.com/go-git/go-billy/v5 v5.5.0 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/glog v1.2.1 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/google/safetext v0.0.0-20240104143208-7a7d9b3d812f // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/hcl/v2 v2.20.1 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/kevinburke/ssh_config v1.2.0 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-localereader v0.0.1 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mitchellh/go-ps v1.0.0 // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/term v0.5.0 // indirect + github.com/morikuni/aec v1.0.0 // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect + github.com/muesli/reflow v0.3.0 // indirect + github.com/muesli/termenv v0.15.2 // indirect + github.com/nxadm/tail v1.4.11 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0 // indirect + github.com/opentracing/basictracer-go v1.1.0 // indirect + github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/pborman/uuid v1.2.1 // indirect + github.com/pelletier/go-toml v1.9.5 // indirect + github.com/pgavlin/fx v0.1.6 // indirect + github.com/pjbgf/sha1cd v0.3.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pkg/term v1.1.0 // indirect + github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 // indirect + github.com/pulumi/esc v0.8.3 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 // indirect + github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect + github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect + github.com/skeema/knownhosts v1.2.2 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/texttheater/golang-levenshtein v1.0.1 // indirect + github.com/tweekmonster/luser v0.0.0-20161003172636-3fa38070dbd7 // indirect + github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect + github.com/uber/jaeger-lib v2.4.1+incompatible // indirect + github.com/xanzy/ssh-agent v0.3.3 // indirect + github.com/zclconf/go-cty v1.14.4 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0 // indirect + go.opentelemetry.io/otel v1.25.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0 // indirect + go.opentelemetry.io/otel/metric v1.25.0 // indirect + go.opentelemetry.io/otel/sdk v1.25.0 // indirect + go.opentelemetry.io/otel/trace v1.25.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + golang.org/x/crypto v0.22.0 // indirect + golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/term v0.19.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.20.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // indirect + google.golang.org/grpc v1.63.2 // indirect + google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect + gopkg.in/warnings.v0 v0.1.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + gotest.tools/v3 v3.5.1 // indirect + lukechampine.com/frand v1.4.2 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect +) diff --git a/native-cli/go.sum b/native-cli/go.sum new file mode 100644 index 000000000..5b5704e38 --- /dev/null +++ b/native-cli/go.sum @@ -0,0 +1,419 @@ +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= +github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= +github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= +github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= +github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= +github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= +github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= +github.com/alessio/shellescape v1.4.2 h1:MHPfaU+ddJ0/bYWpgIeUnQUqKrlJ1S7BfEYPM4uEoM0= +github.com/alessio/shellescape v1.4.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= +github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= +github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0= +github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw= +github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM= +github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg= +github.com/charmbracelet/lipgloss v0.10.0 h1:KWeXFSexGcfahHX+54URiZGkBFazf70JNMtwg/AFW3s= +github.com/charmbracelet/lipgloss v0.10.0/go.mod h1:Wig9DSfvANsxqkRsqj6x87irdy123SR4dOXlKa91ciE= +github.com/cheggaaa/pb v1.0.29 h1:FckUN5ngEk2LpvuG0fw1GEFx6LtyY2pWI/Z2QgCnEYo= +github.com/cheggaaa/pb v1.0.29/go.mod h1:W40334L7FMC5JKWldsTWbdGjLo0RxUKK73K+TuPxX30= +github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= +github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= +github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= +github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro= +github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= +github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c= +github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0= +github.com/docker/docker v26.0.1+incompatible h1:t39Hm6lpXuXtgkF0dm1t9a5HkbUfdGy6XbWexmGr+hA= +github.com/docker/docker v26.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= +github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= +github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE= +github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= +github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= +github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= +github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys= +github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= +github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2/go.mod h1:Tv1PlzqC9t8wNnpPdctvtSUOPUUg4SHeE6vR1Ir2hmg= +github.com/google/safetext v0.0.0-20240104143208-7a7d9b3d812f h1:o2yGZLlsOj5H5uvtQNEdi6DeA0GbUP3lm0gWW5RvY0s= +github.com/google/safetext v0.0.0-20240104143208-7a7d9b3d812f/go.mod h1:H3K1Iu/utuCfa10JO+GsmKUYSWi7ug57Rk6GaDRHaaQ= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU= +github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU= +github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/hcl/v2 v2.20.1 h1:M6hgdyz7HYt1UN9e61j+qKJBqR3orTWbI1HKBJEdxtc= +github.com/hashicorp/hcl/v2 v2.20.1/go.mod h1:TZDqQ4kNKCbh1iJp99FdPiUaVDDUPivbqxZulxDYqL4= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= +github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= +github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= +github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= +github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= +github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= +github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= +github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= +github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= +github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= +github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/opentracing/basictracer-go v1.1.0 h1:Oa1fTSBvAl8pa3U+IJYqrKm0NALwH9OsgwOqDv4xJW0= +github.com/opentracing/basictracer-go v1.1.0/go.mod h1:V2HZueSJEp879yv285Aap1BS69fQMD+MNP1mRs6mBQc= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= +github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pgavlin/fx v0.1.6 h1:r9jEg69DhNoCd3Xh0+5mIbdbS3PqWrVWujkY76MFRTU= +github.com/pgavlin/fx v0.1.6/go.mod h1:KWZJ6fqBBSh8GxHYqwYCf3rYE7Gp2p0N8tJp8xv9u9M= +github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= +github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/term v1.1.0 h1:xIAAdCMh3QIAy+5FrE8Ad8XoDhEU4ufwbaSozViP9kk= +github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 h1:vkHw5I/plNdTr435cARxCW6q9gc0S/Yxz7Mkd38pOb0= +github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231/go.mod h1:murToZ2N9hNJzewjHBgfFdXhZKjY3z5cYC1VXk+lbFE= +github.com/pulumi/esc v0.8.3 h1:myeDL6dD/mz34zZjCL8s7d/tWHBJYxfMxDCL11MHoqc= +github.com/pulumi/esc v0.8.3/go.mod h1:v5VAPxYDa9DRwvubbzKt4ZYf5y0esWC2ccSp/AT923I= +github.com/pulumi/pulumi-docker/sdk/v3 v3.6.1 h1:plWLn9O6u80Vr37LoCsckyobBfcrdTU9cERor72QjqA= +github.com/pulumi/pulumi-docker/sdk/v3 v3.6.1/go.mod h1:N4Yu4c49QErfucPt9Y/fGmpTryRqc0VfhyKHsGR9/g8= +github.com/pulumi/pulumi-kubernetes/sdk/v4 v4.10.0 h1:xHEFQ/k2fzFp3TADpE/US28Ri4WZfzEAcT99fiDZ1+U= +github.com/pulumi/pulumi-kubernetes/sdk/v4 v4.10.0/go.mod h1:9SKR5gTWY4FP9XnSNWd+HSeQt9lffrNCe+zbKvezI/o= +github.com/pulumi/pulumi/sdk/v3 v3.113.0 h1:CIlmxJZdjxpPPoFe/rrP1dWTwh3CB7ahs/dA6SHcbuE= +github.com/pulumi/pulumi/sdk/v3 v3.113.0/go.mod h1:JWSzKBoHd8rlncC1DhXLf7YdV+Bk/Qf+hSZOOQh0WwQ= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI= +github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A= +github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= +github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= +github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/texttheater/golang-levenshtein v1.0.1 h1:+cRNoVrfiwufQPhoMzB6N0Yf/Mqajr6t1lOv8GyGE2U= +github.com/texttheater/golang-levenshtein v1.0.1/go.mod h1:PYAKrbF5sAiq9wd+H82hs7gNaen0CplQ9uvm6+enD/8= +github.com/tweekmonster/luser v0.0.0-20161003172636-3fa38070dbd7 h1:X9dsIWPuuEJlPX//UmRKophhOKCGXc46RVIGuttks68= +github.com/tweekmonster/luser v0.0.0-20161003172636-3fa38070dbd7/go.mod h1:UxoP3EypF8JfGEjAII8jx1q8rQyDnX8qdTCs/UQBVIE= +github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o= +github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= +github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zclconf/go-cty v1.14.4 h1:uXXczd9QDGsgu0i/QFR/hzI5NYCHLf6NQw/atrbnhq8= +github.com/zclconf/go-cty v1.14.4/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0 h1:cEPbyTSEHlQR89XVlyo78gqluF8Y3oMeBkXGWzQsfXY= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0/go.mod h1:DKdbWcT4GH1D0Y3Sqt/PFXt2naRKDWtU+eE6oLdFNA8= +go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k= +go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0 h1:dT33yIHtmsqpixFsSQPwNeY5drM9wTcoL8h0FWF4oGM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0/go.mod h1:h95q0LBGh7hlAC08X2DhSeyIG02YQ0UyioTCVAqRPmc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0 h1:Mbi5PKN7u322woPa85d7ebZ+SOvEoPvoiBu+ryHWgfA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0/go.mod h1:e7ciERRhZaOZXVjx5MiL8TK5+Xv7G5Gv5PA2ZDEJdL8= +go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA= +go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s= +go.opentelemetry.io/otel/sdk v1.25.0 h1:PDryEJPC8YJZQSyLY5eqLeafHtG+X7FWnf3aXMtxbqo= +go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw= +go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM= +go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I= +go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI= +go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 h1:ESSUROHIBHg7USnszlcdmjBEwdMj9VUvU+OPk4yl2mc= +golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ= +golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= +golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= +google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0= +google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be h1:LG9vZxsWGOmUKieR8wPAUR3u3MpnYFQZROPIMaXh7/A= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= +google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= +lukechampine.com/frand v1.4.2 h1:RzFIpOvkMXuPMBb9maa4ND4wjBn71E1Jpf8BzJHMaVw= +lukechampine.com/frand v1.4.2/go.mod h1:4S/TM2ZgrKejMcKMbeLjISpJMO+/eZ1zu3vYX9dtj3s= +pgregory.net/rapid v0.6.1 h1:4eyrDxyht86tT4Ztm+kvlyNBLIk071gR+ZQdhphc9dQ= +pgregory.net/rapid v0.6.1/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= +sigs.k8s.io/kind v0.22.0 h1:z/+yr/azoOfzsfooqRsPw1wjJlqT/ukXP0ShkHwNlsI= +sigs.k8s.io/kind v0.22.0/go.mod h1:aBlbxg08cauDgZ612shr017/rZwqd7AS563FvpWKPVs= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/native-cli/main.go b/native-cli/main.go new file mode 100644 index 000000000..833ce3981 --- /dev/null +++ b/native-cli/main.go @@ -0,0 +1,9 @@ +package main + +import ( + nativecmd "github.com/TraceMachina/nativelink/native-cli/cmd" +) + +func main() { + nativecmd.Execute() +} diff --git a/native-cli/programs/local.go b/native-cli/programs/local.go new file mode 100644 index 000000000..a335831e6 --- /dev/null +++ b/native-cli/programs/local.go @@ -0,0 +1,108 @@ +package programs + +import ( + "log" + "os" + "slices" + + "github.com/TraceMachina/nativelink/native-cli/components" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +) + +// ProgramForLocalCluster is the Pulumi program for the development cluster. +// +//nolint:funlen +func ProgramForLocalCluster(ctx *pulumi.Context) error { + components.Check(components.AddComponent( + ctx, + "kind-registry", + &components.Registry{ + InternalPort: 5000, //nolint:gomnd + ExternalPort: 5001, //nolint:gomnd + }, + )) + + cilium, err := components.AddComponent( + ctx, + "cilium", + &components.Cilium{Version: "1.15.3"}, + ) + if err != nil { + log.Println(err) + os.Exit(1) + } + + localSources, err := components.AddComponent( + ctx, + "local-sources", + &components.LocalPVAndPVC{ + Size: "50Mi", + HostPath: "/mnt", + }, + ) + if err != nil { + log.Println(err) + os.Exit(1) + } + + nixStore, err := components.AddComponent( + ctx, + "nix-store", + &components.LocalPVAndPVC{ + Size: "10Gi", + HostPath: "/nix", + }, + ) + if err != nil { + log.Println(err) + os.Exit(1) + } + + tektonPipelines, err := components.AddComponent( + ctx, + "tekton-pipelines", + &components.TektonPipelines{Version: "0.58.0"}, + ) + if err != nil { + log.Println(err) + os.Exit(1) + } + + tektonTriggers, err := components.AddComponent( + ctx, + "tekton-triggers", + &components.TektonTriggers{Version: "0.26.1"}, + ) + if err != nil { + log.Println(err) + os.Exit(1) + } + + components.Check(components.AddComponent( + ctx, + "tekton-dashboard", + &components.TektonDashboard{Version: "0.45.0"}, + )) + components.Check(components.AddComponent( + ctx, + "rebuild-nativelink", + &components.RebuildNativeLink{ + Dependencies: slices.Concat( + cilium, + tektonPipelines, + tektonTriggers, + localSources, + nixStore, + ), + }, + )) + components.Check(components.AddComponent( + ctx, + "nativelink-gatways", + &components.NativeLinkGateways{ + Dependencies: cilium, + }, + )) + + return nil +} diff --git a/tools/pre-commit-hooks.nix b/tools/pre-commit-hooks.nix index 1b902ee3b..5f63891ef 100644 --- a/tools/pre-commit-hooks.nix +++ b/tools/pre-commit-hooks.nix @@ -3,7 +3,7 @@ nightly-rust, ... }: let - excludes = ["^nativelink-proto/genproto"]; + excludes = ["^nativelink-proto/genproto" "native-cli/vendor"]; in { # Default hooks trailing-whitespace-fixer = { @@ -80,12 +80,63 @@ in { in "${script}/bin/forbid-binary-files"; }; + # Dockerfile + hadolint.enable = true; + # Documentation vale = { + inherit excludes; enable = true; settings.configPath = ".vale.ini"; }; + # Go + gci = { + excludes = ["native-cli/vendor"]; + enable = true; + name = "gci"; + entry = "${pkgs.gci}/bin/gci write"; + description = "Fix go imports."; + types = ["go"]; + }; + gofumpt = { + excludes = ["native-cli/vendor"]; + enable = true; + name = "gofumpt"; + entry = "${pkgs.gofumpt}/bin/gofumpt -w -l"; + description = "Format Go."; + types = ["go"]; + }; + golines = { + excludes = ["native-cli/vendor"]; + enable = true; + name = "golines"; + entry = "${pkgs.golines}/bin/golines --max-len=80 -w"; + description = "Shorten Go lines."; + types = ["go"]; + }; + # TODO(aaronmondal): This linter works in the nix developmen environment, but + # not with `nix flake check`. It's unclear how to fix this. + golangci-lint-in-shell = { + enable = true; + entry = let + script = pkgs.writeShellScript "precommit-golangci-lint" '' + # TODO(aaronmondal): This linter works in the nix development + # environment, but not with `nix flake check`. It's + # unclear how to fix this. + if [ ''${IN_NIX_SHELL} = "impure" ]; then + export PATH=${pkgs.go}/bin:$PATH + cd native-cli + ${pkgs.golangci-lint}/bin/golangci-lint run --modules-download-mode=readonly + fi + ''; + in + builtins.toString script; + types = ["go"]; + require_serial = true; + pass_filenames = false; + }; + # Nix alejandra.enable = true; statix.enable = true; @@ -113,7 +164,4 @@ in { entry = "${pkgs.bazel-buildtools}/bin/buildifier -lint=warn"; types = ["bazel"]; }; - - # Dockerfile - hadolint.enable = true; }