Move packages to proejct/pkg to follow go project folder structure convention (#1190)
This commit is contained in:
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
Copyright 2016 The Fission 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 util
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/imdario/mergo"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
// MergeContainerSpecs merges container specs using a predefined order.
|
||||
//
|
||||
// The order of the arguments indicates which spec has precedence (lower index takes precedence over higher indexes).
|
||||
// Slices and maps are merged; other fields are set only if they are a zero value.
|
||||
func MergeContainerSpecs(specs ...*apiv1.Container) apiv1.Container {
|
||||
result := &apiv1.Container{}
|
||||
for _, spec := range specs {
|
||||
if spec == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
err := mergo.Merge(result, spec)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
return *result
|
||||
}
|
||||
|
||||
// mergeContainer is a specialized implementation of MergeContainerSpecs
|
||||
func mergeContainer(deployContainer *apiv1.Container, containerSpec apiv1.Container) error {
|
||||
|
||||
if &containerSpec == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if deployContainer.Name == containerSpec.Name {
|
||||
volMap := make(map[string]*apiv1.VolumeMount)
|
||||
for _, vol := range deployContainer.VolumeMounts {
|
||||
volMap[vol.Name] = &vol
|
||||
}
|
||||
for _, specVol := range containerSpec.VolumeMounts {
|
||||
_, ok := volMap[specVol.Name]
|
||||
if ok {
|
||||
return errors.New("Duplicate volume name found in the spec")
|
||||
} else {
|
||||
deployContainer.VolumeMounts = append(deployContainer.VolumeMounts, specVol)
|
||||
}
|
||||
}
|
||||
deployContainer.Env = append(deployContainer.Env, containerSpec.Env...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func MergePodSpec(srcPodSpec *apiv1.PodSpec, targetPodSpec *apiv1.PodSpec) error {
|
||||
if &targetPodSpec == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var multierr *multierror.Error
|
||||
|
||||
// Get item from spec, if they exist in deployment - merge, else append
|
||||
// Same pattern for all lists (Mergo can not handle lists)
|
||||
// At some point this is better done with generics/reflection?
|
||||
err := mergeContainerLists(srcPodSpec, targetPodSpec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = mergeInitContainerList(srcPodSpec, targetPodSpec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// For volumes - if duplicate exist, throw error
|
||||
err = mergeVolumeLists(srcPodSpec, targetPodSpec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if targetPodSpec.NodeName != "" {
|
||||
srcPodSpec.NodeName = targetPodSpec.NodeName
|
||||
}
|
||||
|
||||
if targetPodSpec.Subdomain != "" {
|
||||
srcPodSpec.Subdomain = targetPodSpec.Subdomain
|
||||
}
|
||||
|
||||
if targetPodSpec.SchedulerName != "" {
|
||||
srcPodSpec.SchedulerName = targetPodSpec.SchedulerName
|
||||
}
|
||||
|
||||
if targetPodSpec.PriorityClassName != "" {
|
||||
srcPodSpec.PriorityClassName = targetPodSpec.PriorityClassName
|
||||
}
|
||||
|
||||
if targetPodSpec.TerminationGracePeriodSeconds != nil {
|
||||
srcPodSpec.TerminationGracePeriodSeconds = targetPodSpec.TerminationGracePeriodSeconds
|
||||
}
|
||||
|
||||
//TODO - Security context should be merged instead of overriding.
|
||||
if targetPodSpec.SecurityContext != nil {
|
||||
srcPodSpec.SecurityContext = targetPodSpec.SecurityContext
|
||||
}
|
||||
|
||||
//TODO - Affinity should be merged instead of overriding.
|
||||
if targetPodSpec.Affinity != nil {
|
||||
srcPodSpec.Affinity = targetPodSpec.Affinity
|
||||
}
|
||||
|
||||
if targetPodSpec.Hostname != "" {
|
||||
srcPodSpec.Hostname = targetPodSpec.Hostname
|
||||
}
|
||||
|
||||
for _, obj := range targetPodSpec.ImagePullSecrets {
|
||||
srcPodSpec.ImagePullSecrets = append(srcPodSpec.ImagePullSecrets, obj)
|
||||
}
|
||||
|
||||
for _, obj := range targetPodSpec.Tolerations {
|
||||
srcPodSpec.Tolerations = append(srcPodSpec.Tolerations, obj)
|
||||
}
|
||||
|
||||
for _, obj := range targetPodSpec.HostAliases {
|
||||
srcPodSpec.HostAliases = append(srcPodSpec.HostAliases, obj)
|
||||
}
|
||||
|
||||
err = mergo.Merge(&srcPodSpec.NodeSelector, targetPodSpec.NodeSelector)
|
||||
if err != nil {
|
||||
multierr = multierror.Append(multierr, err)
|
||||
}
|
||||
|
||||
return multierr.ErrorOrNil()
|
||||
}
|
||||
|
||||
func mergeContainerLists(srcPodSpec *apiv1.PodSpec, targetPodSpec *apiv1.PodSpec) error {
|
||||
targetSpecContainers := targetPodSpec.Containers
|
||||
targetContainers := make(map[string]apiv1.Container)
|
||||
for _, c := range targetSpecContainers {
|
||||
targetContainers[c.Name] = c
|
||||
}
|
||||
|
||||
var multierr *multierror.Error
|
||||
for _, c := range srcPodSpec.Containers {
|
||||
container, ok := targetContainers[c.Name]
|
||||
if ok {
|
||||
err := mergeContainer(&c, container)
|
||||
multierr = multierror.Append(multierr, err)
|
||||
delete(targetContainers, c.Name)
|
||||
}
|
||||
}
|
||||
|
||||
for _, container := range targetContainers {
|
||||
srcPodSpec.Containers = append(srcPodSpec.Containers, container)
|
||||
}
|
||||
|
||||
return multierr.ErrorOrNil()
|
||||
}
|
||||
|
||||
func mergeInitContainerList(srcPodSpec *apiv1.PodSpec, targetPodSpec *apiv1.PodSpec) error {
|
||||
targetSpecContainers := targetPodSpec.InitContainers
|
||||
targetContainers := make(map[string]apiv1.Container)
|
||||
for _, c := range targetSpecContainers {
|
||||
targetContainers[c.Name] = c
|
||||
}
|
||||
|
||||
var multierr *multierror.Error
|
||||
for _, c := range srcPodSpec.InitContainers {
|
||||
container, ok := targetContainers[c.Name]
|
||||
if ok {
|
||||
err := mergeContainer(&c, container)
|
||||
multierr = multierror.Append(multierr, err)
|
||||
delete(targetContainers, c.Name)
|
||||
}
|
||||
}
|
||||
|
||||
for _, container := range targetContainers {
|
||||
srcPodSpec.InitContainers = append(srcPodSpec.InitContainers, container)
|
||||
}
|
||||
return multierr.ErrorOrNil()
|
||||
}
|
||||
|
||||
func mergeVolumeLists(srcPodSpec *apiv1.PodSpec, targetPodSpec *apiv1.PodSpec) error {
|
||||
volumeList := targetPodSpec.Volumes
|
||||
specVolumes := make(map[string]apiv1.Volume)
|
||||
for _, vol := range volumeList {
|
||||
specVolumes[vol.Name] = vol
|
||||
}
|
||||
|
||||
var multierr *multierror.Error
|
||||
for _, vol := range srcPodSpec.Volumes {
|
||||
_, ok := specVolumes[vol.Name]
|
||||
if ok {
|
||||
multierr = multierror.Append(multierr, errors.New("Duplicate volume name found in the spec"))
|
||||
} else {
|
||||
delete(specVolumes, vol.Name)
|
||||
}
|
||||
}
|
||||
|
||||
for _, volume := range specVolumes {
|
||||
srcPodSpec.Volumes = append(srcPodSpec.Volumes, volume)
|
||||
}
|
||||
return multierr.ErrorOrNil()
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
Copyright 2016 The Fission 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 util
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func TestMergeContainerSpecs(t *testing.T) {
|
||||
expected := apiv1.Container{
|
||||
Name: "containerName",
|
||||
Image: "testImage",
|
||||
Command: []string{
|
||||
"command",
|
||||
},
|
||||
Args: []string{
|
||||
"arg1",
|
||||
"arg2",
|
||||
},
|
||||
ImagePullPolicy: apiv1.PullNever,
|
||||
TTY: true,
|
||||
Env: []apiv1.EnvVar{
|
||||
{
|
||||
Name: "a",
|
||||
Value: "b",
|
||||
},
|
||||
{
|
||||
Name: "c",
|
||||
Value: "d",
|
||||
},
|
||||
},
|
||||
}
|
||||
specs := []*apiv1.Container{
|
||||
{
|
||||
Name: "containerName",
|
||||
Image: "testImage",
|
||||
Command: []string{
|
||||
"command",
|
||||
},
|
||||
Args: []string{
|
||||
"arg1",
|
||||
"arg2",
|
||||
},
|
||||
ImagePullPolicy: apiv1.PullNever,
|
||||
TTY: true,
|
||||
},
|
||||
{
|
||||
Name: "shouldNotBeThere",
|
||||
Image: "shouldNotBeThere",
|
||||
Env: []apiv1.EnvVar{
|
||||
{
|
||||
Name: "a",
|
||||
Value: "b",
|
||||
},
|
||||
},
|
||||
ImagePullPolicy: apiv1.PullAlways,
|
||||
TTY: false,
|
||||
},
|
||||
{
|
||||
Env: []apiv1.EnvVar{
|
||||
{
|
||||
Name: "c",
|
||||
Value: "d",
|
||||
},
|
||||
},
|
||||
ImagePullPolicy: apiv1.PullIfNotPresent,
|
||||
TTY: false,
|
||||
},
|
||||
}
|
||||
result := MergeContainerSpecs(specs...)
|
||||
assert.Equal(t, expected, result)
|
||||
|
||||
// Check if merging order actually matters
|
||||
var rspecs []*apiv1.Container
|
||||
for i := len(specs) - 1; i >= 0; i -= 1 {
|
||||
rspecs = append(rspecs, specs[i])
|
||||
}
|
||||
reverseResult := MergeContainerSpecs(rspecs...)
|
||||
assert.NotEqual(t, expected, reverseResult)
|
||||
}
|
||||
|
||||
func TestMergeContainerSpecsSingle(t *testing.T) {
|
||||
expected := apiv1.Container{
|
||||
Name: "containerName",
|
||||
Image: "testImage",
|
||||
Command: []string{
|
||||
"command",
|
||||
},
|
||||
Args: []string{
|
||||
"arg1",
|
||||
"arg2",
|
||||
},
|
||||
ImagePullPolicy: apiv1.PullNever,
|
||||
TTY: true,
|
||||
}
|
||||
result := MergeContainerSpecs(&expected)
|
||||
assert.EqualValues(t, expected, result)
|
||||
}
|
||||
|
||||
func TestMergeContainerSpecsNil(t *testing.T) {
|
||||
expected := apiv1.Container{}
|
||||
result := MergeContainerSpecs()
|
||||
assert.EqualValues(t, expected, result)
|
||||
}
|
||||
Reference in New Issue
Block a user