Skip to content
Commits on Source (75)
/*
Licensed under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.gnu.org/licenses/agpl-3.0.html
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 application
import (
settings "k8s.libre.sh/application/settings"
"k8s.libre.sh/meta"
"k8s.libre.sh/status"
)
type Meta struct {
Version string
Instance string
// Name string
Component string
PartOf string
ManagedBy string
// Namespace string
}
type Application struct {
meta.Instance
Settings map[string]settings.Component
Components map[string]Component
Jobs map[string]Component
Owner status.ObjectWithStatus
Status *status.ApplicationStatus
Version string
}
func (app *Application) GetVersion() string { return app.Version }
func (app *Application) GetJobs() map[string]Component { return app.Jobs }
func (app *Application) GetApplicationStatus() *status.ApplicationStatus { return app.Status }
func (app *Application) GetComponents() map[string]Component { return app.Components }
func (app *Application) GetOwner() status.ObjectWithStatus { return app.Owner }
func (app *Application) GetSettings() map[string]settings.Component { return app.Settings }
func (app *Application) SetDefaults() {}
/* func NewApplication(i Instance) *Application {
app := &Application{}
app.Settings = make(map[string]settings.Component, len(i.GetSettings()))
app.Components = make(map[string]Component, len(i.GetComponents()))
app.Jobs = make(map[string]Component, len(i.GetJobs()))
for k, v := range i.GetSettings() {
v.SetDefaults()
app.Settings[k] = settings.NewSettings(v.GetCreateOptions(), v.GetSources(), v.GetParameters())
}
for k, v := range i.GetComponents() {
v.SetDefaults()
app.Components[k] = v
}
for k, v := range i.GetJobs() {
v.Init()
v.SetDefaults()
app.Jobs[k] = v
}
om := &meta.ObjectMeta{
Labels: i.GetLabels(),
Annotations: i.GetLabels(),
Name: i.GetName(),
Namespace: i.GetNamespace(),
}
app.Instance = om
app.Owner = i.GetOwner()
app.Version = i.GetVersion()
app.Status = i.GetApplicationStatus()
return app
}
*/
func NewApplication(setts map[string]settings.Component, cpts, jobs map[string]Component, owner status.ObjectWithStatus, m meta.Instance) *Application {
app := &Application{}
app.Settings = make(map[string]settings.Component, len(setts))
app.Components = make(map[string]Component, len(cpts))
app.Jobs = make(map[string]Component, len(jobs))
for k, v := range setts {
// v.SetDefaults()
app.Settings[k] = settings.NewSettings(v.GetCreateOptions(), v.GetSources(), v.GetParameters())
}
for k, v := range cpts {
// v.SetDefaults()
app.Components[k] = v
}
for k, v := range jobs {
// v.SetDefaults()
app.Jobs[k] = v
}
/* om := &meta.ObjectMeta{
// Labels: i.GetLabels(),
// Annotations: i.GetLabels(),
Name: m.GetName(),
Namespace: m.GetNamespace(),
} */
app.Instance = m
app.Owner = owner
app.Version = m.GetVersion()
ownerStatus := owner.GetApplicationStatus()
app.Status = ownerStatus.DeepCopy()
return app
}
......@@ -16,9 +16,7 @@ limitations under the License.
package application
import (
"github.com/presslabs/controller-util/syncer"
settings "k8s.libre.sh/application/settings"
interfaces "k8s.libre.sh/interfaces"
meta "k8s.libre.sh/meta"
objects "k8s.libre.sh/objects"
)
......@@ -29,32 +27,23 @@ type Component interface {
SetDefaults()
Init()
GetObjects() map[int]objects.Object
HasDependencies() bool
GetDependencies() settings.Config
GetConfig() settings.Config
HasSettings() []string
}
func NewSyncer(i Component, r interfaces.Reconcile, owner interfaces.Object) (syncers []syncer.Interface) {
mutators := i.GetObjects()
for _, m := range mutators {
syncers = append(syncers, objects.NewObjectSyncer(m, owner, r))
}
return syncers
type ComponentMutate interface {
GetObjects() map[int]objects.Object
}
func InitComponent(i Instance, c Component) {
component := c.GetComponent()
meta.SetObjectMeta(i, c)
if len(component) > 0 {
c.SetComponent(component)
func SetComponentSettings(i Component, s map[string]settings.Component) {
for _, dep := range i.HasSettings() {
foo := i.GetConfig()
foo = settings.MergeSettings(s[dep].GetConfig(), foo)
}
}
for _, o := range c.GetObjects() {
meta.SetObjectMeta(c, o)
func InitComponentsFromSettings(cs map[string]Component, setts map[string]settings.Component) {
for _, c := range cs {
SetComponentSettings(c, setts)
}
}
/*
Licensed under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.gnu.org/licenses/agpl-3.0.html
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 components
import (
"fmt"
settings "k8s.libre.sh/application/settings"
meta "k8s.libre.sh/meta"
"k8s.libre.sh/objects"
"k8s.libre.sh/objects/cronjob"
)
// const DefaultComponent string = "app"
// +kubebuilder:object:generate=true
type CronJob struct {
*cronjob.CronJob `json:",inline"`
Settings []string `json:"settings,omitempty"`
}
func (w *CronJob) HasSettings() []string { return w.Settings }
func (w *CronJob) GetMeta() meta.Instance { return w.ObjectMeta }
func (w *CronJob) GetObjects() map[int]objects.Object {
objects := make(map[int]objects.Object, 1)
objects[0] = w.CronJob
return objects
}
func (w *CronJob) GetConfig() settings.Config { return w.SettingsSpec }
func (w *CronJob) SetDefaults() {
// TODO TO FIX
if len(w.ObjectMeta.GetComponent()) == 0 {
w.ObjectMeta.SetComponent("cron")
}
if len(w.GetName()) == 0 {
w.SetName(fmt.Sprintf("%v-%v", w.GetInstance(), w.GetComponent()))
}
}
func (w *CronJob) Init() {
if w.CronJob == nil {
w.CronJob = &cronjob.CronJob{}
}
w.CronJob.Init()
if w.ObjectMeta == nil {
w.ObjectMeta = &meta.ObjectMeta{}
}
if len(w.ObjectMeta.Labels) == 0 {
w.ObjectMeta.Labels = make(map[string]string)
}
}
/*
Licensed under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.gnu.org/licenses/agpl-3.0.html
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 components
import (
settings "k8s.libre.sh/application/settings"
meta "k8s.libre.sh/meta"
"k8s.libre.sh/objects"
deployment "k8s.libre.sh/objects/deployment"
service "k8s.libre.sh/objects/service"
corev1 "k8s.io/api/core/v1"
)
// const DefaultComponent string = "app"
// +kubebuilder:object:generate=true
// InternalWorkload implements the component interface.
// It is a component that contains a deployment, service and some settings.
type InternalWorkload struct {
*meta.ObjectMeta `json:"commonMeta,omitempty"`
*deployment.Deployment `json:"deployment,omitempty"`
*service.Service `json:"service,omitempty"`
Settings []string `json:"settings,omitempty"`
}
// HasSettings returns a list of settings name that the component depends on
func (w *InternalWorkload) HasSettings() []string {
return w.Settings
}
// GetMeta returns the meta.Instance interface
func (w *InternalWorkload) GetMeta() meta.Instance {
return w.ObjectMeta
}
// GetObjects returns an ordered list of objects.Object interfaces
func (w *InternalWorkload) GetObjects() map[int]objects.Object {
objects := make(map[int]objects.Object)
objects[0] = w.Service
objects[1] = w.Deployment
return objects
}
func (w *InternalWorkload) GetConfig() settings.Config { return w.SettingsSpec }
// SetDefaults sets the default values of the component.
// Container ports are set from service if not specified
func (w *InternalWorkload) SetDefaults() {
for _, o := range w.GetObjects() {
meta.SetObjectMeta(w.ObjectMeta, o)
}
containerPort := corev1.ContainerPort{
Name: w.Service.Port.Name,
ContainerPort: w.Service.Port.Port,
Protocol: w.Service.Port.Protocol,
}
w.ContainerSpec.Ports = append(w.ContainerSpec.Ports, containerPort)
}
func (w *InternalWorkload) Init() {
// TODO improve initialisation of objectMeta, labels...
if w.ObjectMeta == nil {
w.ObjectMeta = &meta.ObjectMeta{}
}
if len(w.ObjectMeta.Labels) == 0 {
w.ObjectMeta.Labels = make(map[string]string)
}
// TODO FIXME
if len(w.ObjectMeta.GetComponent()) == 0 {
w.ObjectMeta.SetComponent(DefaultComponent)
}
if w.Deployment == nil {
w.Deployment = &deployment.Deployment{}
}
if w.Service == nil {
w.Service = &service.Service{}
}
w.Deployment.Init()
w.Service.Init()
}
/*
Licensed under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.gnu.org/licenses/agpl-3.0.html
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 components
import (
"k8s.libre.sh/application"
settings "k8s.libre.sh/application/settings"
meta "k8s.libre.sh/meta"
"k8s.libre.sh/objects"
"k8s.libre.sh/objects/job"
)
// const DefaultComponent string = "app"
// +kubebuilder:object:generate=true
type Job struct {
*job.Job `json:",inline"`
Settings []string `json:"settings,omitempty"`
}
// +kubebuilder:object:generate=true
type Jobs struct {
*meta.ObjectMeta `json:"commonMeta,omitempty"`
Install *Job `json:"install,omitempty"`
Upgrade *Job `json:"upgrade,omitempty"`
// StorageMigration Job
}
func (w *Jobs) Init() {
if w.ObjectMeta == nil {
w.ObjectMeta = &meta.ObjectMeta{}
}
if w.Install == nil {
w.Install = &Job{}
}
if w.Upgrade == nil {
w.Upgrade = &Job{}
}
w.Install.Init()
w.Upgrade.Init()
}
func (w *Jobs) GetJobs() map[string]application.Component {
cpts := make(map[string]application.Component)
cpts["install"] = w.Install
cpts["upgrade"] = w.Upgrade
return cpts
}
func (w *Jobs) GetObjects() map[int]objects.Object {
objects := make(map[int]objects.Object, 2)
objects[0] = w.Install
objects[1] = w.Upgrade
return objects
}
func (w *Job) HasSettings() []string { return w.Settings }
func (w *Job) GetMeta() meta.Instance { return w.ObjectMeta }
func (w *Job) GetObjects() map[int]objects.Object {
objects := make(map[int]objects.Object, 1)
objects[0] = w.Job
return objects
}
func (w *Job) GetConfig() settings.Config { return w.SettingsSpec }
func (w *Job) SetDefaults() {}
func (w *Job) Init() {
if w.Job == nil {
w.Job = &job.Job{}
}
w.Job.Init()
if w.ObjectMeta == nil {
w.ObjectMeta = &meta.ObjectMeta{}
}
if len(w.ObjectMeta.Labels) == 0 {
w.ObjectMeta.Labels = make(map[string]string)
}
// TODO FIXME
if len(w.ObjectMeta.GetComponent()) == 0 {
w.ObjectMeta.SetComponent("job")
}
if w.JobSpec == nil {
w.JobSpec = &job.JobSpec{}
}
}
......@@ -19,11 +19,9 @@ import (
"fmt"
"strings"
corev1 "k8s.io/api/core/v1"
networkingv1beta1 "k8s.io/api/networking/v1beta1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.libre.sh/interfaces"
meta "k8s.libre.sh/meta"
ingress "k8s.libre.sh/objects/ingress"
service "k8s.libre.sh/objects/service"
......@@ -31,78 +29,106 @@ import (
// +kubebuilder:object:generate=true
type Network struct {
IngressMeta *meta.ObjectMeta `json:"ingressMeta,omitempty"`
ServiceMeta *meta.ObjectMeta `json:"serviceMeta,omitempty"`
*Backend `json:",inline"`
IngressMeta *meta.ObjectMeta `json:"ingressMeta,omitempty"`
ServiceMeta *meta.ObjectMeta `json:"serviceMeta,omitempty"`
*HTTPBackend `json:",inline"`
}
type Service struct {
*meta.ObjectMeta `json:"meta,omitempty"`
*Backend `json:"backend,omitempty"`
func (n *Network) GetService() *service.Service {
return &service.Service{
ObjectMeta: n.ServiceMeta,
ServiceSpec: n.ServiceSpec,
}
}
func (s *Service) Mutate(obj interfaces.Object) error {
service.MutateService(s, obj.(*corev1.Service), s.ObjectMeta)
meta.MutateMeta(s, obj)
return nil
func (n *Network) GetIngress() *ingress.Ingress {
return &ingress.Ingress{
ObjectMeta: n.IngressMeta,
IngressSpec: n.GetIngressSpec(),
}
}
func (s *Service) GetObject() interfaces.Object {
return &corev1.Service{}
}
func (n *Network) Init() {
if n.HTTPBackend == nil {
n.HTTPBackend = &HTTPBackend{}
}
type Ingress struct {
*meta.ObjectMeta `json:"commonMeta,omitempty"`
Backends []Backend `json:"backends,omitempty"`
}
if n.HTTPBackend.ServiceSpec == nil {
n.HTTPBackend.ServiceSpec = &service.ServiceSpec{}
}
// +kubebuilder:object:generate=true
type Backend struct {
ServiceName string `json:"name,omitempty"`
Hostname string `json:"hostname,omitempty"`
Paths []string `json:"routes,omitempty"`
TLS bool `json:"tls,omitempty"`
TLSSecretRef string `json:"tlsSecretRef,omitempty"`
Port service.Port `json:"port,omitempty"`
ServiceType corev1.ServiceType `json:"type,omitempty"`
}
if n.ServiceMeta == nil {
n.ServiceMeta = &meta.ObjectMeta{}
}
func (i *Ingress) Mutate(obj interfaces.Object) error {
ingress.MutateIngress(i, obj.(*networkingv1beta1.Ingress))
meta.MutateMeta(i, obj)
return nil
if n.IngressMeta == nil {
n.IngressMeta = &meta.ObjectMeta{}
}
}
func (i *Ingress) GetObject() interfaces.Object {
return &networkingv1beta1.Ingress{}
// +kubebuilder:object:generate=true
type HTTPBackend struct {
*service.ServiceSpec `json:",inline"`
// Specifies the name of the referenced service.
// +optional
ServiceName string `json:"serviceName,omitempty" protobuf:"bytes,1,opt,name=serviceName"`
// Host is the fully qualified domain name of a network host, as defined by RFC 3986.
// Note the following deviations from the "host" part of the
// URI as defined in RFC 3986:
// 1. IPs are not allowed. Currently an IngressRuleValue can only apply to
// the IP in the Spec of the parent Ingress.
// 2. The `:` delimiter is not respected because ports are not allowed.
// Currently the port of an Ingress is implicitly :80 for http and
// :443 for https.
// Both these may change in the future.
// Incoming requests are matched against the host before the
// IngressRuleValue. If the host is unspecified, the Ingress routes all
// traffic based on the specified IngressRuleValue.
//
// Host can be "precise" which is a domain name without the terminating dot of
// a network host (e.g. "foo.bar.com") or "wildcard", which is a domain name
// prefixed with a single wildcard label (e.g. "*.foo.com").
// The wildcard character '*' must appear by itself as the first DNS label and
// matches only a single label. You cannot have a wildcard label by itself (e.g. Host == "*").
// Requests will be matched against the Host field in the following way:
// 1. If Host is precise, the request matches this rule if the http host header is equal to Host.
// 2. If Host is a wildcard, then the request matches this rule if the http host header
// is to equal to the suffix (removing the first label) of the wildcard rule.
// +optional
Host string `json:"host,omitempty" protobuf:"bytes,1,opt,name=host"`
// Path is matched against the path of an incoming request. Currently it can
// contain characters disallowed from the conventional "path" part of a URL
// as defined by RFC 3986. Paths must begin with a '/'. When unspecified,
// all paths from incoming requests are matched.
// +optional
Paths []string `json:"routes,omitempty"`
// TLS specifies if tls is enabled
TLS bool `json:"tls,omitempty"`
// SecretName is the name of the secret used to terminate TLS traffic on
// port 443. Field is left optional to allow TLS routing based on SNI
// hostname alone. If the SNI host in a listener conflicts with the "Host"
// header field used by an IngressRule, the SNI host is used for termination
// and value of the Host header is used for routing.
// +optional
TLSSecretRef string `json:"tlsSecretRef,omitempty" protobuf:"bytes,2,opt,name=tlsSecretRef"`
}
func (b *Backend) GetServiceType() corev1.ServiceType { return b.ServiceType }
func (b *Backend) GetServicePorts() []corev1.ServicePort {
func (b *HTTPBackend) GetIngressSpec() *networkingv1beta1.IngressSpec {
ports := []corev1.ServicePort{}
ports = append(ports, b.GetServicePort())
return ports
}
func (b *Backend) GetServicePort() corev1.ServicePort {
tlsList := []networkingv1beta1.IngressTLS{}
rules := []networkingv1beta1.IngressRule{}
port := corev1.ServicePort{
TargetPort: intstr.FromString(b.Port.Name),
Port: b.Port.Port,
Name: b.Port.Name,
}
tlsList = append(tlsList, b.GetIngressTLS())
rules = append(rules, b.GetIngressRule())
if len(b.Port.Protocol) > 0 {
port.Protocol = b.Port.Protocol
return &networkingv1beta1.IngressSpec{
Rules: rules,
TLS: tlsList,
}
return port
}
func (b *Backend) GetIngressBackendPaths() []networkingv1beta1.HTTPIngressPath {
func (b *HTTPBackend) GetIngressBackendPaths() []networkingv1beta1.HTTPIngressPath {
bkpaths := []networkingv1beta1.HTTPIngressPath{}
......@@ -110,7 +136,6 @@ func (b *Backend) GetIngressBackendPaths() []networkingv1beta1.HTTPIngressPath {
path := networkingv1beta1.HTTPIngressPath{
Path: p,
Backend: networkingv1beta1.IngressBackend{
// TODO TO FIX
ServiceName: b.ServiceName,
ServicePort: intstr.FromString(b.Port.Name),
},
......@@ -121,10 +146,10 @@ func (b *Backend) GetIngressBackendPaths() []networkingv1beta1.HTTPIngressPath {
return bkpaths
}
func (b *Backend) GetIngressRule() networkingv1beta1.IngressRule {
func (b *HTTPBackend) GetIngressRule() networkingv1beta1.IngressRule {
rule := networkingv1beta1.IngressRule{
Host: b.Hostname,
Host: b.Host,
IngressRuleValue: networkingv1beta1.IngressRuleValue{
HTTP: &networkingv1beta1.HTTPIngressRuleValue{
Paths: b.GetIngressBackendPaths(),
......@@ -135,40 +160,18 @@ func (b *Backend) GetIngressRule() networkingv1beta1.IngressRule {
return rule
}
func (i *Ingress) GetIngressRules() []networkingv1beta1.IngressRule {
rules := []networkingv1beta1.IngressRule{}
for _, ing := range i.Backends {
rules = append(rules, ing.GetIngressRule())
}
return rules
}
func (b *Backend) GetIngressTLS() networkingv1beta1.IngressTLS {
func (b *HTTPBackend) GetIngressTLS() networkingv1beta1.IngressTLS {
tls := networkingv1beta1.IngressTLS{}
if b.TLS {
if len(b.TLSSecretRef) == 0 {
b.TLSSecretRef = fmt.Sprintf("%v-%v", strings.Replace(b.Hostname, ".", "-", -1), "tls")
b.TLSSecretRef = fmt.Sprintf("%v-%v", strings.Replace(b.Host, ".", "-", -1), "tls")
}
tls = networkingv1beta1.IngressTLS{
SecretName: b.TLSSecretRef,
}
tls.Hosts = append(tls.Hosts, b.Hostname)
tls.Hosts = append(tls.Hosts, b.Host)
}
return tls
}
func (i *Ingress) GetIngressTLS() []networkingv1beta1.IngressTLS {
tlsList := []networkingv1beta1.IngressTLS{}
for _, ing := range i.Backends {
tlsList = append(tlsList, ing.GetIngressTLS())
}
return tlsList
}
......@@ -31,11 +31,11 @@ type Workload struct {
*meta.ObjectMeta `json:"commonMeta,omitempty"`
*deployment.Deployment `json:"deployment,omitempty"`
*Network `json:"network,omitempty"`
Dependencies bool `json:"dependencies,omitempty"`
Settings []string `json:"settings,omitempty"`
}
func (w *Workload) HasDependencies() bool {
return w.Dependencies
func (w *Workload) HasSettings() []string {
return w.Settings
}
func (w *Workload) GetMeta() meta.Instance {
......@@ -46,22 +46,12 @@ func (w *Workload) GetObjects() map[int]objects.Object {
objects := make(map[int]objects.Object)
ing := &Ingress{
// TODO TO FIX
ObjectMeta: w.IngressMeta,
Backends: []Backend{
*w.Backend,
},
}
srv := &Service{
// TODO TO FIX
ObjectMeta: w.ServiceMeta,
Backend: w.Backend,
}
srv := w.Network.GetService()
ing := w.Network.GetIngress()
meta.SetObjectMeta(w.ObjectMeta, ing.ObjectMeta)
meta.SetObjectMeta(w.ObjectMeta, srv.ObjectMeta)
// TODO TO FIX
//meta.SetObjectMeta(w.ObjectMeta, ing.ObjectMeta)
//meta.SetObjectMeta(w.ObjectMeta, srv.ObjectMeta)
objects[0] = srv
objects[1] = ing
......@@ -70,20 +60,22 @@ func (w *Workload) GetObjects() map[int]objects.Object {
return objects
}
func (w *Workload) SetDependencies(s *settings.Component) { w.ConfigSpec = s.ConfigSpec }
func (w *Workload) GetDependencies() settings.Config { return w.ConfigSpec }
func (w *Workload) GetConfig() settings.Config { return w.SettingsSpec }
func (w *Workload) SetDefaults() {
// TODO FIXME
w.Dependencies = true
for _, o := range w.GetObjects() {
meta.SetObjectMeta(w.ObjectMeta, o)
}
w.Backend.ServiceName = w.ServiceMeta.GetName()
if len(w.ServiceMeta.GetName()) > 0 {
w.HTTPBackend.ServiceName = w.ServiceMeta.GetName()
}
containerPort := corev1.ContainerPort{
Name: w.Backend.Port.Name,
ContainerPort: w.Backend.Port.Port,
Protocol: w.Backend.Port.Protocol,
Name: w.HTTPBackend.Port.Name,
ContainerPort: w.HTTPBackend.Port.Port,
Protocol: w.HTTPBackend.Port.Protocol,
}
w.ContainerSpec.Ports = append(w.ContainerSpec.Ports, containerPort)
......@@ -106,23 +98,15 @@ func (w *Workload) Init() {
w.ObjectMeta.SetComponent(DefaultComponent)
}
if w.Deployment.ObjectMeta == nil {
w.Deployment.ObjectMeta = &meta.ObjectMeta{}
if w.Deployment == nil {
w.Deployment = &deployment.Deployment{}
}
if w.ConfigSpec == nil {
w.ConfigSpec = &settings.ConfigSpec{}
}
w.Deployment.Init()
if w.Backend == nil {
w.Backend = &Backend{}
if w.Network == nil {
w.Network = &Network{}
}
if w.ServiceMeta == nil {
w.ServiceMeta = &meta.ObjectMeta{}
}
if w.IngressMeta == nil {
w.IngressMeta = &meta.ObjectMeta{}
}
w.Network.Init()
}
......@@ -21,26 +21,148 @@ package components
import (
"k8s.libre.sh/meta"
"k8s.libre.sh/objects/cronjob"
"k8s.libre.sh/objects/deployment"
"k8s.libre.sh/objects/job"
"k8s.libre.sh/objects/service"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Backend) DeepCopyInto(out *Backend) {
func (in *CronJob) DeepCopyInto(out *CronJob) {
*out = *in
if in.CronJob != nil {
in, out := &in.CronJob, &out.CronJob
*out = new(cronjob.CronJob)
(*in).DeepCopyInto(*out)
}
if in.Settings != nil {
in, out := &in.Settings, &out.Settings
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CronJob.
func (in *CronJob) DeepCopy() *CronJob {
if in == nil {
return nil
}
out := new(CronJob)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HTTPBackend) DeepCopyInto(out *HTTPBackend) {
*out = *in
if in.ServiceSpec != nil {
in, out := &in.ServiceSpec, &out.ServiceSpec
*out = new(service.ServiceSpec)
(*in).DeepCopyInto(*out)
}
if in.Paths != nil {
in, out := &in.Paths, &out.Paths
*out = make([]string, len(*in))
copy(*out, *in)
}
out.Port = in.Port
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Backend.
func (in *Backend) DeepCopy() *Backend {
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPBackend.
func (in *HTTPBackend) DeepCopy() *HTTPBackend {
if in == nil {
return nil
}
out := new(Backend)
out := new(HTTPBackend)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *InternalWorkload) DeepCopyInto(out *InternalWorkload) {
*out = *in
if in.ObjectMeta != nil {
in, out := &in.ObjectMeta, &out.ObjectMeta
*out = new(meta.ObjectMeta)
(*in).DeepCopyInto(*out)
}
if in.Deployment != nil {
in, out := &in.Deployment, &out.Deployment
*out = new(deployment.Deployment)
(*in).DeepCopyInto(*out)
}
if in.Service != nil {
in, out := &in.Service, &out.Service
*out = new(service.Service)
(*in).DeepCopyInto(*out)
}
if in.Settings != nil {
in, out := &in.Settings, &out.Settings
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InternalWorkload.
func (in *InternalWorkload) DeepCopy() *InternalWorkload {
if in == nil {
return nil
}
out := new(InternalWorkload)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Job) DeepCopyInto(out *Job) {
*out = *in
if in.Job != nil {
in, out := &in.Job, &out.Job
*out = new(job.Job)
(*in).DeepCopyInto(*out)
}
if in.Settings != nil {
in, out := &in.Settings, &out.Settings
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Job.
func (in *Job) DeepCopy() *Job {
if in == nil {
return nil
}
out := new(Job)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Jobs) DeepCopyInto(out *Jobs) {
*out = *in
if in.ObjectMeta != nil {
in, out := &in.ObjectMeta, &out.ObjectMeta
*out = new(meta.ObjectMeta)
(*in).DeepCopyInto(*out)
}
if in.Install != nil {
in, out := &in.Install, &out.Install
*out = new(Job)
(*in).DeepCopyInto(*out)
}
if in.Upgrade != nil {
in, out := &in.Upgrade, &out.Upgrade
*out = new(Job)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Jobs.
func (in *Jobs) DeepCopy() *Jobs {
if in == nil {
return nil
}
out := new(Jobs)
in.DeepCopyInto(out)
return out
}
......@@ -58,9 +180,9 @@ func (in *Network) DeepCopyInto(out *Network) {
*out = new(meta.ObjectMeta)
(*in).DeepCopyInto(*out)
}
if in.Backend != nil {
in, out := &in.Backend, &out.Backend
*out = new(Backend)
if in.HTTPBackend != nil {
in, out := &in.HTTPBackend, &out.HTTPBackend
*out = new(HTTPBackend)
(*in).DeepCopyInto(*out)
}
}
......@@ -93,6 +215,11 @@ func (in *Workload) DeepCopyInto(out *Workload) {
*out = new(Network)
(*in).DeepCopyInto(*out)
}
if in.Settings != nil {
in, out := &in.Settings, &out.Settings
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Workload.
......
......@@ -16,99 +16,102 @@ limitations under the License.
package application
import (
"context"
"github.com/presslabs/controller-util/syncer"
"strings"
settings "k8s.libre.sh/application/settings"
"k8s.libre.sh/application/settings/parameters"
"k8s.libre.sh/interfaces"
"k8s.libre.sh/meta"
"k8s.libre.sh/objects"
"k8s.libre.sh/status"
)
type Instance interface {
meta.Instance
GetComponents() map[int]Component
GetOwner() interfaces.Object
GetSettings() settings.Settings
// GetSettings() map[int]settings.Settings
GetComponents() map[string]Component
GetJobs() map[string]Component
GetOwner() status.ObjectWithStatus
GetSettings() map[string]settings.Component
GetApplicationStatus() *status.ApplicationStatus
SetDefaults()
}
func Init(i Instance) {
i.SetDefaults()
for _, c := range i.GetComponents() {
c.Init()
// TODO TOFIX
component := c.GetComponent()
func GetTemplateValues(i Instance, setts map[string]settings.Component) map[string]interface{} {
values := make(map[string]interface{})
cptsVals := make(map[string]interface{})
settsVals := make(map[string]interface{})
meta.SetObjectMeta(i, c)
for _, cpt := range i.GetComponents() {
// TODO create templateValues for Component interface
cptsVals[cpt.GetMeta().GetComponent()] = cpt.(interface{})
}
if len(component) > 0 {
c.SetComponent(component)
}
for _, sett := range setts {
// TODO create templateValues for Settings interface
settsVals[sett.GetMeta().GetComponent()] = parameters.KeyPairValues(sett.GetConfig().GetParameters())
}
for _, o := range c.GetObjects() {
meta.SetObjectMeta(c, o)
}
values["components"] = cptsVals
values["settings"] = settsVals
c.SetDefaults()
return values
}
}
func NewSyncers(app Instance, r interfaces.Reconcile, owner interfaces.Object) (syncers []syncer.Interface, err error) {
////////////////////
//// Settings //////
///////////////////
// TODO TO FIX SHOULD BE RUN IN INITIALIZED FUNCTION
// INITIALISE VALUES FROM EXTERNAL RESOURCES, RANDOM & TEMPLATES
// sett := app.GetSettings().GetConfig().(*components.Settings)
sett := app.GetSettings().(*settings.Component)
func InitSettings(i Instance, r interfaces.Reconcile) error {
appStatus := i.GetApplicationStatus()
setts := i.GetSettings()
// First we init external values and random values
for name, sett := range setts {
if len(appStatus.Settings[name].Resources.Objects) > 0 {
for _, o := range appStatus.Settings[name].Resources.Objects {
src := settings.Source{
Ref: o.Name,
// & TO FIX
Type: strings.ToLower(o.Kind),
}
*sett.GetConfig().GetSources() = append(*sett.GetConfig().GetSources(), src)
}
}
err = sett.Init(r.GetClient())
err := settings.Init(sett, r.GetClient(), i.GetOwner())
if err != nil {
return syncers, err
if err != nil {
return err
}
}
// Object Syncers
// Transform instance to template values used to init settings
values := GetTemplateValues(i, i.GetSettings())
for _, obj := range sett.GetObjects() {
// meta.SetObjectMeta(sett.CommonMeta, obj)
s := objects.NewObjectSyncer(obj, owner, r)
if err := syncer.Sync(context.TODO(), s, r.GetRecorder()); err != nil {
return syncers, err
// Init the values from templates & set defaults
for _, sett := range setts {
err := sett.GetConfig().GetParameters().InitTemplateValues(values)
if err != nil {
return err
}
sett.SetDefaults()
}
/////////////////////
//// Components ////
///////////////////
components := app.GetComponents()
return nil
}
// TODO TO FIX SHOULD BE RUN IN INITIALIZED FUNCTION
// INITIALIZE
for _, c := range components {
if c.HasDependencies() {
// Initiatilize & Defaults
func Init(i Instance, r interfaces.Reconcile) error {
deps := c.GetDependencies()
deps = settings.MergeSettings(sett, deps)
i.SetDefaults()
}
// GET SYNCERS
mutators := c.GetObjects()
for _, m := range mutators {
syncers = append(syncers, objects.NewObjectSyncer(m, owner, r))
}
err := InitSettings(i, r)
if err != nil {
return err
}
return syncers, nil
// Init Components
cpts := i.GetComponents()
InitComponentsFromSettings(cpts, i.GetSettings())
return nil
}
package reconciler
import (
"context"
"github.com/presslabs/controller-util/syncer"
"k8s.libre.sh/application"
interfaces "k8s.libre.sh/interfaces"
"k8s.libre.sh/objects"
"k8s.libre.sh/status"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
type ComponentReconciler struct {
ReconcilerBase *ReconcilerBase
Component application.Component
Status status.ComponentStatus
experiments map[string]chan struct{}
Owner interfaces.Object
}
func (r *ComponentReconciler) Reconcile(request reconcile.Request) (reconcile.Result, error) {
for _, o := range r.Component.GetObjects() {
reconciler := NewResourceReconciler(r.ReconcilerBase, o, r.Owner, context.TODO())
res, err := reconciler.Reconcile(request)
if err != nil {
return res, err
}
r.Status.Resources.Objects = append(r.Status.Resources.Objects, reconciler.Status)
}
return ctrl.Result{}, nil
}
func SyncComponent(ctx context.Context, r interfaces.Reconcile, m application.ComponentMutate, owner interfaces.Object) (resources status.Resources, err error) {
keys := []int{}
syncers := make(map[int]syncer.Interface, len(m.GetObjects()))
for k, o := range m.GetObjects() {
keys = append(keys, k)
syncers[k] = objects.NewObjectSyncer(o, owner, r)
}
resources, err = SyncObjects(context.TODO(), r, syncers)
if err != nil {
return resources, err
}
return resources, nil
}
package reconciler
import (
"context"
"sort"
"github.com/hashicorp/go-version"
"k8s.libre.sh/application"
interfaces "k8s.libre.sh/interfaces"
"k8s.libre.sh/status"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
type InstanceReconciler struct {
*ReconcilerBase
Instance application.Instance
SyncOrder map[int]string
}
func NewInstanceReconciler(r *ReconcilerBase, i application.Instance, order map[int]string) *InstanceReconciler {
return &InstanceReconciler{
ReconcilerBase: r,
Instance: i,
SyncOrder: order,
}
}
/* func (er *InstanceReconciler) ManageSuccess(instance apis.Resource) (reconcile.Result, error) {
if enforcingReconcileStatusAware, updateStatus := (instance).(apis.EnforcingReconcileStatusAware); updateStatus {
condition := status.Condition{
Type: "ReconcileSuccess",
LastTransitionTime: metav1.Now(),
Message: astatus.SuccessfulMessage,
Reason: astatus.SuccessfulReason,
Status: corev1.ConditionTrue,
}
status := apis.EnforcingReconcileStatus{
Conditions: status.NewConditions(condition),
LockedResourceStatuses: er.GetLockedResourceStatuses(instance),
LockedPatchStatuses: er.GetLockedPatchStatuses(instance),
}
enforcingReconcileStatusAware.SetEnforcingReconcileStatus(status)
log.V(1).Info("about to modify state for", "instance version", instance.GetResourceVersion())
err := er.GetClient().Status().Update(context.Background(), instance)
if err != nil {
if errors.IsResourceExpired(err) {
log.Info("unable to update status for", "object version", instance.GetResourceVersion(), "resource version expired, will trigger another reconcile cycle", "")
} else {
log.Error(err, "unable to update status for", "object", instance)
}
return reconcile.Result{}, err
}
} else {
log.V(1).Info("object is not RecocileStatusAware, not setting status")
}
return reconcile.Result{}, nil
}
*/
func (r *InstanceReconciler) ManageError(owner status.ObjectWithStatus) (reconcile.Result, error) {
return reconcile.Result{}, nil
}
func (r *InstanceReconciler) Reconcile(request reconcile.Request) (reconcile.Result, error) {
appStatus := r.Instance.GetApplicationStatus()
oldStatus := appStatus.DeepCopy()
err := application.Init(r.Instance, r.ReconcilerBase)
if err != nil {
return reconcile.Result{}, err
}
// Sync Settings
for name, s := range r.Instance.GetSettings() {
res, err := SyncComponent(context.TODO(), r.ReconcilerBase, s, r.Instance.GetOwner())
cptStatus := status.ComponentStatus{
Resources: res,
}
if len(appStatus.Settings) == 0 {
appStatus.Settings = make(map[string]status.ComponentStatus)
}
if err != nil {
cptStatus.ComponentsReady = "NotReady"
appStatus.Settings[name] = cptStatus
return ctrl.Result{}, err
}
cptStatus.ComponentsReady = "Ready"
appStatus.Settings[name] = cptStatus
}
jobs := r.Instance.GetJobs()
current, err := version.NewVersion(r.Instance.GetApplicationStatus().Version)
// get nextcloud desired version in spec
desired, err := version.NewVersion(r.Instance.GetVersion())
if len(r.Instance.GetApplicationStatus().Version) == 0 {
// Sync INSTALL
res, err := SyncComponent(context.TODO(), r.ReconcilerBase, jobs["install"], r.Instance.GetOwner())
cptStatus := status.ComponentStatus{
Resources: res,
}
if len(appStatus.Components) == 0 {
appStatus.Components = make(map[string]status.ComponentStatus)
}
if err != nil {
cptStatus.ComponentsReady = "NotReady"
appStatus.Components["jobs"] = cptStatus
return ctrl.Result{}, err
}
cptStatus.ComponentsReady = "Ready"
appStatus.Components["jobs"] = cptStatus
// ADD manage success
// return ctrl.Result{}, nil
delete(jobs, "upgrade")
} else {
switch current.Compare(desired) {
case 1:
// current version is greater than desired, no downgrade
// TODO Format error
return ctrl.Result{}, err
case -1:
// current version lower than desired, upgrade job
// SYNC UPGRADE
delete(jobs, "install")
case 0:
// SYNC CPTS
// current version and desired are the same, normal start
delete(jobs, "install")
delete(jobs, "upgrade")
}
}
cpts := make(map[string]application.ComponentMutate)
for name, j := range jobs {
cpts[name] = j
}
// Sync Components
for name, c := range r.Instance.GetComponents() {
cpts[name] = c
}
sts, err := SyncComponents(context.TODO(), r.ReconcilerBase, cpts, r.Instance.GetOwner(), r.SyncOrder)
appStatus.Components = sts.Components
appStatus.SetConditions(sts.Conditions...)
appStatus.Version = desired.String()
owner := r.Instance.GetOwner()
owner.SetApplicationStatus(*appStatus)
// TODO Improve
if err != nil {
if err := r.ReconcilerBase.GetClient().Status().Update(context.TODO(), owner); err != nil {
return ctrl.Result{}, err
}
return ctrl.Result{}, err
}
if err := r.ReconcilerBase.GetClient().Status().Update(context.TODO(), owner); err != nil {
return ctrl.Result{}, err
}
// Clean up resources and update status
err = CleanUpResources(oldStatus.GetResources(), appStatus.GetResources(), r.ReconcilerBase.GetClient(), r.Instance.GetNamespace())
if err != nil {
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}
func SyncComponents(ctx context.Context, r interfaces.Reconcile, cpts map[string]application.ComponentMutate, owner interfaces.Object, order map[int]string) (appStatus status.ApplicationStatus, err error) {
keys := []int{}
appStatus.Components = make(map[string]status.ComponentStatus)
for k := range order {
keys = append(keys, k)
}
sort.Ints(keys)
for _, k := range keys {
if cpts[order[k]] != nil {
resources, err := SyncComponent(ctx, r, cpts[order[k]], owner)
cptStatus := status.ComponentStatus{
Resources: resources,
}
if err != nil {
// manage error
cptStatus.ComponentsReady = "NotReady"
appStatus.Components[order[k]] = cptStatus
appStatus.SetConditions(status.ReconcileError(err))
return appStatus, err
}
cptStatus.ComponentsReady = "Ready"
appStatus.Components[order[k]] = cptStatus
}
}
appStatus.SetConditions(status.ReconcileSuccess())
return appStatus, nil
}
package reconciler
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/record"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
)
// ReconcilerBase is a base struct from which all reconcilers can be derived. By doing so your finalizers will also inherir a set of utility functions
// To inherit from reconciler just build your finalizer this way:
// type MyReconciler struct {
// util.ReconcilerBase
// ... other optional fields ...
// }
var log = logf.Log.WithName("reconciler")
type ReconcilerBase struct {
// This client, initialized using mgr.Client() above, is a split client
// that reads objects from the cache and writes to the apiserver
client client.Client
scheme *runtime.Scheme
restConfig *rest.Config
recorder record.EventRecorder
}
func NewReconcilerBase(client client.Client, scheme *runtime.Scheme, restConfig *rest.Config, recorder record.EventRecorder) ReconcilerBase {
return ReconcilerBase{
client: client,
scheme: scheme,
restConfig: restConfig,
recorder: recorder,
}
}
// IsValid determines if a CR instance is valid. this implementation returns always true, should be overridden
func (r *ReconcilerBase) IsValid(obj metav1.Object) (bool, error) {
return true, nil
}
// IsInitialized determines if a CR instance is initialized. this implementation returns always true, should be overridden
func (r *ReconcilerBase) IsInitialized(obj metav1.Object) bool {
return true
}
// Reconcile is a stub function to have ReconcilerBase match the Reconciler interface. You must redefine this function
func (r *ReconcilerBase) Reconcile(request reconcile.Request) (reconcile.Result, error) {
return reconcile.Result{}, nil
}
// GetClient returns the underlying client
func (r *ReconcilerBase) GetClient() client.Client {
return r.client
}
// GetRestConfig returns the undelying rest config
func (r *ReconcilerBase) GetRestConfig() *rest.Config {
return r.restConfig
}
// GetRecorder returns the underlying recorder
func (r *ReconcilerBase) GetRecorder() record.EventRecorder {
return r.recorder
}
// GetScheme returns the scheme
func (r *ReconcilerBase) GetScheme() *runtime.Scheme {
return r.scheme
}
package reconciler
import (
"context"
"sort"
"cloud.google.com/go/logging"
"github.com/presslabs/controller-util/syncer"
interfaces "k8s.libre.sh/interfaces"
"k8s.libre.sh/objects"
"k8s.libre.sh/status"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
type ResourceReconciler struct {
*ReconcilerBase
Object objects.Object
Owner interfaces.Object
Status status.ObjectStatus
log logging.Logger
ctx context.Context
}
func NewResourceReconciler(r *ReconcilerBase, o objects.Object, owner interfaces.Object, ctx context.Context) *ResourceReconciler {
return &ResourceReconciler{
ReconcilerBase: r,
Object: o,
Owner: owner,
ctx: ctx,
}
}
func (r *ResourceReconciler) Reconcile(request reconcile.Request) (reconcile.Result, error) {
return ctrl.Result{}, nil
}
func SyncObject(ctx context.Context, r interfaces.Reconcile, syncer syncer.Interface) (status.ObjectStatus, error) {
resource := status.ObjectStatus{}
syncResult, err := syncer.Sync(ctx)
obj := syncer.GetObject().(interfaces.Object)
if err != nil {
Record(syncer.GetOwner(), r.GetRecorder(), syncResult)
// TODO TOFIX object cannot be set as it is not recognized
// resource.Status = "NotReady"
return resource, err
}
err = WaitUntilCompletion(r, obj)
// TODO TO fix gvk is not recognized, need to get resource with client before
resource = status.ObjectToStatus(obj)
if err != nil {
// TODO replace syncResult by sync failed
resource.Status = "NotReady"
Record(syncer.GetOwner(), r.GetRecorder(), syncResult)
return resource, err
}
// resource = status.ObjectToStatus(obj)
resource.Status = "Ready"
Record(syncer.GetOwner(), r.GetRecorder(), syncResult)
return resource, nil
}
func SyncObjects(ctx context.Context, r interfaces.Reconcile, syncers map[int]syncer.Interface) (resources status.Resources, err error) {
keys := []int{}
for k := range syncers {
keys = append(keys, k)
}
sort.Ints(keys)
for _, k := range keys {
objStatus, err := SyncObject(ctx, r, syncers[k])
resources.Objects = append(resources.Objects, objStatus)
if err != nil {
return resources, err
}
}
return resources, nil
}
package reconciler
import (
"context"
"errors"
"time"
"github.com/presslabs/controller-util/syncer"
batchv1 "k8s.io/api/batch/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/tools/record"
interfaces "k8s.libre.sh/interfaces"
"k8s.libre.sh/status"
kstatus "sigs.k8s.io/cli-utils/pkg/kstatus/status"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
const (
RetryInterval = 50 * time.Millisecond
RetryTimeout = 2 * time.Second
// ReadinessTimeout = 30 * time.Second
ReadinessTimeout = 5 * time.Minute
GCTimeout = 5 * time.Minute
)
func CleanUpResources(old, new *status.Resources, c client.Client, ns string) error {
diff := status.Difference(old, new)
if len(diff.Objects) > 0 {
for _, o := range diff.Objects {
unstruct := &unstructured.Unstructured{}
unstruct.SetGroupVersionKind(o.GVK())
unstruct.SetName(o.Name)
unstruct.SetNamespace(ns)
deleteBackground := metav1.DeletePropagationBackground
opts := client.DeleteOptions{
PropagationPolicy: &deleteBackground,
}
if err := c.Delete(context.TODO(), unstruct, &opts); err != nil {
return err
}
}
}
return nil
}
func Record(owner runtime.Object, recorder record.EventRecorder, result syncer.SyncResult) error {
if recorder != nil && owner != nil && result.EventType != "" && result.EventReason != "" && result.EventMessage != "" {
if result.Operation != controllerutil.OperationResultNone {
recorder.Eventf(owner, result.EventType, result.EventReason, result.EventMessage)
}
}
return nil
}
func WaitUntilCompletion(r interfaces.Reconcile, obj runtime.Object) error {
return wait.Poll(RetryInterval, ReadinessTimeout, func() (bool, error) {
objectKey, _ := client.ObjectKeyFromObject(obj)
if err := r.GetClient().Get(context.Background(), objectKey, obj); err != nil {
return false, err
}
unstruct := &unstructured.Unstructured{}
unstructuredObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
if err != nil {
return false, err
}
unstruct.Object = unstructuredObj
s, err := kstatus.Compute(unstruct)
// Check if job then check if completed
obj, ok := obj.(*batchv1.Job)
if ok {
// TODO TO FIX ?
if obj.Status.Succeeded > 0 { // || job.Status.Failed > types.Int32(job.Spec.BackoffLimit) {
return true, nil
}
if obj.Status.Failed > *obj.Spec.BackoffLimit {
return true, errors.New("failed")
}
return false, nil
}
if err != nil {
return false, err
}
if s.Status == kstatus.FailedStatus {
return true, errors.New("failed")
}
if s.Status == kstatus.UnknownStatus {
return true, errors.New("unkown")
}
if s.Status == kstatus.InProgressStatus {
return false, nil
}
return true, nil
})
}
......@@ -16,86 +16,53 @@ limitations under the License.
package settings
import (
"sort"
"strings"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"k8s.libre.sh/application/settings/parameters"
"k8s.libre.sh/interfaces"
"k8s.libre.sh/meta"
"k8s.libre.sh/objects"
"k8s.libre.sh/objects/configmap"
"k8s.libre.sh/objects/secret"
)
const (
// Generate settings as env variables in a configMap or Secret
GenEnvFile SettingsGenerate = "envFile"
)
"sigs.k8s.io/controller-runtime/pkg/client"
const (
// Generate settings as env variables in a configMap or Secret
SecretSettings SettingsType = "secret"
corev1 "k8s.io/api/core/v1"
)
type SettingsGenerate string
type SettingsType string
// +kubebuilder:object:generate=true
type Component struct {
CommonMeta *meta.ObjectMeta `json:"commonMeta,omitempty"`
SecretMeta *meta.ObjectMeta `json:"secretMeta,omitempty"`
ConfigMeta *meta.ObjectMeta `json:"configMeta,omitempty"`
// DefaultType parameters.ParameterType `json:"defaultType,omitempty"`
// DefaultMountType parameters.MountType `json:"defaultMountType,omitempty"`
// Template string json:"template,omitempty"`
Generate SettingsGenerate `json:"generate,omitempty"`
SettingsType SettingsType `json:"type,omitempty"`
*ConfigSpec `json:",inline"`
type Component interface {
GetMeta() meta.Instance
GetConfig() Config
SetDefaults()
Config
GetCreateOptions() *CreateOptions
GetObjects() map[int]objects.Object
}
// ConfigSpec defines a list of parameters and references to resources from which those parameters can be fetched
// Only secrets and configmaps in the same namespace are supported as references
// +kubebuilder:object:generate=true
type SettingsSpec struct {
Sources []Source `json:"sources,omitempty"`
// Parameters is a list of parameters
*parameters.Parameters `json:"parameters,omitempty"`
}
// TemplateValues returns the component as values for the template function
func TemplateValues(s Component) map[string]interface{} {
type CreateOptions struct {
CommonMeta *meta.ObjectMeta `json:"commonMeta,omitempty"`
SecretMeta *meta.ObjectMeta `json:"secretMeta,omitempty"`
ConfigMeta *meta.ObjectMeta `json:"configMeta,omitempty"`
// DefaultType parameters.ParameterType `json:"defaultType,omitempty"`
// DefaultMountType parameters.MountType `json:"defaultMountType,omitempty"`
// Template string json:"template,omitempty"`
Generate SettingsGenerate `json:"generate,omitempty"`
SettingsType SettingsType `json:"type,omitempty"`
}
func (s *Component) GetConfig() Config { return s.ConfigSpec }
func (s *Component) GetMeta() meta.Instance { return s.CommonMeta }
func (s *Component) GetSecretMeta() meta.Instance { return s.SecretMeta }
values := make(map[string]interface{})
func (s *Component) GetConfigMapMeta() meta.Instance { return s.ConfigMeta }
keyPairValues := parameters.KeyPairValues(s.GetConfig().GetParameters())
values[s.GetMeta().GetComponent()] = keyPairValues
func (s *Component) SetDefaults() {
// TODO TO FIX DUPLICATE WITH INIT
meta.SetObjectMeta(s.CommonMeta, s.SecretMeta)
meta.SetObjectMeta(s.CommonMeta, s.ConfigMeta)
return values
}
func (s *Component) GetObjects() map[int]objects.Object {
// GetObjects returns an ordered list of objects.Object interfaces.
// Annotations are added for the generated parameters.
// Only secrets and configmaps are supported.
func GetObjects(s Component) map[int]objects.Object {
cm := &configmap.ConfigMap{
ObjectMeta: s.ConfigMeta,
ObjectMeta: s.GetCreateOptions().ConfigMeta,
Data: s.GetParameters().GetConfigData(),
}
secret := &secret.Secret{
ObjectMeta: s.SecretMeta,
ObjectMeta: s.GetCreateOptions().SecretMeta,
Data: s.GetParameters().GetSecretData(),
}
objs := make(map[int]objects.Object, 2)
......@@ -103,201 +70,179 @@ func (s *Component) GetObjects() map[int]objects.Object {
genConfigParams := []string{}
genSecretParams := []string{}
for _, p := range *s.ConfigSpec.Parameters {
// TODO TO FIX
if p.MountType == parameters.MountEnvFile &&
// TODO TO FIX
(p.Type == parameters.SecretParameter || p.Type == "") &&
len(p.Value) > 0 {
if p.Generate != parameters.GenerateTemplate && p.Generate != "" {
for _, p := range *s.GetConfig().GetParameters() {
if p.IsMount() && len(p.Value) > 0 && p.IsRand() {
switch p.Type {
case parameters.SecretParameter, "":
genSecretParams = append(genSecretParams, p.Key)
}
secret.Parameters = append(secret.Parameters, p)
}
if p.MountType == parameters.MountEnvFile &&
p.Type == parameters.ConfigParameter &&
len(p.Value) > 0 {
if p.Generate != parameters.GenerateTemplate && p.Generate != "" {
case parameters.ConfigParameter:
genConfigParams = append(genConfigParams, p.Key)
}
cm.Parameters = append(cm.Parameters, p)
}
}
// Sort annotations as it is sorted in kubernetes and will have a diff otherwise
sort.Strings(genConfigParams)
sort.Strings(genSecretParams)
// TODO TO FIX remove annotation if secret is not generated anymore
if len(genSecretParams) > 0 {
if len(secret.ObjectMeta.Annotations) == 0 {
secret.ObjectMeta.Annotations = make(map[string]string)
secret.ObjectMeta.Annotations = make(map[string]string, 1)
}
secret.ObjectMeta.Annotations["settings.k8s.libre.sh/generate"] = strings.Join(genSecretParams, ",")
}
if len(genConfigParams) > 0 {
if len(secret.ObjectMeta.Annotations) == 0 {
cm.ObjectMeta.Annotations = make(map[string]string)
if len(cm.ObjectMeta.Annotations) == 0 {
cm.ObjectMeta.Annotations = make(map[string]string, 1)
}
secret.ObjectMeta.Annotations["settings.k8s.libre.sh/generate"] = strings.Join(genConfigParams, ",")
cm.ObjectMeta.Annotations["settings.k8s.libre.sh/generate"] = strings.Join(genConfigParams, ",")
}
// TODO IMPROVE
if len(cm.Parameters) > 0 {
objs[0] = cm
if len(secret.Data) > 0 {
objs[0] = secret
}
if len(secret.Parameters) > 0 {
if objs[0] == nil {
objs[0] = secret
} else {
objs[1] = secret
}
if len(cm.Data) > 0 {
objs[1] = cm
}
return objs
}
func (s *Component) Init(c client.Client) error {
// TODO TO FIX DUPLICATE WITH SETDEFAULTS
meta.SetObjectMeta(s.CommonMeta, s.ConfigMeta)
meta.SetObjectMeta(s.CommonMeta, s.SecretMeta)
}
err := InitParametersValueFrom(s, c)
func GetGenAnnotionForType(ps *parameters.Parameters, pType parameters.ParameterType) map[string]string {
list := []string{}
annot := make(map[string]string)
if err != nil {
return err
for _, p := range *ps {
if p.IsMount() && len(p.Value) > 0 && p.IsRand() && p.Type == pType {
list = append(list, p.Key)
}
}
s.InitRandValues()
err = s.InitTemplateValues(parameters.KeyPairValues(s.Parameters))
if err != nil {
return err
if len(list) > 0 {
annot["settings.k8s.libre.sh/generate"] = strings.Join(list, ",")
}
return nil
}
return annot
// InitParametersValueFrom intialise the parameters with values provided in external resources in the same namespace
// All parameters values are filled from those resources
// Only Secrets and Configmaps are supported
func InitParametersValueFrom(s *Component, c client.Client) error {
}
params := parameters.Parameters{}
func GetConfigGenAnnotion(ps *parameters.Parameters) map[string]string {
list := []string{}
annot := make(map[string]string)
paramsByKey := parameters.ParametersByKey(s.Parameters)
paramsBySecretSource, paramsByConfigMapSource := OrderByResourceRef(s.ConfigSpec)
for _, p := range *ps {
if p.IsMount() && len(p.Value) > 0 && p.IsRand() && p.Type == parameters.ConfigParameter {
list = append(list, p.Key)
}
}
cm := &corev1.ConfigMap{}
sec := &corev1.Secret{}
if len(list) > 0 {
annot["settings.k8s.libre.sh/generate"] = strings.Join(list, ",")
}
cm.SetNamespace(s.CommonMeta.GetNamespace())
sec.SetNamespace(s.CommonMeta.GetNamespace())
return annot
}
for k, v := range paramsBySecretSource {
sec.SetName(k)
func GetSecretGenAnnotion(ps *parameters.Parameters) map[string]string {
list := []string{}
annot := make(map[string]string)
err := parameters.GetAndMergeParameters(v, paramsByKey, c, sec)
if err != nil {
return err
for _, p := range *ps {
if p.IsMount() && len(p.Value) > 0 && p.IsRand() && (p.Type == parameters.SecretParameter || p.Type == "") {
list = append(list, p.Key)
}
}
for k, v := range paramsByConfigMapSource {
cm.SetName(k)
err := parameters.GetAndMergeParameters(v, paramsByKey, c, cm)
if err != nil {
return err
}
if len(list) > 0 {
annot["settings.k8s.libre.sh/generate"] = strings.Join(list, ",")
}
secretSrc := Source{
Ref: s.GetSecretMeta().GetName(),
Type: "secret",
}
configSrc := Source{
Ref: s.GetSecretMeta().GetName(),
Type: "configmap",
}
return annot
}
if s.Generate == GenEnvFile {
// s.SecretRefs = []string{s.GetSecretMeta().GetName()}
// s.ConfigRefs = []string{s.GetConfigMapMeta().GetName()}
func Init(s Component, c client.Client, owner interfaces.Object) error {
s.Sources = []Source{}
opts := s.GetCreateOptions()
if opts == nil {
opts = &CreateOptions{}
}
opts.Init()
if len(paramsByConfigMapSource) > 0 {
s.Sources = append(s.Sources, configSrc)
}
meta.SetObjectMeta(opts.CommonMeta, opts.ConfigMeta)
meta.SetObjectMeta(opts.CommonMeta, opts.SecretMeta)
if len(paramsBySecretSource) > 0 {
s.Sources = append(s.Sources, secretSrc)
}
err := InitParametersValueFrom(s, c, owner)
for _, v := range paramsByKey {
v.MountType = parameters.MountEnvFile
v.Ref = ""
params = append(params, v)
}
s.Parameters = &params
if err != nil {
return err
}
return nil
s.GetParameters().InitRandValues()
if err != nil {
return err
}
addSecretSrc := false
addConfigSrc := false
return nil
}
for _, v := range paramsByKey {
if v.MountType == parameters.MountEnvFile {
v.Ref = ""
// InitParametersValueFrom intialise the parameters with values provided in external resources in the same namespace.
// All parameters values are filled from those resources and new parameters are appended.
// Only Secrets and Configmaps are supported.
func InitParametersValueFrom(s Component, c client.Client, owner interfaces.Object) error {
if !addConfigSrc && v.Type == parameters.ConfigParameter {
s.Sources = append(s.Sources, configSrc)
addConfigSrc = true
} else if !addSecretSrc && v.Type == parameters.SecretParameter {
s.Sources = append(s.Sources, secretSrc)
addSecretSrc = true
}
}
params = append(params, v)
err := s.GetParameters().InitValueFrom(c, owner)
if err != nil {
return err
}
s.Parameters = &params
ps, err := ParametersFromSources(s.GetSources(), c, owner)
s.GetParameters().Merge(ps)
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SettingsSpec) DeepCopyInto(out *SettingsSpec) {
*out = *in
if in.Sources != nil {
in, out := &in.Sources, &out.Sources
*out = make([]Source, len(*in))
copy(*out, *in)
}
if in.Parameters != nil {
in, out := &in.Parameters, &out.Parameters
*out = new(parameters.Parameters)
if **in != nil {
in, out := *in, *out
*out = make([]*parameters.Parameter, len(*in))
for i := range *in {
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = new(parameters.Parameter)
**out = **in
}
}
// ParametersFromSources returns the parameters from external resources that are provided in the sources.
// Owner is provided to check if resources is owned in that case, only generated data defined in the annotations are fetched.
// Only Secrets and Configmaps are supported.
func ParametersFromSources(srcs *Sources, c client.Client, owner interfaces.Object) (*parameters.Parameters, error) {
var obj interfaces.Object
ps := &parameters.Parameters{}
for _, src := range *srcs {
switch src.Type {
case "configmap":
obj = &corev1.ConfigMap{}
case "secret":
obj = &corev1.Secret{}
default:
return ps, nil
}
obj.SetName(src.Ref)
obj.SetNamespace(owner.GetNamespace())
}
data, err := parameters.GetDataFromResource(c, obj, owner)
if err != nil {
return ps, err
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SettingsSpec.
func (in *SettingsSpec) DeepCopy() *SettingsSpec {
if in == nil {
return nil
if len(data) > 0 {
for k, v := range data {
*ps = append(*ps, &parameters.Parameter{
Value: v,
Key: k,
})
}
}
out := new(SettingsSpec)
in.DeepCopyInto(out)
return out
return ps, nil
}
......@@ -21,12 +21,7 @@ import (
type Config interface {
GetParameters() *parameters.Parameters
SetParameters(*parameters.Parameters)
InitRandValues()
InitTemplateValues(map[string]string) error
// InitExternaValues(c client.Client) error
SetSources(sources []Source)
GetSources() (sources []Source)
GetSources() *Sources
}
func MergeSettings(src, dest Config) Config {
......@@ -34,18 +29,27 @@ func MergeSettings(src, dest Config) Config {
if dest == nil {
dest = src
} else {
dest.SetSources(append(dest.GetSources(), src.GetSources()...))
destSrcs := dest.GetSources()
srcSrcs := src.GetSources()
params := *dest.GetParameters()
if srcSrcs != nil {
for _, src := range *srcSrcs {
destSrcs = AppendSourceIfUnique(destSrcs, src)
}
if destSrcs != nil {
destSrcs.DeepCopyInto(dest.GetSources())
}
}
params := dest.GetParameters()
if params == nil {
params = *src.GetParameters()
params = src.GetParameters()
} else {
for _, p := range *src.GetParameters() {
params = append(params, p)
*params = append(*params, p)
}
}
dest.SetParameters(&params)
}
return dest
......
......@@ -58,7 +58,7 @@ type Parameters []*Parameter
// +kubebuilder:object:generate=true
type Parameter struct {
// Key of the parameter, can be mounted as as an environment variable, used in template
// or as in the data fied of configmap/secret
// or as the key in the key:value data fied in the configmap or secret
//
// Key must be unique
Key string `json:"key,omitempty"`
......
......@@ -21,7 +21,7 @@ import (
corev1 "k8s.io/api/core/v1"
)
// GetEnvVar gets an environment variables to set in the container.
// GetEnvVar return the EnvVar to set in the container.
func (p *Parameter) GetEnvVar() (envVar corev1.EnvVar, err error) {
switch p.MountType {
case MountLiteral:
......@@ -85,10 +85,70 @@ func (p *Parameter) GetEnvVar() (envVar corev1.EnvVar, err error) {
}
}
func (p *Parameter) GetPodVolume() *corev1.Volume {
return nil
// GetPodVolume return the Volume to set in the pod.
func (p *Parameter) GetPodVolume() corev1.Volume {
volumeSource := corev1.VolumeSource{}
if p.Type == ConfigParameter {
volumeSource = corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: p.ValueFrom.Ref,
},
Items: []corev1.KeyToPath{
{
Key: p.Key,
Path: p.MountPath.SubPath,
},
},
},
}
} else {
volumeSource = corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: p.ValueFrom.Ref,
Items: []corev1.KeyToPath{
{
Key: p.Key,
Path: p.MountPath.SubPath,
},
},
},
}
}
volume := corev1.Volume{
Name: p.Key,
VolumeSource: volumeSource,
}
return volume
}
// GetVolumeMount returns the VolumeMount to set in the container.
func (p *Parameter) GetVolumeMount() corev1.VolumeMount {
return corev1.VolumeMount{
Name: p.Key,
ReadOnly: true,
MountPath: p.MountPath.Path,
SubPath: p.MountPath.SubPath,
}
}
func (p *Parameter) GetVolumeMount() *corev1.VolumeMount {
return nil
// IsRand if the the parameter is randomly generated.
func (p *Parameter) IsRand() bool {
// TODO if should generate random, is we add other types it will fail
if p.Generate != GenerateTemplate && p.Generate != "" {
return true
}
return false
}
// IsMount returns if the parameter is mounted in a secret or configmap
func (p *Parameter) IsMount() bool {
if p.MountType == MountEnvFile || p.MountType == MountFile {
return true
}
return false
}
package parameters_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
. "k8s.libre.sh/application/settings/parameters"
corev1 "k8s.io/api/core/v1"
)
var _ = Describe("Parameter", func() {
BeforeEach(func() {
/* paramTemplateLiteral = Parameter{
Key: "TEMPLATE_PARAMETER",
Value: "{{ .LITERAL_PARAMETER }}",
Type: ConfigParameter,
Generate: GenerateTemplate,
MountType: MountLiteral,
} */
/* paramTemplateFrom = Parameter{
Key: "TEMPLATE_PARAMETER_FROM",
Value: "{{ .LITERAL_PARAMETER }}",
Type: ConfigParameter,
Generate: GenerateTemplate,
MountType: MountEnvFile,
} */
})
Describe("Generating Environment Variables for containers from parameter", func() {
Context("MountType is Literal", func() {
It("should generate envVar", func() {
paramEnvVar := Parameter{
Key: "LITERAL_PARAMETER",
Value: "LiteralParameter",
Type: ConfigParameter,
MountType: MountLiteral,
}
expectedObj := corev1.EnvVar{
Name: "LITERAL_PARAMETER",
Value: "LiteralParameter",
}
res, err := paramEnvVar.GetEnvVar()
Expect(res).To(Equal(expectedObj))
Expect(err).NotTo(HaveOccurred())
})
})
Context("MountType is From", func() {
It("should generate envVar with valueFrom ConfigMap", func() {
valueFrom := ValueFrom{
FromKey: "FROM_KEY_CONFIG_PARAMETER",
Ref: "configRef",
RefType: "configmap",
}
paramEnvVarFrom := Parameter{
Key: "VALUE_FROM_CONFIG_PARAMETER",
ValueFrom: valueFrom,
Type: ConfigParameter,
MountType: MountFrom,
}
expectedObj := corev1.EnvVar{
Name: "VALUE_FROM_CONFIG_PARAMETER",
ValueFrom: &corev1.EnvVarSource{
ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "configRef",
},
Key: "FROM_KEY_CONFIG_PARAMETER",
},
},
}
res, err := paramEnvVarFrom.GetEnvVar()
Expect(res).To(Equal(expectedObj))
Expect(err).NotTo(HaveOccurred())
})
It("should generate envVar with valueFrom Secret", func() {
valueFromSecret := ValueFrom{
FromKey: "FROM_KEY_SECRET_PARAMETER",
Ref: "secretRef",
RefType: "secret",
}
paramEnvVarFromSecret := Parameter{
Key: "VALUE_FROM_SECRET_PARAMETER",
ValueFrom: valueFromSecret,
Type: SecretParameter,
MountType: MountFrom,
}
expectedObj := corev1.EnvVar{
Name: "VALUE_FROM_SECRET_PARAMETER",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "secretRef",
},
Key: "FROM_KEY_SECRET_PARAMETER",
},
},
}
res, err := paramEnvVarFromSecret.GetEnvVar()
Expect(res).To(Equal(expectedObj))
Expect(err).NotTo(HaveOccurred())
})
})
Context("MountType is ObjectField", func() {
It("should generate envVar with valueFrom", func() {
valueFromObject := ValueFrom{
FromKey: "status.podIP",
Ref: "v1",
}
paramEnvVarFromObject := Parameter{
Key: "PARAMETER_FROM_OBJECT",
Type: ObjectFieldParameter,
ValueFrom: valueFromObject,
MountType: MountFrom,
}
expectedObj := corev1.EnvVar{
Name: "PARAMETER_FROM_OBJECT",
ValueFrom: &corev1.EnvVarSource{
FieldRef: &corev1.ObjectFieldSelector{
APIVersion: "v1",
FieldPath: "status.podIP",
},
},
}
res, err := paramEnvVarFromObject.GetEnvVar()
Expect(res).To(Equal(expectedObj))
Expect(err).NotTo(HaveOccurred())
})
})
})
Describe("Generating mount files", func() {
var paramMountFile Parameter
BeforeEach(func() {
paramMountFile = Parameter{
Key: "nginx-conf",
ValueFrom: ValueFrom{
Ref: "mountfile-source",
},
Type: ConfigParameter,
Value: "example",
MountType: MountFile,
MountPath: MountPath{
Path: "/ect/nginx/nginx.conf",
SubPath: "nginx.conf",
},
}
})
Context("Generating volumes & volume mount", func() {
It("should generate volume for pod from configmap", func() {
expectedObj := corev1.Volume{
Name: "nginx-conf",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: "mountfile-source",
},
Items: []corev1.KeyToPath{
{
Key: "nginx-conf",
Path: "nginx.conf",
},
},
},
},
}
Expect(paramMountFile.GetPodVolume()).To(Equal(expectedObj))
})
It("should generate volume for pod from secret", func() {
paramMountFile.Type = SecretParameter
expectedObj := corev1.Volume{
Name: "nginx-conf",
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: "mountfile-source",
Items: []corev1.KeyToPath{
{
Key: "nginx-conf",
Path: "nginx.conf",
},
},
},
},
}
Expect(paramMountFile.GetPodVolume()).To(Equal(expectedObj))
})
It("should generate volumeMount for container from configmap", func() {
expectedObj := corev1.VolumeMount{
Name: "nginx-conf",
ReadOnly: true,
MountPath: "/ect/nginx/nginx.conf",
SubPath: "nginx.conf",
}
Expect(paramMountFile.GetVolumeMount()).To(Equal(expectedObj))
})
})
})
/* Describe("Generating Env for containers with EnvFrom", func() {
Context("Generating env", func() {
It("should generate envFrom", func() {
expectedObj := []corev1.EnvFromSource{
{
ConfigMapRef: &corev1.ConfigMapEnvSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: "myconfigmap",
},
},
},
{
SecretRef: &corev1.SecretEnvSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: "mysecret",
},
},
},
{
SecretRef: &corev1.SecretEnvSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: "myothersecret",
},
},
},
}
res := settings.GetEnvFrom()
Expect(res).To(Equal(expectedObj))
// Expect(err).NotTo(HaveOccurred())
})
})
}) */
})
......@@ -17,23 +17,20 @@ package parameters
import (
"bytes"
"errors"
"sort"
"text/template"
"github.com/presslabs/controller-util/rand"
corev1 "k8s.io/api/core/v1"
"k8s.libre.sh/interfaces"
"sigs.k8s.io/controller-runtime/pkg/client"
)
func (p *Parameters) InitValues() {
p.InitRandValues()
p.InitTemplateValues(KeyPairValues(p))
}
// InitRandValues initialies the parameters random values
func (p *Parameters) InitRandValues() {
for _, param := range *p {
if len(param.Key) > 0 && len(param.Value) == 0 && param.Generate != GenerateTemplate && param.Type != ObjectFieldParameter {
if len(param.Key) > 0 && param.IsRand() && len(param.Value) == 0 {
var size int
switch param.Generate {
case GenerateRand24:
......@@ -51,13 +48,14 @@ func (p *Parameters) InitRandValues() {
param.Value = random
}
}
}
}
// InitTemplateValues initialies the parameters values from a template and a key pair value set
// The template shoud be in the parameter value when GenerateTemplate is specified and the values
// will be replaced from the result of the template processing.
func (p *Parameters) InitTemplateValues(values map[string]string) error {
// The template shoud be in the parameter value when GenerateTemplate is specified.
// The values will be replaced from the result of the template processing.
func (p *Parameters) InitTemplateValues(values map[string]interface{}) error {
for _, param := range *p {
if param.Generate == GenerateTemplate {
......@@ -80,15 +78,13 @@ func (p *Parameters) InitTemplateValues(values map[string]string) error {
return nil
}
// GetData get the data for the secret or configmap as a key:values
// GetData returns the data for the secret or configmap as a key:value pairs
// This function only returns data which should be either in a secret or a configmap
// It implements the configmap and secret interfaces
func (p *Parameters) GetData() map[string]string {
data := make(map[string]string)
for _, param := range *p {
// TODO TO FIX
if (param.MountType == MountEnvFile || param.MountType == "") &&
if param.IsMount() &&
param.Type != ObjectFieldParameter &&
len(param.Value) > 0 {
data[param.Key] = param.Value
......@@ -98,21 +94,56 @@ func (p *Parameters) GetData() map[string]string {
return data
}
// GetEnvVar gets a list of environment variables to set in the container.
// GetConfigData returns the parameters of type ConfigParameter as key:value pairs
// It implements the configmap interface
func (p *Parameters) GetConfigData() map[string]string {
data := make(map[string]string)
for _, param := range *p {
if param.IsMount() &&
param.Type == ConfigParameter &&
len(param.Value) > 0 {
data[param.Key] = param.Value
}
}
return data
}
// GetSecretData returns the parameters of type ConfigParameter as key:value pairs
// It implements the secret interface
func (p *Parameters) GetSecretData() map[string][]byte {
data := make(map[string][]byte)
for _, param := range *p {
if param.IsMount() &&
// TODO TO FIX ?
(param.Type == SecretParameter || param.Type == "") &&
len(param.Value) > 0 {
data[param.Key] = []byte(param.Value)
}
}
return data
}
// GetEnvVar return a list of EnvVar to set in the container.
func (p *Parameters) GetEnvVar() []corev1.EnvVar {
envVars := []corev1.EnvVar{}
envVar := corev1.EnvVar{}
var err error
for _, param := range *p {
envVar, err = param.GetEnvVar()
// TODO TOFIX
if err != nil {
}
if p != nil {
for _, param := range *p {
envVar, err = param.GetEnvVar()
// TODO TOFIX
if err != nil {
}
if len(envVar.Name) > 0 {
envVars = append(envVars, envVar)
if len(envVar.Name) > 0 {
envVars = append(envVars, envVar)
}
}
}
......@@ -126,3 +157,139 @@ func (p *Parameters) GetEnvVar() []corev1.EnvVar {
}
return nil
}
// GetVolumeMounts returns a list of VolumeMount to set in the container.
func (p *Parameters) GetVolumeMounts() []corev1.VolumeMount {
volumeMounts := []corev1.VolumeMount{}
if p != nil {
for _, param := range *p {
if param.MountType == MountFile {
volumeMount := param.GetVolumeMount()
if len(volumeMount.Name) > 0 {
volumeMounts = append(volumeMounts, volumeMount)
}
}
}
}
if len(volumeMounts) == 0 {
return nil
}
return volumeMounts
}
// GetPodVolumes returns a list of Volume to set in the pod.
func (p *Parameters) GetPodVolumes() []corev1.Volume {
volumes := []corev1.Volume{}
if p != nil {
for _, param := range *p {
if param.MountType == MountFile {
volume := param.GetPodVolume()
if len(volume.Name) > 0 {
volumes = append(volumes, volume)
}
}
}
}
if len(volumes) == 0 {
return nil
}
return volumes
}
// MergeData merges the parameters with a set of data provided as key:values pairs.
// Data values takes over parameters values and are appended to the parameters if not set.
func (ps *Parameters) MergeData(data map[string]string) error {
paramsByKey := ByKey(ps)
if len(data) > 0 {
if len(*ps) > 0 {
for _, p := range *ps {
if len(string(data[p.FromKey])) == 0 {
return errors.New("parameter is not in resource data")
}
paramsByKey[p.Key].Value = string(data[p.FromKey])
// TODO to fix, is it needed if data is generated only if value is empty ?
// Reset default Generate
paramsByKey[p.Key].Generate = ""
}
// TODO remove ?
} else {
for k, v := range data {
*ps = append(*ps, &Parameter{
Value: v,
Key: k,
})
}
}
}
return nil
}
// Merge mergers parameters with new parameters.
// The new parameters provided takes over the parameters and are appended to the parameters if not set.
func (ps *Parameters) Merge(in *Parameters) error {
inByKey := ByKey(in)
destByKey := ByKey(ps)
for k, p := range inByKey {
if destByKey[k] != nil {
p.DeepCopyInto(destByKey[k])
} else {
*ps = append(*ps, p)
}
}
return nil
}
// InitValueFrom intialise the parameters value from external resources
// Only secrets and configmaps are supported
func (ps *Parameters) InitValueFrom(c client.Client, owner interfaces.Object) error {
var obj interfaces.Object
sorted := OrderByResourceRef(ps)
for ptype, byRef := range sorted {
switch ptype {
case ConfigParameter:
obj = &corev1.ConfigMap{}
case SecretParameter:
obj = &corev1.Secret{}
default:
return nil
}
for ref, params := range byRef {
obj.SetName(ref)
obj.SetNamespace(owner.GetNamespace())
data, err := GetDataFromResource(c, obj, owner)
if err != nil {
return err
}
err = params.MergeData(data)
if err != nil {
return err
}
}
}
return nil
}