Skip to content

Commit

Permalink
feat: single sidecars exe and versioned templates (#745)
Browse files Browse the repository at this point in the history
  • Loading branch information
olevski authored Oct 23, 2024
1 parent d50401c commit 9494c23
Show file tree
Hide file tree
Showing 54 changed files with 2,135 additions and 1,854 deletions.
13 changes: 0 additions & 13 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,3 @@ jobs:
make test-e2e
push: never
skipContainerUserIdUpdate: false

run-authproxy-unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Tests
uses: devcontainers/[email protected]
with:
runCmd: |
cd authproxy
make test
push: never
skipContainerUserIdUpdate: false
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@ COPY go.sum go.sum
RUN go mod download

# Copy the go source
COPY cmd/main.go cmd/main.go
COPY cmd/amalthea/main.go cmd/amalthea/main.go
COPY api/ api/
COPY internal/controller/ internal/controller/
COPY internal/ internal/

# Build
# the GOARCH has not a default value to allow the binary be built according to the host where the command
# was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO
# the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore,
# by leaving it empty we can ensure that the container and binary shipped on it will have the same platform.
RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager cmd/main.go
RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager cmd/amalthea/main.go

# Use distroless as minimal base image to package the manager binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
Expand Down
18 changes: 16 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ IMAGE_TAG_BASE ?= renku/amalthea
# BUNDLE_IMG defines the image:tag used for the bundle.
# You can use it as an arg. (E.g make bundle-build BUNDLE_IMG=<some-registry>/<project-name-bundle>:<tag>)
BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION)
SIDECARS_IMG ?= $(IMAGE_TAG_BASE)-sidecars:v$(VERSION)

# BUNDLE_GEN_FLAGS are the flags passed to the operator-sdk generate bundle command
BUNDLE_GEN_FLAGS ?= -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS)
Expand Down Expand Up @@ -89,6 +90,8 @@ all: build
# More info on the awk command:
# http://linuxcommand.org/lc3_adv_awk.php

HELM_CRD_TEMPLATE ?= helm-chart/amalthea-sessions/templates/amaltheasession-crd.yaml

.PHONY: help
help: ## Display this help.
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
Expand All @@ -98,6 +101,9 @@ help: ## Display this help.
.PHONY: manifests
manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
$(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
echo "{{- if .Values.deployCrd -}}" > $(HELM_CRD_TEMPLATE)
echo "# This manifest is auto-generated from the makefile do not edit manually." >> $(HELM_CRD_TEMPLATE)
cat config/crd/bases/*yaml >> $(HELM_CRD_TEMPLATE)

.PHONY: generate
generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
Expand Down Expand Up @@ -140,11 +146,19 @@ lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes

.PHONY: build
build: manifests generate fmt vet ## Build manager binary.
go build -o bin/manager cmd/main.go
go build -o bin/manager cmd/amalthea/main.go

.PHONY: build-sidecars
build-sidecars: fmt vet
go build -o bin/sidecars cmd/sidecars/main.go

.PHONY: build-sidecars
docker-build-sidecars: build-sidecars
$(CONTAINER_TOOL) build -t ${SIDECARS_IMG} -f sidecars.Dockerfile .

.PHONY: run
run: manifests generate fmt vet ## Run a controller from your host.
go run ./cmd/main.go
go run ./cmd/amalthea/main.go

# If you wish to build the manager image targeting other platforms you can use the --platform flag.
# (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it.
Expand Down
145 changes: 15 additions & 130 deletions api/v1alpha1/amaltheasession_children.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"fmt"
"net/url"
"os"
"strings"
"time"

appsv1 "k8s.io/api/apps/v1"
Expand All @@ -29,16 +28,14 @@ const servicePort int32 = 80
const sessionVolumeName string = prefix + "volume"
const shmVolumeName string = prefix + "dev-shm"
const authProxyPort int32 = 65535
const oauth2ProxyImage = "bitnami/oauth2-proxy:7.6.0"
const authProxyImage = "renku/authproxy:0.0.1-test-1"

var rcloneStorageClass string = getStorageClass()
var rcloneDefaultStorage resource.Quantity = resource.MustParse("1Gi")

const rcloneStorageSecretNameAnnotation = "csi-rclone.dev/secretName"

// StatefulSet returns a AmaltheaSession StatefulSet object
func (cr *AmaltheaSession) StatefulSet() appsv1.StatefulSet {
func (cr *AmaltheaSession) StatefulSet() (appsv1.StatefulSet, error) {
labels := labelsForAmaltheaSession(cr.Name)
replicas := int32(1)
if cr.Spec.Hibernated {
Expand Down Expand Up @@ -104,9 +101,13 @@ func (cr *AmaltheaSession) StatefulSet() appsv1.StatefulSet {
initContainers := []v1.Container{}

if len(cr.Spec.CodeRepositories) > 0 {
gitCloneContainers, gitCloneVols := cr.initClones()
initContainers = append(initContainers, gitCloneContainers...)
volumes = append(volumes, gitCloneVols...)
templateFunc, err := initCloneTemplate.GetFunc(cr.Spec.Sidecars.Image)
if err != nil {
return appsv1.StatefulSet{}, err
}
cloneManifests := templateFunc(cr)
initContainers = append(initContainers, cloneManifests.Containers...)
volumes = append(volumes, cloneManifests.Volumes...)
}

initContainers = append(initContainers, cr.Spec.ExtraInitContainers...)
Expand Down Expand Up @@ -141,84 +142,13 @@ func (cr *AmaltheaSession) StatefulSet() appsv1.StatefulSet {
containers := []v1.Container{sessionContainer}
containers = append(containers, cr.Spec.ExtraContainers...)

if auth := cr.Spec.Authentication; auth != nil && auth.Enabled {
extraAuthMounts := []v1.VolumeMount{}
if len(auth.ExtraVolumeMounts) > 0 {
extraAuthMounts = auth.ExtraVolumeMounts
}
volumes = append(volumes, v1.Volume{
Name: "proxy-configuration-secret",
VolumeSource: v1.VolumeSource{
Secret: &v1.SecretVolumeSource{
SecretName: auth.SecretRef.Name,
Optional: ptr.To(false),
},
},
})

if auth.Type == Oidc {
sessionURL := cr.sessionLocalhostURL().String()
if !strings.HasSuffix(sessionURL, "/") {
// NOTE: If the url does not end with "/" then the oauth2proxy proxies only the exact path
// and does not proxy subpaths
sessionURL += "/"
}
authContainer := v1.Container{
Image: oauth2ProxyImage,
Name: "oauth2-proxy",
SecurityContext: &v1.SecurityContext{
AllowPrivilegeEscalation: ptr.To(false),
RunAsNonRoot: ptr.To(true),
},
Args: []string{
fmt.Sprintf("--upstream=%s", sessionURL),
fmt.Sprintf("--http-address=:%d", authProxyPort),
"--silence-ping-logging",
"--config=/etc/oauth2-proxy/" + auth.SecretRef.Key,
},
VolumeMounts: append(
[]v1.VolumeMount{
{
Name: "proxy-configuration-secret",
MountPath: "/etc/oauth2-proxy",
},
},
extraAuthMounts...,
),
}

containers = append(containers, authContainer)
} else if auth.Type == Token {
authContainer := v1.Container{
Image: authProxyImage,
Name: "authproxy",
SecurityContext: &v1.SecurityContext{
AllowPrivilegeEscalation: ptr.To(false),
RunAsNonRoot: ptr.To(true),
RunAsUser: ptr.To(int64(1000)),
RunAsGroup: ptr.To(int64(1000)),
},
Args: []string{"serve", "--config", "/etc/authproxy/" + auth.SecretRef.Key},
Env: []v1.EnvVar{
{Name: "AUTHPROXY_PORT", Value: fmt.Sprintf("%d", authProxyPort)},
// NOTE: The url for the remote has to not have a path at all, if it does, then the path
// in the url is appended to any path that is already there when the request comes in.
{Name: "AUTHPROXY_REMOTE", Value: fmt.Sprintf("http://127.0.0.1:%d", cr.Spec.Session.Port)},
},
VolumeMounts: append(
[]v1.VolumeMount{
{
Name: "proxy-configuration-secret",
MountPath: "/etc/authproxy",
},
},
extraAuthMounts...,
),
}

containers = append(containers, authContainer)
}
authTemplateFn, err := authTemplate.GetFunc(cr.Spec.Sidecars.Image)
if err != nil {
return appsv1.StatefulSet{}, err
}
authManifests := authTemplateFn(cr)
containers = append(containers, authManifests.Containers...)
volumes = append(volumes, authManifests.Volumes...)

sts := appsv1.StatefulSet{
ObjectMeta: metav1.ObjectMeta{
Expand Down Expand Up @@ -249,7 +179,7 @@ func (cr *AmaltheaSession) StatefulSet() appsv1.StatefulSet {
},
},
}
return sts
return sts, nil
}

// Service returns a AmaltheaSession Service object
Expand Down Expand Up @@ -407,51 +337,6 @@ func (cr *AmaltheaSession) Pod(ctx context.Context, clnt client.Client) (*v1.Pod
return &pod, err
}

// Generates the init containers that clones the specified Git repositories
func (cr *AmaltheaSession) initClones() ([]v1.Container, []v1.Volume) {
envVars := []v1.EnvVar{}
volMounts := []v1.VolumeMount{{Name: sessionVolumeName, MountPath: cr.Spec.Session.Storage.MountPath}}
vols := []v1.Volume{}
containers := []v1.Container{}

for irepo, repo := range cr.Spec.CodeRepositories {
args := []string{"clone", "--strategy", "notifexist", "--remote", repo.Remote, "--path", cr.Spec.Session.Storage.MountPath + "/" + repo.ClonePath}

if repo.CloningConfigSecretRef != nil {
secretVolName := fmt.Sprintf("git-clone-cred-volume-%d", irepo)
secretMountPath := "/git-clone-secrets"
secretFilePath := fmt.Sprintf("%s/%s", secretMountPath, repo.CloningConfigSecretRef.Key)
vols = append(
vols,
v1.Volume{
Name: secretVolName,
VolumeSource: v1.VolumeSource{Secret: &v1.SecretVolumeSource{SecretName: repo.CloningConfigSecretRef.Name}},
},
)
volMounts = append(volMounts, v1.VolumeMount{Name: secretVolName, MountPath: secretMountPath})

args = append(args, []string{"--config", secretFilePath}...)
}

if repo.Revision != "" {
args = append(args, []string{"--revision", repo.Revision}...)
}

gitCloneContainerName := fmt.Sprintf("git-clone-%d", irepo)
containers = append(containers, v1.Container{
Name: gitCloneContainerName,
Image: "renku/cloner:0.0.1",
VolumeMounts: volMounts,
WorkingDir: cr.Spec.Session.Storage.MountPath,
Env: envVars,
SecurityContext: &v1.SecurityContext{RunAsUser: &cr.Spec.Session.RunAsUser, RunAsGroup: &cr.Spec.Session.RunAsGroup},
Args: args,
})
}

return containers, vols
}

// Returns the list of all the secrets used in this CR
func (cr *AmaltheaSession) AdoptedSecrets() v1.SecretList {
secrets := v1.SecretList{}
Expand Down
12 changes: 12 additions & 0 deletions api/v1alpha1/amaltheasession_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ type AmaltheaSessionSpec struct {
// Passed right through to the Statefulset used for the session.
// +optional
Tolerations []v1.Toleration `json:"tolerations,omitempty"`

//Control over the sidecars and init containers Amalthea adds to each session
Sidecars Sidecars `json:"sidecars,omitempty"`
}

type Session struct {
Expand Down Expand Up @@ -416,3 +419,12 @@ func (a *AmaltheaSession) GetURL() *url.URL {
}
return &sessionURL
}

type Sidecars struct {
// The docker image with the sidecars CLI.
// The containers that are templated are decided based on the image version tag, falling back
// on the latest templates for the latest version. Using a non-official image here is strongly
// discouraged, if you must then you should use the same semver tags as the official image you
// started with or based your custom image on.
Image string `json:"image,omitempty"`
}
Loading

0 comments on commit 9494c23

Please sign in to comment.