-
Notifications
You must be signed in to change notification settings - Fork 156
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add new ephemeral package for registering and applying PodOptions cha…
…nges (#2874) * Added ephemeral any registration scheme * Added convenience functions for registering environment variables * Added ephemeral Apply to PodOptions usage * Added ephemeral Apply to corev1.Container usage * Added test for registering env vars * Switch to generics for increased type safety * After Pavan's review --------- Co-authored-by: Mark Severson <mark@kasten.io> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
- Loading branch information
1 parent
bd3fd99
commit 7f0c993
Showing
14 changed files
with
511 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
// Copyright 2024 The Kanister 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 ephemeral | ||
|
||
import ( | ||
"os" | ||
|
||
corev1 "k8s.io/api/core/v1" | ||
|
||
"github.com/kanisterio/kanister/pkg/kube" | ||
) | ||
|
||
// OSEnvVar creates an ApplierSet to set an environment variable if it's present | ||
// in the current environment. | ||
func OSEnvVar(name string) ApplierSet { | ||
return ApplierSet{ | ||
Container: ApplierFunc[corev1.Container](func(container *corev1.Container) { | ||
if val, present := os.LookupEnv(name); present { | ||
container.Env = append( | ||
container.Env, | ||
corev1.EnvVar{ | ||
Name: name, | ||
Value: val, | ||
}, | ||
) | ||
} | ||
}), | ||
PodOptions: ApplierFunc[kube.PodOptions](func(options *kube.PodOptions) { | ||
if val, present := os.LookupEnv(name); present { | ||
options.EnvironmentVariables = append( | ||
options.EnvironmentVariables, | ||
corev1.EnvVar{ | ||
Name: name, | ||
Value: val, | ||
}, | ||
) | ||
} | ||
}), | ||
} | ||
} | ||
|
||
// StaticEnvVar creates an ApplierSet to set a static environment variable. | ||
func StaticEnvVar(name, value string) ApplierSet { | ||
return ApplierSet{ | ||
Container: ApplierFunc[corev1.Container](func(container *corev1.Container) { | ||
container.Env = append( | ||
container.Env, | ||
corev1.EnvVar{ | ||
Name: name, | ||
Value: value, | ||
}, | ||
) | ||
}), | ||
PodOptions: ApplierFunc[kube.PodOptions](func(options *kube.PodOptions) { | ||
options.EnvironmentVariables = append( | ||
options.EnvironmentVariables, | ||
corev1.EnvVar{ | ||
Name: name, | ||
Value: value, | ||
}, | ||
) | ||
}), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
// Copyright 2024 The Kanister 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 ephemeral_test | ||
|
||
import ( | ||
"os" | ||
|
||
. "gopkg.in/check.v1" | ||
corev1 "k8s.io/api/core/v1" | ||
|
||
"github.com/kanisterio/kanister/pkg/ephemeral" | ||
"github.com/kanisterio/kanister/pkg/kube" | ||
) | ||
|
||
type EnvVarSuite struct{} | ||
|
||
var _ = Suite(&EnvVarSuite{}) | ||
|
||
func (s *EnvVarSuite) TestOSEnvVarKubePodOptions(c *C) { | ||
expected := []corev1.EnvVar{ | ||
{ | ||
Name: "KANISTER_REGISTERED_OS_ENVVAR", | ||
Value: "1", | ||
}, | ||
} | ||
|
||
// OS environment variable not set | ||
var registeredAppliers ephemeral.ApplierList[kube.PodOptions] | ||
set := ephemeral.OSEnvVar(expected[0].Name) | ||
registeredAppliers.Register(set.PodOptions) | ||
|
||
var options kube.PodOptions | ||
registeredAppliers.Apply(&options) | ||
c.Assert(options.EnvironmentVariables, DeepEquals, []corev1.EnvVar(nil)) | ||
|
||
// OS environment variable set | ||
os.Setenv(expected[0].Name, expected[0].Value) | ||
defer os.Unsetenv(expected[0].Name) | ||
|
||
registeredAppliers.Apply(&options) | ||
c.Assert(options.EnvironmentVariables, DeepEquals, expected) | ||
} | ||
|
||
func (s *EnvVarSuite) TestOSEnvVarCoreV1Container(c *C) { | ||
expected := []corev1.EnvVar{ | ||
{ | ||
Name: "KANISTER_REGISTERED_OS_ENVVAR", | ||
Value: "1", | ||
}, | ||
} | ||
|
||
// OS environment variable not set | ||
var registeredAppliers ephemeral.ApplierList[corev1.Container] | ||
set := ephemeral.OSEnvVar(expected[0].Name) | ||
registeredAppliers.Register(set.Container) | ||
|
||
var options corev1.Container | ||
registeredAppliers.Apply(&options) | ||
c.Assert(options.Env, DeepEquals, []corev1.EnvVar(nil)) | ||
|
||
// OS environment variable set | ||
os.Setenv(expected[0].Name, expected[0].Value) | ||
defer os.Unsetenv(expected[0].Name) | ||
|
||
registeredAppliers.Apply(&options) | ||
c.Assert(options.Env, DeepEquals, expected) | ||
} | ||
|
||
func (s *EnvVarSuite) TestStaticEnvVarKubePodOptions(c *C) { | ||
expected := []corev1.EnvVar{ | ||
{ | ||
Name: "KANISTER_REGISTERED_STATIC_ENVVAR", | ||
Value: "1", | ||
}, | ||
} | ||
|
||
var registeredAppliers ephemeral.ApplierList[kube.PodOptions] | ||
set := ephemeral.StaticEnvVar(expected[0].Name, expected[0].Value) | ||
registeredAppliers.Register(set.PodOptions) | ||
|
||
var options kube.PodOptions | ||
registeredAppliers.Apply(&options) | ||
c.Assert(options.EnvironmentVariables, DeepEquals, expected) | ||
} | ||
|
||
func (s *EnvVarSuite) TestRegisteringStaticEnvVarCoreV1Container(c *C) { | ||
expected := []corev1.EnvVar{ | ||
{ | ||
Name: "KANISTER_REGISTERED_STATIC_ENVVAR", | ||
Value: "1", | ||
}, | ||
} | ||
|
||
var registeredAppliers ephemeral.ApplierList[corev1.Container] | ||
set := ephemeral.StaticEnvVar(expected[0].Name, expected[0].Value) | ||
registeredAppliers.Register(set.Container) | ||
|
||
var options corev1.Container | ||
registeredAppliers.Apply(&options) | ||
c.Assert(options.Env, DeepEquals, expected) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
// Copyright 2024 The Kanister 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 ephemeral | ||
|
||
import ( | ||
corev1 "k8s.io/api/core/v1" | ||
|
||
"github.com/kanisterio/kanister/pkg/kube" | ||
) | ||
|
||
var ( | ||
Container ApplierList[corev1.Container] | ||
PodOptions ApplierList[kube.PodOptions] | ||
) | ||
|
||
// ApplierSet is a group of Appliers, typically returned by a constructor. | ||
type ApplierSet struct { | ||
Container Applier[corev1.Container] | ||
PodOptions Applier[kube.PodOptions] | ||
} | ||
|
||
// Register generically registers an Applier. | ||
func Register[T Constraint](applier Applier[T]) { | ||
switch applier := any(applier).(type) { | ||
case Applier[corev1.Container]: | ||
Container.Register(applier) | ||
case Applier[kube.PodOptions]: | ||
PodOptions.Register(applier) | ||
default: | ||
panic("Unknown applier type") | ||
} | ||
} | ||
|
||
// RegisterSet registers each of the Appliers contained in the set. | ||
func RegisterSet(set ApplierSet) { | ||
if set.Container != nil { | ||
Container.Register(set.Container) | ||
} | ||
|
||
if set.PodOptions != nil { | ||
PodOptions.Register(set.PodOptions) | ||
} | ||
} | ||
|
||
// Constraint provides the set of types allowed for appliers and filterers. | ||
type Constraint interface { | ||
kube.PodOptions | corev1.Container | ||
} | ||
|
||
// Applier is the interface which applies a manipulation to the PodOption to be | ||
// used to run ephemeral pdos. | ||
type Applier[T Constraint] interface { | ||
Apply(*T) | ||
} | ||
|
||
// ApplierFunc is a function which implements the Applier interface and can be | ||
// used to generically manipulate the PodOptions. | ||
type ApplierFunc[T Constraint] func(*T) | ||
|
||
func (f ApplierFunc[T]) Apply(options *T) { f(options) } | ||
|
||
// ApplierList is an array of registered Appliers which will be applied on | ||
// a PodOption. | ||
type ApplierList[T Constraint] []Applier[T] | ||
|
||
// Apply calls the Applier::Apply method on all registered appliers. | ||
func (l ApplierList[T]) Apply(options *T) { | ||
for _, applier := range l { | ||
applier.Apply(options) | ||
} | ||
} | ||
|
||
// Register adds the applier to the list of Appliers to be used when | ||
// manipulating the PodOptions. | ||
func (l *ApplierList[T]) Register(applier Applier[T]) { | ||
*l = append(*l, applier) | ||
} | ||
|
||
// Filterer is the interface which filters the use of registered appliers to | ||
// only those PodOptions that match the filter criteria. | ||
type Filterer[T Constraint] interface { | ||
Filter(*T) bool | ||
} | ||
|
||
// FiltererFunc is a function which implements the Filterer interface and can be | ||
// used to generically filter PodOptions to manipulate using the ApplierList. | ||
type FiltererFunc[T Constraint] func(*T) bool | ||
|
||
func (f FiltererFunc[T]) Filter(options *T) bool { | ||
return f(options) | ||
} | ||
|
||
// Filter applies the Appliers if the Filterer criterion is met. | ||
func Filter[T Constraint](filterer Filterer[T], appliers ...Applier[T]) Applier[T] { | ||
return ApplierFunc[T](func(options *T) { | ||
if !filterer.Filter(options) { | ||
return | ||
} | ||
|
||
for _, applier := range appliers { | ||
applier.Apply(options) | ||
} | ||
}) | ||
} | ||
|
||
// PodOptionsNameFilter is a Filterer that filters based on the PodOptions.Name | ||
// which is the Pod name. | ||
type PodOptionsNameFilter string | ||
|
||
func (n PodOptionsNameFilter) Filter(options *kube.PodOptions) bool { | ||
return string(n) == options.Name | ||
} | ||
|
||
// ContainerNameFilter is a Filterer that filters based on the Container.Name. | ||
type ContainerNameFilter string | ||
|
||
func (n ContainerNameFilter) Filter(container *corev1.Container) bool { | ||
return string(n) == container.Name | ||
} |
Oops, something went wrong.