// Copyright 2023 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package spec contains Kubernetes object specifications for gVisor setup.
package spec

import (
	"fmt"
	"maps"
	"slices"
	"strings"

	"google.golang.org/protobuf/proto"
	appsv1 "k8s.io/api/apps/v1"
	v13 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	"k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
	// SystemNamespace is the name of the Kubernetes system namespace.
	SystemNamespace = "kube-system"
	// PauseContainerImage is the name of a container image that does nothing.
	PauseContainerImage = "gcr.io/google-containers/pause"
	// gvisorNodepoolKey the key for the label given to GKE Sandbox nodepools.
	gvisorNodepoolKey = "sandbox.gke.io/runtime"
	// gvisorRuntimeClass the runtimeClassName used for GKE Sandbox pods.
	gvisorRuntimeClass = "gvisor"
)

var (
	// GKESandboxNodeSelector selects GKE Sandbox nodes on GKE.
	GKESandboxNodeSelector = map[string]string{gvisorNodepoolKey: gvisorRuntimeClass}
)

// InstallOptions is the set of options to install runsc.
type InstallOptions struct {
	DaemonSetNamespace  string
	DaemonSetName       string
	Labels              map[string]string
	NodeSelector        map[string]string
	PauseContainerImage string
	ExtraFlags          map[string]string
}

// RunscInstallDaemonSet returns a DaemonSet spec that installs runsc in
// Kubernetes.
func RunscInstallDaemonSet(image string, options InstallOptions) *appsv1.DaemonSet {
	hpType := v13.HostPathDirectory
	runscConfigFlagOverride := ""
	if len(options.ExtraFlags) > 0 {
		var flagStrings []string
		for _, k := range slices.Sorted(maps.Keys(options.ExtraFlags)) {
			flagStrings = append(flagStrings, fmt.Sprintf("--%s=%s", k, options.ExtraFlags[k]))
		}
		runscConfigFlagOverride = strings.Join(flagStrings, " ")
	}
	var envVars []v13.EnvVar
	if runscConfigFlagOverride != "" {
		envVars = append(envVars, v13.EnvVar{
			Name:  "RUNSC_CONFIG_FLAG_OVERRIDE",
			Value: runscConfigFlagOverride,
		})
	}
	return &appsv1.DaemonSet{
		TypeMeta: v1.TypeMeta{
			Kind:       "DaemonSet",
			APIVersion: "apps/v1",
		},
		ObjectMeta: v1.ObjectMeta{
			Name:      options.DaemonSetName,
			Namespace: options.DaemonSetNamespace,
		},
		Spec: appsv1.DaemonSetSpec{
			Selector: &v1.LabelSelector{
				MatchLabels: options.Labels,
			},
			UpdateStrategy: appsv1.DaemonSetUpdateStrategy{
				Type: appsv1.RollingUpdateDaemonSetStrategyType,
			},
			Template: v13.PodTemplateSpec{
				ObjectMeta: v1.ObjectMeta{
					Labels: options.Labels,
				},
				Spec: v13.PodSpec{
					Tolerations: []v13.Toleration{
						{
							Operator: v13.TolerationOpExists,
						},
					},
					HostPID: true,
					InitContainers: []v13.Container{
						{
							Name:  options.DaemonSetName,
							Image: image,
							VolumeMounts: []v13.VolumeMount{
								{
									Name:      "host",
									MountPath: "/host",
								},
							},
							Resources: v13.ResourceRequirements{
								Requests: v13.ResourceList{
									v13.ResourceCPU:    resource.MustParse("5m"),
									v13.ResourceMemory: resource.MustParse("5Mi"),
								},
							},
							Env: envVars,
							SecurityContext: &v13.SecurityContext{
								Capabilities: &v13.Capabilities{
									Add: []v13.Capability{"CAP_SYS_ADMIN"},
								},
								Privileged: proto.Bool(true),
							},
						},
					},
					Containers: []v13.Container{
						{
							Name:  "pause",
							Image: options.PauseContainerImage,
						},
					},
					NodeSelector: options.NodeSelector,
					Volumes: []v13.Volume{
						{
							Name: "host",
							VolumeSource: v13.VolumeSource{
								HostPath: &v13.HostPathVolumeSource{
									Path: "/",
									Type: &hpType,
								},
							},
						},
					},
				},
			},
		},
	}
}
