Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

e2e: generate deployment yaml #189

Merged
merged 5 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
e2e: basic kube resources and parts
Signed-off-by: Paul Meyer <49727155+katexochen@users.noreply.github.com>
  • Loading branch information
katexochen committed Mar 4, 2024
commit 41acb073649f736601503d55ca0a9ca3414d7251
149 changes: 149 additions & 0 deletions e2e/internal/kuberesource/parts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package kuberesource

import (
"strconv"

applyappsv1 "k8s.io/client-go/applyconfigurations/apps/v1"
applycorev1 "k8s.io/client-go/applyconfigurations/core/v1"
)

// PortForwarderConfig wraps a PodApplyConfiguration for a port forwarder.
type PortForwarderConfig struct {
*applycorev1.PodApplyConfiguration
}

// PortForwarder constructs a port forwarder pod.
func PortForwarder(name, namespace string) *PortForwarderConfig {
name = "port-forwarder-" + name

p := Pod(name, namespace).
WithLabels(map[string]string{"app.kubernetes.io/name": name}).
WithSpec(PodSpec().
WithContainers(
Container().
WithName("port-forwarder").
WithImage("ghcr.io/edgelesssys/nunki/port-forwarder:latest").
WithCommand("/bin/bash", "-c", "echo Starting port-forward with socat; exec socat -d -d TCP-LISTEN:${LISTEN_PORT},fork TCP:${FORWARD_HOST}:${FORWARD_PORT}").
WithResources(ResourceRequirements().
WithMemoryLimitAndRequest(50),
),
),
)

return &PortForwarderConfig{p}
}

// WithListenPort sets the port to listen on.
func (p *PortForwarderConfig) WithListenPort(port int32) *PortForwarderConfig {
p.Spec.Containers[0].
WithPorts(
ContainerPort().
WithContainerPort(port),
).
WithEnv(
NewEnvVar("LISTEN_PORT", strconv.Itoa(int(port))),
)
return p
}

// WithForwardTarget sets the target host and port to forward to.
func (p *PortForwarderConfig) WithForwardTarget(host string, port int32) *PortForwarderConfig {
p.Spec.Containers[0].
WithEnv(
NewEnvVar("FORWARD_HOST", host),
NewEnvVar("FORWARD_PORT", strconv.Itoa(int(port))),
)
return p
}

// CoordinatorConfig wraps applyappsv1.DeploymentApplyConfiguration for a coordinator.
type CoordinatorConfig struct {
*applyappsv1.DeploymentApplyConfiguration
}

// Coordinator constructs a new CoordinatorConfig.
func Coordinator(namespace string) *CoordinatorConfig {
c := Deployment("coordinator", namespace).
WithSpec(DeploymentSpec().
WithReplicas(1).
WithSelector(LabelSelector().
WithMatchLabels(map[string]string{"app.kubernetes.io/name": "coordinator"}),
).
WithTemplate(PodTemplateSpec().
WithLabels(map[string]string{"app.kubernetes.io/name": "coordinator"}).
WithAnnotations(map[string]string{"nunki.edgeless.systems/pod-role": "coordinator"}).
WithSpec(PodSpec().
WithRuntimeClassName("kata-cc-isolation").
WithContainers(
Container().
WithName("coordinator").
WithImage("ghcr.io/edgelesssys/nunki/coordinator:latest").
WithEnv(
NewEnvVar("NUNKI_LOG_LEVEL", "debug"),
).
WithPorts(
ContainerPort().
WithName("userapi").
WithContainerPort(1313),
ContainerPort().
WithName("meshapi").
WithContainerPort(7777),
).
WithResources(ResourceRequirements().
WithMemoryLimitAndRequest(100),
),
),
),
),
)

return &CoordinatorConfig{c}
}

// WithImage sets the image of the coordinator.
func (c *CoordinatorConfig) WithImage(image string) *CoordinatorConfig {
c.Spec.Template.Spec.Containers[0].WithImage(image)
return c
}

// GetDeploymentConfig returns the DeploymentConfig of the coordinator.
func (c *CoordinatorConfig) GetDeploymentConfig() *DeploymentConfig {
return &DeploymentConfig{c.DeploymentApplyConfiguration}
}

// ServiceForDeployment creates a service for a deployment by exposing the configured ports
// of the deployment's first container.
func ServiceForDeployment(d *applyappsv1.DeploymentApplyConfiguration) *applycorev1.ServiceApplyConfiguration {
selector := d.Spec.Selector.MatchLabels
ports := d.Spec.Template.Spec.Containers[0].Ports

s := Service(*d.Name, *d.Namespace).
WithSpec(ServiceSpec().
WithSelector(selector),
)

for _, p := range ports {
s.Spec.WithPorts(
ServicePort().
WithName(*p.Name).
WithPort(*p.ContainerPort),
)
}

return s
}

// Initializer creates a new InitializerConfig.
func Initializer() *applycorev1.ContainerApplyConfiguration {
return applycorev1.Container().
WithName("initializer").
WithImage("ghcr.io/edgelesssys/nunki/initializer:latest").
WithResources(ResourceRequirements().
WithMemoryLimitAndRequest(50),
).
WithEnv(NewEnvVar("COORDINATOR_HOST", "coordinator")).
WithVolumeMounts(VolumeMount().
WithName("tls-certs").
WithMountPath("/tls-config"),
)
}
29 changes: 29 additions & 0 deletions e2e/internal/kuberesource/parts_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package kuberesource

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestNewPortForwarder(t *testing.T) {
require := require.New(t)

config := PortForwarder("coordinator", "default").
WithListenPort(1313).
WithForwardTarget("coordinator", 1313)

b, err := EncodeResources(config)
require.NoError(err)
t.Log("\n" + string(b))
}

func TestCoordinator(t *testing.T) {
require := require.New(t)

config := Coordinator("default")

b, err := EncodeResources(config)
require.NoError(err)
t.Log("\n" + string(b))
}
217 changes: 217 additions & 0 deletions e2e/internal/kuberesource/wrappers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
package kuberesource

import (
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"

applyappsv1 "k8s.io/client-go/applyconfigurations/apps/v1"
applycorev1 "k8s.io/client-go/applyconfigurations/core/v1"
applymetav1 "k8s.io/client-go/applyconfigurations/meta/v1"
)

// DeploymentConfig wraps applyappsv1.DeploymentApplyConfiguration.
type DeploymentConfig struct {
*applyappsv1.DeploymentApplyConfiguration
}

// Deployment creates a new DeploymentConfig.
func Deployment(name, namespace string) *DeploymentConfig {
return &DeploymentConfig{applyappsv1.Deployment(name, namespace)}
}

// DeploymentSpecConfig wraps applyappsv1.DeploymentSpecApplyConfiguration.
type DeploymentSpecConfig struct {
*applyappsv1.DeploymentSpecApplyConfiguration
}

// DeploymentSpec creates a new DeploymentSpecConfig.
func DeploymentSpec() *DeploymentSpecConfig {
return &DeploymentSpecConfig{applyappsv1.DeploymentSpec()}
}

// PodConfig wraps applyappsv1.PodApplyConfiguration.
type PodConfig struct {
*applycorev1.PodApplyConfiguration
}

// Pod creates a new PodConfig.
func Pod(name, namespace string) *PodConfig {
return &PodConfig{applycorev1.Pod(name, namespace)}
}

// LabelSelectorConfig wraps applymetav1.LabelSelectorApplyConfiguration.
type LabelSelectorConfig struct {
*applymetav1.LabelSelectorApplyConfiguration
}

// LabelSelector creates a new LabelSelectorConfig.
func LabelSelector() *LabelSelectorConfig {
return &LabelSelectorConfig{applymetav1.LabelSelector()}
}

// PodTemplateSpecConfig wraps applycorev1.PodTemplateSpecApplyConfiguration.
type PodTemplateSpecConfig struct {
*applycorev1.PodTemplateSpecApplyConfiguration
}

// PodTemplateSpec creates a new PodTemplateSpecConfig.
func PodTemplateSpec() *PodTemplateSpecConfig {
return &PodTemplateSpecConfig{applycorev1.PodTemplateSpec()}
}

// PodSpecConfig wraps applycorev1.PodSpecApplyConfiguration.
type PodSpecConfig struct {
*applycorev1.PodSpecApplyConfiguration
}

// PodSpec creates a new PodSpecConfig.
func PodSpec() *PodSpecConfig {
return &PodSpecConfig{applycorev1.PodSpec()}
}

// ContainerConfig wraps applycorev1.ContainerApplyConfiguration.
type ContainerConfig struct {
*applycorev1.ContainerApplyConfiguration
}

// Container creates a new ContainerConfig.
func Container() *ContainerConfig {
return &ContainerConfig{applycorev1.Container()}
}

// EnvVarConfig wraps applycorev1.EnvVarApplyConfiguration.
type EnvVarConfig struct {
*applycorev1.EnvVarApplyConfiguration
}

// EnvVar creates a new EnvVarConfig.
func EnvVar() *EnvVarConfig {
return &EnvVarConfig{applycorev1.EnvVar()}
}

// NewEnvVar creates a new EnvVarApplyConfiguration from name and value.
func NewEnvVar(name, value string) *applycorev1.EnvVarApplyConfiguration {
return applycorev1.EnvVar().WithName(name).WithValue(value)
}

// VolumeMountConfig wraps applycorev1.VolumeMountApplyConfiguration.
type VolumeMountConfig struct {
*applycorev1.VolumeMountApplyConfiguration
}

// VolumeMount creates a new VolumeMountConfig.
func VolumeMount() *VolumeMountConfig {
return &VolumeMountConfig{applycorev1.VolumeMount()}
}

// ResourceRequirementsConfig wraps applycorev1.ResourceRequirementsApplyConfiguration.
type ResourceRequirementsConfig struct {
*applycorev1.ResourceRequirementsApplyConfiguration
}

// ResourceRequirements creates a new ResourceRequirementsConfig.
func ResourceRequirements() *ResourceRequirementsConfig {
return &ResourceRequirementsConfig{applycorev1.ResourceRequirements()}
}

// WithMemoryLimitAndRequest sets the memory limit and request of the ResourceRequirements.
func (r *ResourceRequirementsConfig) WithMemoryLimitAndRequest(memoryMi int64) *applycorev1.ResourceRequirementsApplyConfiguration {
return r.
WithRequests(corev1.ResourceList{
corev1.ResourceMemory: fromPtr(resource.NewQuantity(memoryMi*1024*1024, resource.BinarySI)),
}).
WithLimits(corev1.ResourceList{
corev1.ResourceMemory: fromPtr(resource.NewQuantity(memoryMi*1024*1024, resource.BinarySI)),
})
}

// WithCPURequest sets the CPU request of the ResourceRequirements.
func (r *ResourceRequirementsConfig) WithCPURequest(cpuM int64) *applycorev1.ResourceRequirementsApplyConfiguration {
return r.WithRequests(corev1.ResourceList{
corev1.ResourceCPU: fromPtr(resource.NewMilliQuantity(cpuM, resource.DecimalSI)),
// Don't set CPU limits, see https://home.robusta.dev/blog/stop-using-cpu-limits
})
}

// VolumeConfig wraps applycorev1.VolumeApplyConfiguration.
type VolumeConfig struct {
*applycorev1.VolumeApplyConfiguration
}

// Volume creates a new VolumeConfig.
func Volume() *VolumeConfig {
return &VolumeConfig{applycorev1.Volume()}
}

// EmptyDirVolumeSourceConfig wraps applycorev1.EmptyDirVolumeSourceApplyConfiguration.
type EmptyDirVolumeSourceConfig struct {
*applycorev1.EmptyDirVolumeSourceApplyConfiguration
}

// EmptyDirVolumeSource creates a new EmptyDirVolumeSourceConfig.
func EmptyDirVolumeSource() *EmptyDirVolumeSourceConfig {
return &EmptyDirVolumeSourceConfig{applycorev1.EmptyDirVolumeSource()}
}

// Inner returns the inner applycorev1.EmptyDirVolumeSourceApplyConfiguration.
func (e *EmptyDirVolumeSourceConfig) Inner() *applycorev1.EmptyDirVolumeSourceApplyConfiguration {
return e.EmptyDirVolumeSourceApplyConfiguration
}

// ContainerPortConfig wraps applycorev1.ContainerPortApplyConfiguration.
type ContainerPortConfig struct {
*applycorev1.ContainerPortApplyConfiguration
}

// ContainerPort creates a new ContainerPortConfig.
func ContainerPort() *ContainerPortConfig {
return &ContainerPortConfig{applycorev1.ContainerPort()}
}

// ServiceConfig wraps applycorev1.ServiceApplyConfiguration.
type ServiceConfig struct {
*applycorev1.ServiceApplyConfiguration
}

// Service creates a new ServiceConfig.
func Service(name, namespace string) *ServiceConfig {
return &ServiceConfig{applycorev1.Service(name, namespace)}
}

// ServiceSpecConfig wraps applycorev1.ServiceSpecApplyConfiguration.
type ServiceSpecConfig struct {
*applycorev1.ServiceSpecApplyConfiguration
}

// ServiceSpec creates a new ServiceSpecConfig.
func ServiceSpec() *ServiceSpecConfig {
return &ServiceSpecConfig{applycorev1.ServiceSpec()}
}

// ServicePortConfig wraps applycorev1.ServicePortApplyConfiguration.
type ServicePortConfig struct {
*applycorev1.ServicePortApplyConfiguration
}

// ServicePort creates a new ServicePortConfig.
func ServicePort() *ServicePortConfig {
return &ServicePortConfig{applycorev1.ServicePort()}
}

// NamespaceConfig wraps applycorev1.NamespaceApplyConfiguration.
type NamespaceConfig struct {
*applycorev1.NamespaceApplyConfiguration
}

// Namespace creates a new NamespaceConfig.
func Namespace(name string) *applycorev1.NamespaceApplyConfiguration {
return applycorev1.Namespace(name)
}

func fromPtr[T any](v *T) T {
if v != nil {
return *v
}
var zero T
return zero
}
Loading