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; }