zfs-localpv/tests/container/container.go

444 lines
10 KiB
Go
Raw Normal View History

/*
Copyright 2019 The OpenEBS 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 container
import (
"github.com/openebs/zfs-localpv/pkg/common/errors"
corev1 "k8s.io/api/core/v1"
)
type container struct {
corev1.Container // kubernetes container type
}
// OptionFunc is a typed function that abstracts anykind of operation
// against the provided container instance
//
// This is the basic building block to create functional operations
// against the container instance
type OptionFunc func(*container)
// Predicate abstracts conditional logic w.r.t the container instance
//
// NOTE:
// Predicate is a functional approach versus traditional approach to mix
// conditions such as *if-else* within blocks of business logic
//
// NOTE:
// Predicate approach enables clear separation of conditionals from
// imperatives i.e. actions that form the business logic
type Predicate func(*container) (nameOrMsg string, ok bool)
// predicateFailedError returns the provided predicate as an error
func predicateFailedError(message string) error {
return errors.Errorf("predicatefailed: %s", message)
}
var (
errorvalidationFailed = errors.New("container validation failed")
)
// asContainer transforms this container instance into corresponding kubernetes
// container type
func (c *container) asContainer() corev1.Container {
return corev1.Container{
Name: c.Name,
Image: c.Image,
Command: c.Command,
Args: c.Args,
WorkingDir: c.WorkingDir,
Ports: c.Ports,
EnvFrom: c.EnvFrom,
Env: c.Env,
Resources: c.Resources,
VolumeMounts: c.VolumeMounts,
VolumeDevices: c.VolumeDevices,
LivenessProbe: c.LivenessProbe,
ReadinessProbe: c.ReadinessProbe,
Lifecycle: c.Lifecycle,
TerminationMessagePath: c.TerminationMessagePath,
TerminationMessagePolicy: c.TerminationMessagePolicy,
ImagePullPolicy: c.ImagePullPolicy,
SecurityContext: c.SecurityContext,
Stdin: c.Stdin,
StdinOnce: c.StdinOnce,
TTY: c.TTY,
}
}
// New returns a new kubernetes container
func New(opts ...OptionFunc) corev1.Container {
c := &container{}
for _, o := range opts {
o(c)
}
return c.asContainer()
}
// Builder provides utilities required to build a kubernetes container type
type Builder struct {
con *container // container instance
checks []Predicate // validations to be done while building the container instance
errors []error // errors found while building the container instance
}
// NewBuilder returns a new instance of builder
func NewBuilder() *Builder {
return &Builder{
con: &container{},
}
}
// validate will run checks against container instance
func (b *Builder) validate() error {
for _, c := range b.checks {
if m, ok := c(b.con); !ok {
b.errors = append(b.errors, predicateFailedError(m))
}
}
if len(b.errors) == 0 {
return nil
}
return errorvalidationFailed
}
// Build returns the final kubernetes container
func (b *Builder) Build() (corev1.Container, error) {
err := b.validate()
if err != nil {
return corev1.Container{}, err
}
return b.con.asContainer(), nil
}
// AddCheck adds the predicate as a condition to be validated against the
// container instance
func (b *Builder) AddCheck(p Predicate) *Builder {
b.checks = append(b.checks, p)
return b
}
// AddChecks adds the provided predicates as conditions to be validated against
// the container instance
func (b *Builder) AddChecks(p []Predicate) *Builder {
for _, check := range p {
b.AddCheck(check)
}
return b
}
// WithName sets the name of the container
func (b *Builder) WithName(name string) *Builder {
if len(name) == 0 {
b.errors = append(
b.errors,
errors.New("failed to build container object: missing name"),
)
return b
}
WithName(name)(b.con)
return b
}
// WithName sets the name of the container
func WithName(name string) OptionFunc {
return func(c *container) {
c.Name = name
}
}
// WithImage sets the image of the container
func (b *Builder) WithImage(img string) *Builder {
if len(img) == 0 {
b.errors = append(
b.errors,
errors.New("failed to build container object: missing image"),
)
return b
}
WithImage(img)(b.con)
return b
}
// WithImage sets the image of the container
func WithImage(img string) OptionFunc {
return func(c *container) {
c.Image = img
}
}
// WithCommandNew sets the command of the container
func (b *Builder) WithCommandNew(cmd []string) *Builder {
if cmd == nil {
b.errors = append(
b.errors,
errors.New("failed to build container object: nil command"),
)
return b
}
if len(cmd) == 0 {
b.errors = append(
b.errors,
errors.New("failed to build container object: missing command"),
)
return b
}
newcmd := []string{}
newcmd = append(newcmd, cmd...)
b.con.Command = newcmd
return b
}
// WithArgumentsNew sets the command arguments of the container
func (b *Builder) WithArgumentsNew(args []string) *Builder {
if args == nil {
b.errors = append(
b.errors,
errors.New("failed to build container object: nil arguments"),
)
return b
}
if len(args) == 0 {
b.errors = append(
b.errors,
errors.New("failed to build container object: missing arguments"),
)
return b
}
newargs := []string{}
newargs = append(newargs, args...)
b.con.Args = newargs
return b
}
// WithVolumeMountsNew sets the command arguments of the container
func (b *Builder) WithVolumeMountsNew(volumeMounts []corev1.VolumeMount) *Builder {
if volumeMounts == nil {
b.errors = append(
b.errors,
errors.New("failed to build container object: nil volumemounts"),
)
return b
}
if len(volumeMounts) == 0 {
b.errors = append(
b.errors,
errors.New("failed to build container object: missing volumemounts"),
)
return b
}
newvolumeMounts := []corev1.VolumeMount{}
newvolumeMounts = append(newvolumeMounts, volumeMounts...)
b.con.VolumeMounts = newvolumeMounts
return b
}
// WithVolumeDevicesNew sets the command arguments of the container
func (b *Builder) WithVolumeDevicesNew(volumeDevices []corev1.VolumeDevice) *Builder {
if volumeDevices == nil {
b.errors = append(
b.errors,
errors.New("failed to build container object: nil volumeDevices"),
)
return b
}
if len(volumeDevices) == 0 {
b.errors = append(
b.errors,
errors.New("failed to build container object: missing volumeDevices"),
)
return b
}
newvolumeDevices := []corev1.VolumeDevice{}
newvolumeDevices = append(newvolumeDevices, volumeDevices...)
b.con.VolumeDevices = newvolumeDevices
return b
}
// WithImagePullPolicy sets the image pull policy of the container
func (b *Builder) WithImagePullPolicy(policy corev1.PullPolicy) *Builder {
if len(policy) == 0 {
b.errors = append(
b.errors,
errors.New(
"failed to build container object: missing imagepullpolicy",
),
)
return b
}
b.con.ImagePullPolicy = policy
return b
}
// WithPrivilegedSecurityContext sets securitycontext of the container
func (b *Builder) WithPrivilegedSecurityContext(privileged *bool) *Builder {
if privileged == nil {
b.errors = append(
b.errors,
errors.New(
"failed to build container object: missing securitycontext",
),
)
return b
}
newprivileged := *privileged
newsecuritycontext := &corev1.SecurityContext{
Privileged: &newprivileged,
}
b.con.SecurityContext = newsecuritycontext
return b
}
// WithResources sets resources of the container
func (b *Builder) WithResources(
resources *corev1.ResourceRequirements,
) *Builder {
if resources == nil {
b.errors = append(
b.errors,
errors.New("failed to build container object: missing resources"),
)
return b
}
newresources := *resources
b.con.Resources = newresources
return b
}
// WithResourcesByValue sets resources of the container
func (b *Builder) WithResourcesByValue(resources corev1.ResourceRequirements) *Builder {
b.con.Resources = resources
return b
}
// WithPortsNew sets ports of the container
func (b *Builder) WithPortsNew(ports []corev1.ContainerPort) *Builder {
if ports == nil {
b.errors = append(
b.errors,
errors.New("failed to build container object: nil ports"),
)
return b
}
if len(ports) == 0 {
b.errors = append(
b.errors,
errors.New("failed to build container object: missing ports"),
)
return b
}
newports := []corev1.ContainerPort{}
newports = append(newports, ports...)
b.con.Ports = newports
return b
}
// WithEnvsNew sets the envs of the container
func (b *Builder) WithEnvsNew(envs []corev1.EnvVar) *Builder {
if envs == nil {
b.errors = append(
b.errors,
errors.New("failed to build container object: nil envs"),
)
return b
}
if len(envs) == 0 {
b.errors = append(
b.errors,
errors.New("failed to build container object: missing envs"),
)
return b
}
newenvs := []corev1.EnvVar{}
newenvs = append(newenvs, envs...)
b.con.Env = newenvs
return b
}
// WithEnvs sets the envs of the container
func (b *Builder) WithEnvs(envs []corev1.EnvVar) *Builder {
if envs == nil {
b.errors = append(
b.errors,
errors.New("failed to build container object: nil envs"),
)
return b
}
if len(envs) == 0 {
b.errors = append(
b.errors,
errors.New("failed to build container object: missing envs"),
)
return b
}
if b.con.Env == nil {
b.WithEnvsNew(envs)
return b
}
b.con.Env = append(b.con.Env, envs...)
return b
}
// WithLivenessProbe sets the liveness probe of the container
func (b *Builder) WithLivenessProbe(liveness *corev1.Probe) *Builder {
if liveness == nil {
b.errors = append(
b.errors,
errors.New("failed to build container object: nil liveness probe"),
)
return b
}
b.con.LivenessProbe = liveness
return b
}
// WithLifeCycle sets the life cycle of the container
func (b *Builder) WithLifeCycle(lc *corev1.Lifecycle) *Builder {
if lc == nil {
b.errors = append(
b.errors,
errors.New("failed to build container object: nil lifecycle"),
)
return b
}
b.con.Lifecycle = lc
return b
}