Skip to content

Commit

Permalink
feat: build nix image (#2132)
Browse files Browse the repository at this point in the history
## Description:
Add ability to build Nix images inside APIC.


## Is this change user facing?
YES
  • Loading branch information
lostbean committed Feb 9, 2024
1 parent aaf64ca commit 0eae9fc
Show file tree
Hide file tree
Showing 40 changed files with 852 additions and 70 deletions.
10 changes: 6 additions & 4 deletions cli/cli/commands/import/import.go
Expand Up @@ -3,6 +3,11 @@ package _import
import (
"context"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"

"github.com/compose-spec/compose-go/loader"
"github.com/compose-spec/compose-go/types"
"github.com/joho/godotenv"
Expand All @@ -25,10 +30,6 @@ import (
"github.com/kurtosis-tech/kurtosis/name_generator"
"github.com/kurtosis-tech/stacktrace"
"github.com/sirupsen/logrus"
"os"
"path/filepath"
"strconv"
"strings"
)

const (
Expand Down Expand Up @@ -175,6 +176,7 @@ func run(

func convertComposeFileToStarlark(path string, dotEnvMap map[string]string) (string, map[string]string, error) {
project, err := loader.Load(types.ConfigDetails{ //nolint:exhaustruct
// nolint: exhaustruct
ConfigFiles: []types.ConfigFile{{Filename: path}},
Environment: dotEnvMap,
})
Expand Down
Expand Up @@ -2,11 +2,13 @@ package docker_kurtosis_backend

import (
"context"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec"
"io"
"sync"

"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/nix_build_spec"

"github.com/sirupsen/logrus"

"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_impls/docker/docker_kurtosis_backend/engine_functions"
Expand Down Expand Up @@ -544,6 +546,10 @@ func (backend *DockerKurtosisBackend) BuildImage(ctx context.Context, imageName
return backend.dockerManager.BuildImage(ctx, imageName, imageBuildSpec)
}

func (backend *DockerKurtosisBackend) NixBuild(ctx context.Context, nixBuildSpec *nix_build_spec.NixBuildSpec) (string, error) {
return backend.dockerManager.NixBuild(ctx, nixBuildSpec)
}

// ====================================================================================================
//
// Private helper functions shared by multiple subfunctions files
Expand Down
Expand Up @@ -9,22 +9,28 @@ import (
"bufio"
"bytes"
"context"
"crypto/md5"
"encoding/json"
"fmt"
"github.com/docker/docker/api/types/registry"
"github.com/docker/go-units"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/uuid_generator"
"github.com/kurtosis-tech/kurtosis/utils"
"io"
"math"
"net"
"os"
"os/exec"
"regexp"
"strings"
"sync"
"time"

"github.com/docker/docker/api/types/registry"
"github.com/docker/go-units"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/nix_build_spec"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/image_utils"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/uuid_generator"
"github.com/kurtosis-tech/kurtosis/utils"

"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
Expand Down Expand Up @@ -152,6 +158,8 @@ const (

// Per https://github.com/hashicorp/waypoint/pull/1937/files
buildkitSessionSharedKey = ""

nixCmdPath = "/nix/var/nix/profiles/default/bin/nix"
)

type RestartPolicy string
Expand Down Expand Up @@ -1313,6 +1321,58 @@ func (manager *DockerManager) FetchImage(ctx context.Context, image string, regi
return pulledFromRemote, imageArchitecture, nil
}

func (manager *DockerManager) NixBuild(ctx context.Context, nixBuildSpec *nix_build_spec.NixBuildSpec) (string, error) {
flakeReference := nixBuildSpec.GetFullFlakeReference()

// Flake generates a link to the nix store containing the image result, to avoid collision with a possible existing one from the
// build context (from the user env) and which would result when trying to overwrite it, we create a unique one
hasher := md5.New()
hasher.Write([]byte(flakeReference))
resultLink := fmt.Sprintf("nix-result-%x", hasher.Sum(nil))

cmd := exec.Command(
nixCmdPath, "build", flakeReference,
"--print-out-paths",
"--extra-experimental-features", "flakes nix-command",
"--out-link", resultLink)

var errBuffer strings.Builder
cmd.Stderr = &errBuffer
imageFileRaw, err := cmd.Output()
if err != nil {
errMsg := errBuffer.String()
logrus.WithError(err).Error(errMsg)
return "", stacktrace.Propagate(err, "Failed to build nix image with Nix.")
}
imageFile := strings.TrimSpace(string(imageFileRaw))
logrus.Debugf("Nix flake image on attribute %s, result on image file %s", flakeReference, imageFile)

imageTags, err := image_utils.GetRepoTags(imageFile)
if err != nil {
return "", stacktrace.Propagate(err, "Failed to get image tags from Nix image %s", imageFile)
}

if len(imageTags) == 0 {
return "", stacktrace.NewError("Generated image %s did not have any tags", imageFile)
} else if len(imageTags) > 1 {
logrus.Warnf("Generated image %s had multiple tags: %v. We'll select the first.", imageFile, imageTags)
}
imageTag := imageTags[0]

image, err := os.Open(imageFile)
if err != nil {
return "", stacktrace.Propagate(err, "Failed to open generated Nix image on %s", imageFile)
}

_, err = manager.dockerClient.ImageLoad(ctx, image, false)
if err != nil {
return "", stacktrace.Propagate(err, "Failed to load Nix image %s in docker", imageFile)
}
logrus.Debugf("Nix generated image file %s is loaded into docker", imageFile)

return imageTag, nil
}

func (manager *DockerManager) BuildImage(ctx context.Context, imageName string, imageBuildSpec *image_build_spec.ImageBuildSpec) (string, error) {
buildContextDirPath := imageBuildSpec.GetBuildContextDir()
buildContextTarReader, err := getBuildContextReader(buildContextDirPath)
Expand Down
Expand Up @@ -321,6 +321,7 @@ func createEngineClusterRole(
}
clusterRoleName := clusterRolesAttributes.GetName().GetString()
clusterRoleLabels := shared_helpers.GetStringMapFromLabelMap(clusterRolesAttributes.GetLabels())
// nolint: exhaustruct
clusterRolePolicyRules := []rbacv1.PolicyRule{
{
Verbs: []string{
Expand Down Expand Up @@ -388,6 +389,7 @@ func createEngineClusterRoleBindings(
}
clusterRoleBindingsName := clusterRoleBindingsAttributes.GetName().GetString()
clusterRoleBindingsLabels := shared_helpers.GetStringMapFromLabelMap(clusterRoleBindingsAttributes.GetLabels())
// nolint: exhaustruct
clusterRoleBindingsSubjects := []rbacv1.Subject{
{
Kind: rbacv1.ServiceAccountKind,
Expand Down Expand Up @@ -452,6 +454,7 @@ func createEnginePod(
}
engineContainerEnvVars = append(engineContainerEnvVars, envVar)
}
// nolint: exhaustruct
engineContainers := []apiv1.Container{
{
Name: kurtosisEngineContainerName,
Expand Down
Expand Up @@ -2,9 +2,11 @@ package kubernetes_kurtosis_backend

import (
"context"
"io"

"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec"
"io"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/nix_build_spec"
apiv1 "k8s.io/api/core/v1"

"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/engine_functions"
Expand Down Expand Up @@ -487,6 +489,11 @@ func (backend *KubernetesKurtosisBackend) BuildImage(ctx context.Context, imageN
return "", stacktrace.NewError("Building images isn't yet implemented in Kubernetes.")
}

func (backend *KubernetesKurtosisBackend) NixBuild(ctx context.Context, nixBuildSpec *nix_build_spec.NixBuildSpec) (string, error) {
// TODO IMPLEMENT
return "", stacktrace.NewError("Nix image building isn't yet implemented in Kubernetes.")
}

// ====================================================================================================
//
// Private Helper Functions
Expand Down
Expand Up @@ -3,6 +3,9 @@ package kubernetes_kurtosis_backend
import (
"context"
"fmt"
"net"
"time"

"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/consts"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/shared_helpers"
kubernetes_manager_consts "github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_manager/consts"
Expand All @@ -17,8 +20,6 @@ import (
apiv1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
applyconfigurationsv1 "k8s.io/client-go/applyconfigurations/core/v1"
"net"
"time"
)

const (
Expand Down Expand Up @@ -233,6 +234,7 @@ func (backend *KubernetesKurtosisBackend) CreateAPIContainer(

clusterRoleName := clusterRolesAttributes.GetName().GetString()
clusterRoleLabels := shared_helpers.GetStringMapFromLabelMap(clusterRolesAttributes.GetLabels())
// nolint: exhaustruct
clusterRolePolicyRules := []rbacv1.PolicyRule{
{
Verbs: []string{
Expand Down Expand Up @@ -292,6 +294,7 @@ func (backend *KubernetesKurtosisBackend) CreateAPIContainer(

clusterRoleBindingName := clusterRoleBindingsAttributes.GetName().GetString()
clusterRoleBindingsLabels := shared_helpers.GetStringMapFromLabelMap(clusterRoleBindingsAttributes.GetLabels())
// nolint: exhaustruct
clusterRoleBindingsSubjects := []rbacv1.Subject{
{
Kind: rbacv1.ServiceAccountKind,
Expand Down Expand Up @@ -335,6 +338,7 @@ func (backend *KubernetesKurtosisBackend) CreateAPIContainer(

roleName := rolesAttributes.GetName().GetString()
roleLabels := shared_helpers.GetStringMapFromLabelMap(rolesAttributes.GetLabels())
// nolint: exhaustruct
rolePolicyRules := []rbacv1.PolicyRule{
{
Verbs: []string{
Expand Down Expand Up @@ -394,6 +398,7 @@ func (backend *KubernetesKurtosisBackend) CreateAPIContainer(

roleBindingName := roleBindingsAttributes.GetName().GetString()
roleBindingsLabels := shared_helpers.GetStringMapFromLabelMap(roleBindingsAttributes.GetLabels())
// nolint: exhaustruct
roleBindingsSubjects := []rbacv1.Subject{
{
Kind: rbacv1.ServiceAccountKind,
Expand Down Expand Up @@ -1085,6 +1090,7 @@ func getApiContainerContainersAndVolumes(
}
containerEnvVars = append(containerEnvVars, ownNamespaceEnvVar)

// nolint: exhaustruct
containers := []apiv1.Container{
{
Name: kurtosisApiContainerContainerName,
Expand Down
Expand Up @@ -3,9 +3,10 @@ package user_services_functions
import (
"context"
"fmt"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service_user"
"strings"

"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/service_user"

"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/consts"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_kurtosis_backend/shared_helpers"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_impls/kubernetes/kubernetes_manager"
Expand Down Expand Up @@ -681,7 +682,7 @@ func getUserServicePodContainerSpecs(
Limits: resourceLimitsList,
Requests: resourceRequestsList,
}

// nolint: exhaustruct
containers := []apiv1.Container{
{
Name: userServiceContainerName,
Expand Down Expand Up @@ -871,6 +872,7 @@ func createRegisterUserServiceOperation(

// Kubernetes doesn't allow us to create services without any ports, so we need to set this to a notional value
// until the user calls StartService
// nolint: exhaustruct
notionalServicePorts := []apiv1.ServicePort{
{
Name: unboundPortName,
Expand Down
Expand Up @@ -2,11 +2,13 @@ package metrics_reporting

import (
"context"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec"
"io"
"time"

"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/nix_build_spec"

"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/api_container"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/compute_resources"
Expand Down Expand Up @@ -470,3 +472,7 @@ func (backend *MetricsReportingKurtosisBackend) GetAvailableCPUAndMemory(ctx con
func (backend *MetricsReportingKurtosisBackend) BuildImage(ctx context.Context, imageName string, imageBuildSpec *image_build_spec.ImageBuildSpec) (string, error) {
return backend.underlying.BuildImage(ctx, imageName, imageBuildSpec)
}

func (backend *MetricsReportingKurtosisBackend) NixBuild(ctx context.Context, nixBuildSpec *nix_build_spec.NixBuildSpec) (string, error) {
return backend.underlying.NixBuild(ctx, nixBuildSpec)
}
Expand Up @@ -2,11 +2,13 @@ package backend_interface

import (
"context"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec"
"io"
"time"

"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_build_spec"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/image_registry_spec"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/nix_build_spec"

"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/api_container"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/compute_resources"
"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/backend_interface/objects/enclave"
Expand Down Expand Up @@ -356,4 +358,6 @@ type KurtosisBackend interface {
// BuildImage builds a container image based on the [imageBuildSpec] with [imageName]
// Returns image architecture and if error occurred
BuildImage(ctx context.Context, imageName string, imageBuildSpec *image_build_spec.ImageBuildSpec) (string, error)

NixBuild(ctx context.Context, nixBuildSpec *nix_build_spec.NixBuildSpec) (string, error)
}

0 comments on commit 0eae9fc

Please sign in to comment.