diff --git a/deploy/crds/io_v1alpha1_jaeger_cr.yaml b/deploy/crds/io_v1alpha1_jaeger_cr.yaml new file mode 100644 index 000000000..93f25ffa2 --- /dev/null +++ b/deploy/crds/io_v1alpha1_jaeger_cr.yaml @@ -0,0 +1,5 @@ +# see /examples +apiVersion: io.jaegertracing/v1alpha1 +kind: Jaeger +metadata: + name: example-jaeger diff --git a/deploy/crds/io_v1alpha1_jaeger_crd.yaml b/deploy/crds/io_v1alpha1_jaeger_crd.yaml new file mode 100644 index 000000000..987872757 --- /dev/null +++ b/deploy/crds/io_v1alpha1_jaeger_crd.yaml @@ -0,0 +1,13 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: jaegers.io.jaegertracing +spec: + group: io.jaegertracing + names: + kind: Jaeger + listKind: JaegerList + plural: jaegers + singular: jaeger + scope: Namespaced + version: v1alpha1 diff --git a/pkg/apis/addtoscheme_io_v1alpha1.go b/pkg/apis/addtoscheme_io_v1alpha1.go new file mode 100644 index 000000000..1208ee274 --- /dev/null +++ b/pkg/apis/addtoscheme_io_v1alpha1.go @@ -0,0 +1,10 @@ +package apis + +import ( + "github.com/jaegertracing/jaeger-operator/pkg/apis/io/v1alpha1" +) + +func init() { + // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back + AddToSchemes = append(AddToSchemes, v1alpha1.SchemeBuilder.AddToScheme) +} diff --git a/pkg/apis/io/v1alpha1/builder.go b/pkg/apis/io/v1alpha1/builder.go new file mode 100644 index 000000000..f0fdaf740 --- /dev/null +++ b/pkg/apis/io/v1alpha1/builder.go @@ -0,0 +1,12 @@ +package v1alpha1 + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +// NewJaeger returns a new Jaeger instance with the given name +func NewJaeger(name string) *Jaeger { + return &Jaeger{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + } +} diff --git a/pkg/apis/io/v1alpha1/doc.go b/pkg/apis/io/v1alpha1/doc.go new file mode 100644 index 000000000..f58ba2460 --- /dev/null +++ b/pkg/apis/io/v1alpha1/doc.go @@ -0,0 +1,4 @@ +// Package v1alpha1 contains API Schema definitions for the io v1alpha1 API group +// +k8s:deepcopy-gen=package,register +// +groupName=io.jaegertracing +package v1alpha1 diff --git a/pkg/apis/io/v1alpha1/freeform.go b/pkg/apis/io/v1alpha1/freeform.go new file mode 100644 index 000000000..b6757f275 --- /dev/null +++ b/pkg/apis/io/v1alpha1/freeform.go @@ -0,0 +1,40 @@ +package v1alpha1 + +import ( + "encoding/json" +) + +// FreeForm defines a common options parameter that maintains the hierarchical +// structure of the data, unlike Options which flattens the hierarchy into a +// key/value map where the hierarchy is converted to '.' separated items in the key. +type FreeForm struct { + json []byte +} + +// NewFreeForm build a new FreeForm object based on the given map +func NewFreeForm(o map[string]interface{}) FreeForm { + freeForm := FreeForm{} + if o != nil { + freeForm.json, _ = json.Marshal(o) + } + return freeForm +} + +// UnmarshalJSON implements an alternative parser for this field +func (o *FreeForm) UnmarshalJSON(b []byte) error { + o.json = b + return nil +} + +// MarshalJSON specifies how to convert this object into JSON +func (o FreeForm) MarshalJSON() ([]byte, error) { + if len(o.json) == 0 { + return []byte("{}"), nil + } + return o.json, nil +} + +// IsEmpty determines if the freeform options are empty +func (o FreeForm) IsEmpty() bool { + return len(o.json) == 0 || string(o.json) == "{}" +} diff --git a/pkg/apis/io/v1alpha1/freeform_test.go b/pkg/apis/io/v1alpha1/freeform_test.go new file mode 100644 index 000000000..4ddc64f9e --- /dev/null +++ b/pkg/apis/io/v1alpha1/freeform_test.go @@ -0,0 +1,53 @@ +package v1alpha1 + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestFreeForm(t *testing.T) { + uiconfig := `{"es":{"password":"changeme","server-urls":"http://elasticsearch:9200","username":"elastic"}}` + o := NewFreeForm(map[string]interface{}{ + "es": map[string]interface{}{ + "server-urls": "http://elasticsearch:9200", + "username": "elastic", + "password": "changeme", + }, + }) + json, err := o.MarshalJSON() + assert.NoError(t, err) + assert.NotNil(t, json) + assert.Equal(t, uiconfig, string(o.json)) +} + +func TestFreeFormUnmarhalMarshal(t *testing.T) { + uiconfig := `{"es":{"password":"changeme","server-urls":"http://elasticsearch:9200","username":"elastic"}}` + o := NewFreeForm(nil) + o.UnmarshalJSON([]byte(uiconfig)) + json, err := o.MarshalJSON() + assert.NoError(t, err) + assert.NotNil(t, json) + assert.Equal(t, uiconfig, string(o.json)) +} + +func TestFreeFormIsEmptyFalse(t *testing.T) { + o := NewFreeForm(map[string]interface{}{ + "es": map[string]interface{}{ + "server-urls": "http://elasticsearch:9200", + "username": "elastic", + "password": "changeme", + }, + }) + assert.False(t, o.IsEmpty()) +} + +func TestFreeFormIsEmptyTrue(t *testing.T) { + o := NewFreeForm(map[string]interface{}{}) + assert.True(t, o.IsEmpty()) +} + +func TestFreeFormIsEmptyNilTrue(t *testing.T) { + o := NewFreeForm(nil) + assert.True(t, o.IsEmpty()) +} diff --git a/pkg/apis/io/v1alpha1/jaeger_types.go b/pkg/apis/io/v1alpha1/jaeger_types.go new file mode 100644 index 000000000..9f97cdb6a --- /dev/null +++ b/pkg/apis/io/v1alpha1/jaeger_types.go @@ -0,0 +1,202 @@ +package v1alpha1 + +import ( + "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/jaegertracing/jaeger-operator/pkg/storage/elasticsearch/v1alpha1" + esv1alpha1 "github.com/jaegertracing/jaeger-operator/pkg/storage/elasticsearch/v1alpha1" +) + +// IngressSecurityType represents the possible values for the security type +type IngressSecurityType string + +const ( + // FlagPlatformKubernetes represents the value for the 'platform' flag for Kubernetes + FlagPlatformKubernetes = "kubernetes" + + // FlagPlatformOpenShift represents the value for the 'platform' flag for OpenShift + FlagPlatformOpenShift = "openshift" + + // FlagPlatformAutoDetect represents the "auto-detect" value for the platform flag + FlagPlatformAutoDetect = "auto-detect" + + // FlagProvisionElasticsearchAuto represents the 'auto' value for the 'es-provision' flag + FlagProvisionElasticsearchAuto = "auto" + + // FlagProvisionElasticsearchTrue represents the value 'true' for the 'es-provision' flag + FlagProvisionElasticsearchTrue = "true" + + // FlagProvisionElasticsearchFalse represents the value 'false' for the 'es-provision' flag + FlagProvisionElasticsearchFalse = "false" + + // IngressSecurityNone disables any form of security for ingress objects (default) + IngressSecurityNone IngressSecurityType = "" + + // IngressSecurityNoneExplicit used when the user specifically set it to 'none' + IngressSecurityNoneExplicit IngressSecurityType = "none" + + // IngressSecurityOAuthProxy represents an OAuth Proxy as security type + IngressSecurityOAuthProxy IngressSecurityType = "oauth-proxy" +) + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// JaegerList is a list of Jaeger structs +type JaegerList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + Items []Jaeger `json:"items"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// Jaeger defines the main structure for the custom-resource +type Jaeger struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata"` + Spec JaegerSpec `json:"spec"` + Status JaegerStatus `json:"status,omitempty"` +} + +// JaegerSpec defines the structure of the Jaeger JSON object from the CR +type JaegerSpec struct { + Strategy string `json:"strategy"` + AllInOne JaegerAllInOneSpec `json:"allInOne"` + Query JaegerQuerySpec `json:"query"` + Collector JaegerCollectorSpec `json:"collector"` + Ingester JaegerIngesterSpec `json:"ingester"` + Agent JaegerAgentSpec `json:"agent"` + UI JaegerUISpec `json:"ui"` + Sampling JaegerSamplingSpec `json:"sampling"` + Storage JaegerStorageSpec `json:"storage"` + Ingress JaegerIngressSpec `json:"ingress"` + JaegerCommonSpec +} + +// JaegerCommonSpec defines the common elements used in multiple other spec structs +type JaegerCommonSpec struct { + Volumes []v1.Volume `json:"volumes"` + VolumeMounts []v1.VolumeMount `json:"volumeMounts"` + Annotations map[string]string `json:"annotations,omitempty"` + Resources v1.ResourceRequirements `json:"resources,omitempty"` +} + +// JaegerStatus defines what is to be returned from a status query +type JaegerStatus struct { + // CollectorSpansReceived represents sum of the metric jaeger_collector_spans_received_total across all collectors + CollectorSpansReceived int `json:"collectorSpansReceived"` + + // CollectorSpansDropped represents sum of the metric jaeger_collector_spans_dropped_total across all collectors + CollectorSpansDropped int `json:"collectorSpansDropped"` +} + +// JaegerQuerySpec defines the options to be used when deploying the query +type JaegerQuerySpec struct { + Size int `json:"size"` + Image string `json:"image"` + Options Options `json:"options"` + JaegerCommonSpec +} + +// JaegerUISpec defines the options to be used to configure the UI +type JaegerUISpec struct { + Options FreeForm `json:"options"` +} + +// JaegerSamplingSpec defines the options to be used to configure the UI +type JaegerSamplingSpec struct { + Options FreeForm `json:"options"` +} + +// JaegerIngressSpec defines the options to be used when deploying the query ingress +type JaegerIngressSpec struct { + Enabled *bool `json:"enabled"` + Security IngressSecurityType `json:"security"` + JaegerCommonSpec +} + +// JaegerAllInOneSpec defines the options to be used when deploying the query +type JaegerAllInOneSpec struct { + Image string `json:"image"` + Options Options `json:"options"` + JaegerCommonSpec +} + +// JaegerCollectorSpec defines the options to be used when deploying the collector +type JaegerCollectorSpec struct { + Size int `json:"size"` + Image string `json:"image"` + Options Options `json:"options"` + JaegerCommonSpec +} + +// JaegerIngesterSpec defines the options to be used when deploying the ingester +type JaegerIngesterSpec struct { + Size int `json:"size"` + Image string `json:"image"` + Options Options `json:"options"` + JaegerCommonSpec +} + +// JaegerAgentSpec defines the options to be used when deploying the agent +type JaegerAgentSpec struct { + Strategy string `json:"strategy"` // can be either 'DaemonSet' or 'Sidecar' (default) + Image string `json:"image"` + Options Options `json:"options"` + JaegerCommonSpec +} + +// JaegerStorageSpec defines the common storage options to be used for the query and collector +type JaegerStorageSpec struct { + Type string `json:"type"` // can be `memory` (default), `cassandra`, `elasticsearch`, `kafka` or `managed` + SecretName string `json:"secretName"` + Options Options `json:"options"` + CassandraCreateSchema JaegerCassandraCreateSchemaSpec `json:"cassandraCreateSchema"` + SparkDependencies JaegerDependenciesSpec `json:"dependencies"` + EsIndexCleaner JaegerEsIndexCleanerSpec `json:"esIndexCleaner"` + Elasticsearch ElasticsearchSpec `json:"elasticsearch"` +} + +// ElasticsearchSpec represents the ES configuration options that we pass down to the Elasticsearch operator +type ElasticsearchSpec struct { + Resources v1.ResourceRequirements `json:"resources"` + NodeCount int32 `json:"nodeCount"` + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + Storage esv1alpha1.ElasticsearchStorageSpec `json:"storage"` + RedundancyPolicy v1alpha1.RedundancyPolicyType `json:"redundancyPolicy"` +} + +// JaegerCassandraCreateSchemaSpec holds the options related to the create-schema batch job +type JaegerCassandraCreateSchemaSpec struct { + Enabled *bool `json:"enabled"` + Image string `json:"image"` + Datacenter string `json:"datacenter"` + Mode string `json:"mode"` +} + +// JaegerDependenciesSpec defined options for running spark-dependencies. +type JaegerDependenciesSpec struct { + Enabled *bool `json:"enabled"` + SparkMaster string `json:"sparkMaster"` + Schedule string `json:"schedule"` + Image string `json:"image"` + JavaOpts string `json:"javaOpts"` + CassandraUseSsl bool `json:"cassandraUseSsl"` + CassandraLocalDc string `json:"cassandraLocalDc"` + CassandraClientAuthEnabled bool `json:"cassandraClientAuthEnabled"` + ElasticsearchClientNodeOnly bool `json:"elasticsearchClientNodeOnly"` + ElasticsearchNodesWanOnly bool `json:"elasticsearchNodesWanOnly"` +} + +// JaegerEsIndexCleanerSpec holds the options related to es-index-cleaner +type JaegerEsIndexCleanerSpec struct { + Enabled *bool `json:"enabled"` + NumberOfDays int `json:"numberOfDays"` + Schedule string `json:"schedule"` + Image string `json:"image"` +} + +func init() { + SchemeBuilder.Register(&Jaeger{}, &JaegerList{}) +} diff --git a/pkg/apis/io/v1alpha1/logger.go b/pkg/apis/io/v1alpha1/logger.go new file mode 100644 index 000000000..fc4e9b5dc --- /dev/null +++ b/pkg/apis/io/v1alpha1/logger.go @@ -0,0 +1,11 @@ +package v1alpha1 + +import log "github.com/sirupsen/logrus" + +// Logger returns a logger filled with context-related fields, such as Name and Namespace +func (j *Jaeger) Logger() *log.Entry { + return log.WithFields(log.Fields{ + "instance": j.Name, + "namespace": j.Namespace, + }) +} diff --git a/pkg/apis/io/v1alpha1/options.go b/pkg/apis/io/v1alpha1/options.go new file mode 100644 index 000000000..1dd3cab68 --- /dev/null +++ b/pkg/apis/io/v1alpha1/options.go @@ -0,0 +1,94 @@ +package v1alpha1 + +import ( + "encoding/json" + "fmt" + "strings" +) + +// Options defines a common options parameter to the different structs +type Options struct { + opts map[string]string +} + +// NewOptions build a new Options object based on the given map +func NewOptions(o map[string]interface{}) Options { + options := Options{} + options.parse(o) + return options +} + +// Filter creates a new Options object with just the elements identified by the supplied prefix +func (o *Options) Filter(prefix string) Options { + options := Options{} + options.opts = make(map[string]string) + + archivePrefix := prefix + "-archive." + prefix += "." + + for k, v := range o.opts { + if strings.HasPrefix(k, prefix) || strings.HasPrefix(k, archivePrefix) { + options.opts[k] = v + } + } + + return options +} + +// UnmarshalJSON implements an alternative parser for this field +func (o *Options) UnmarshalJSON(b []byte) error { + var entries map[string]interface{} + json.Unmarshal(b, &entries) + + return o.parse(entries) +} + +// MarshalJSON specifies how to convert this object into JSON +func (o Options) MarshalJSON() ([]byte, error) { + b, err := json.Marshal(o.opts) + return b, err +} + +func (o *Options) parse(entries map[string]interface{}) error { + o.opts = make(map[string]string) + + for k, v := range entries { + o.opts = entry(o.opts, k, v) + } + + return nil +} + +func entry(entries map[string]string, key string, value interface{}) map[string]string { + switch value.(type) { + case map[string]interface{}: + for k, v := range value.(map[string]interface{}) { + entries = entry(entries, fmt.Sprintf("%s.%v", key, k), v) + } + case interface{}: + entries[key] = fmt.Sprintf("%v", value) + } + + return entries +} + +// ToArgs converts the options to a value suitable for the Container.Args field +func (o *Options) ToArgs() []string { + if len(o.opts) > 0 { + i := 0 + args := make([]string, len(o.opts)) + for k, v := range o.opts { + args[i] = fmt.Sprintf("--%s=%v", k, v) + i++ + } + return args + } + + return nil +} + +// Map returns a map representing the option entries. Items are flattened, with dots as separators. For instance +// an option "cassandra" with a nested "servers" object becomes an entry with the key "cassandra.servers" +func (o *Options) Map() map[string]string { + return o.opts +} diff --git a/pkg/apis/io/v1alpha1/options_test.go b/pkg/apis/io/v1alpha1/options_test.go new file mode 100644 index 000000000..e36fcd798 --- /dev/null +++ b/pkg/apis/io/v1alpha1/options_test.go @@ -0,0 +1,92 @@ +package v1alpha1 + +import ( + "encoding/json" + "sort" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSimpleOption(t *testing.T) { + o := Options{} + o.UnmarshalJSON([]byte(`{"key": "value"}`)) + args := o.ToArgs() + assert.Equal(t, "--key=value", args[0]) +} + +func TestNoOptions(t *testing.T) { + o := Options{} + assert.Len(t, o.ToArgs(), 0) +} + +func TestNestedOption(t *testing.T) { + o := NewOptions(nil) + o.UnmarshalJSON([]byte(`{"log-level": "debug", "memory": {"max-traces": 10000}}`)) + args := o.ToArgs() + assert.Len(t, args, 2) + + sort.Strings(args) + assert.Equal(t, "--log-level=debug", args[0]) + assert.Equal(t, "--memory.max-traces=10000", args[1]) +} + +func TestMarshalling(t *testing.T) { + o := NewOptions(map[string]interface{}{ + "es.server-urls": "http://elasticsearch.default.svc:9200", + "es.username": "elastic", + "es.password": "changeme", + }) + + b, err := json.Marshal(o) + assert.NoError(t, err) + s := string(b) + assert.Contains(t, s, `"es.password":"changeme"`) + assert.Contains(t, s, `"es.server-urls":"http://elasticsearch.default.svc:9200"`) + assert.Contains(t, s, `"es.username":"elastic"`) +} + +func TestMarshallingWithFilter(t *testing.T) { + o := NewOptions(map[string]interface{}{ + "es.server-urls": "http://elasticsearch.default.svc:9200", + "memory.max-traces": "50000", + }) + o = o.Filter("memory") + args := o.ToArgs() + assert.Len(t, args, 1) + assert.Equal(t, "50000", o.Map()["memory.max-traces"]) +} + +func TestMultipleSubValues(t *testing.T) { + o := NewOptions(nil) + o.UnmarshalJSON([]byte(`{"es": {"server-urls": "http://elasticsearch:9200", "username": "elastic", "password": "changeme"}}`)) + args := o.ToArgs() + assert.Len(t, args, 3) +} + +func TestMultipleSubValuesWithFilter(t *testing.T) { + o := NewOptions(nil) + o.UnmarshalJSON([]byte(`{"memory": {"max-traces": "50000"}, "es": {"server-urls": "http://elasticsearch:9200", "username": "elastic", "password": "changeme"}}`)) + o = o.Filter("memory") + args := o.ToArgs() + assert.Len(t, args, 1) + assert.Equal(t, "50000", o.Map()["memory.max-traces"]) +} + +func TestMultipleSubValuesWithFilterWithArchive(t *testing.T) { + o := NewOptions(nil) + o.UnmarshalJSON([]byte(`{"memory": {"max-traces": "50000"}, "es": {"server-urls": "http://elasticsearch:9200", "username": "elastic", "password": "changeme"}, "es-archive": {"server-urls": "http://elasticsearch2:9200"}}`)) + o = o.Filter("es") + args := o.ToArgs() + assert.Len(t, args, 4) + assert.Equal(t, "http://elasticsearch:9200", o.Map()["es.server-urls"]) + assert.Equal(t, "http://elasticsearch2:9200", o.Map()["es-archive.server-urls"]) + assert.Equal(t, "elastic", o.Map()["es.username"]) + assert.Equal(t, "changeme", o.Map()["es.password"]) +} + +func TestExposedMap(t *testing.T) { + o := NewOptions(nil) + o.UnmarshalJSON([]byte(`{"cassandra": {"servers": "cassandra:9042"}}`)) + assert.Equal(t, "cassandra:9042", o.Map()["cassandra.servers"]) +} diff --git a/pkg/apis/io/v1alpha1/register.go b/pkg/apis/io/v1alpha1/register.go new file mode 100644 index 000000000..4767dc395 --- /dev/null +++ b/pkg/apis/io/v1alpha1/register.go @@ -0,0 +1,19 @@ +// NOTE: Boilerplate only. Ignore this file. + +// Package v1alpha1 contains API Schema definitions for the io v1alpha1 API group +// +k8s:deepcopy-gen=package,register +// +groupName=io.jaegertracing +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/runtime/scheme" +) + +var ( + // SchemeGroupVersion is group version used to register these objects + SchemeGroupVersion = schema.GroupVersion{Group: "io.jaegertracing", Version: "v1alpha1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} +) diff --git a/pkg/apis/io/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/io/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 000000000..a50cfa620 --- /dev/null +++ b/pkg/apis/io/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,466 @@ +// +build !ignore_autogenerated + +/* +Copyright The Kubernetes 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. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1 "k8s.io/api/core/v1" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ElasticsearchSpec) DeepCopyInto(out *ElasticsearchSpec) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.Storage.DeepCopyInto(&out.Storage) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ElasticsearchSpec. +func (in *ElasticsearchSpec) DeepCopy() *ElasticsearchSpec { + if in == nil { + return nil + } + out := new(ElasticsearchSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FreeForm) DeepCopyInto(out *FreeForm) { + *out = *in + if in.json != nil { + in, out := &in.json, &out.json + *out = make([]byte, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FreeForm. +func (in *FreeForm) DeepCopy() *FreeForm { + if in == nil { + return nil + } + out := new(FreeForm) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Jaeger) DeepCopyInto(out *Jaeger) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Jaeger. +func (in *Jaeger) DeepCopy() *Jaeger { + if in == nil { + return nil + } + out := new(Jaeger) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Jaeger) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JaegerAgentSpec) DeepCopyInto(out *JaegerAgentSpec) { + *out = *in + in.Options.DeepCopyInto(&out.Options) + in.JaegerCommonSpec.DeepCopyInto(&out.JaegerCommonSpec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JaegerAgentSpec. +func (in *JaegerAgentSpec) DeepCopy() *JaegerAgentSpec { + if in == nil { + return nil + } + out := new(JaegerAgentSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JaegerAllInOneSpec) DeepCopyInto(out *JaegerAllInOneSpec) { + *out = *in + in.Options.DeepCopyInto(&out.Options) + in.JaegerCommonSpec.DeepCopyInto(&out.JaegerCommonSpec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JaegerAllInOneSpec. +func (in *JaegerAllInOneSpec) DeepCopy() *JaegerAllInOneSpec { + if in == nil { + return nil + } + out := new(JaegerAllInOneSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JaegerCassandraCreateSchemaSpec) DeepCopyInto(out *JaegerCassandraCreateSchemaSpec) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JaegerCassandraCreateSchemaSpec. +func (in *JaegerCassandraCreateSchemaSpec) DeepCopy() *JaegerCassandraCreateSchemaSpec { + if in == nil { + return nil + } + out := new(JaegerCassandraCreateSchemaSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JaegerCollectorSpec) DeepCopyInto(out *JaegerCollectorSpec) { + *out = *in + in.Options.DeepCopyInto(&out.Options) + in.JaegerCommonSpec.DeepCopyInto(&out.JaegerCommonSpec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JaegerCollectorSpec. +func (in *JaegerCollectorSpec) DeepCopy() *JaegerCollectorSpec { + if in == nil { + return nil + } + out := new(JaegerCollectorSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JaegerCommonSpec) DeepCopyInto(out *JaegerCommonSpec) { + *out = *in + if in.Volumes != nil { + in, out := &in.Volumes, &out.Volumes + *out = make([]v1.Volume, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.VolumeMounts != nil { + in, out := &in.VolumeMounts, &out.VolumeMounts + *out = make([]v1.VolumeMount, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.Resources.DeepCopyInto(&out.Resources) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JaegerCommonSpec. +func (in *JaegerCommonSpec) DeepCopy() *JaegerCommonSpec { + if in == nil { + return nil + } + out := new(JaegerCommonSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JaegerDependenciesSpec) DeepCopyInto(out *JaegerDependenciesSpec) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JaegerDependenciesSpec. +func (in *JaegerDependenciesSpec) DeepCopy() *JaegerDependenciesSpec { + if in == nil { + return nil + } + out := new(JaegerDependenciesSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JaegerEsIndexCleanerSpec) DeepCopyInto(out *JaegerEsIndexCleanerSpec) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JaegerEsIndexCleanerSpec. +func (in *JaegerEsIndexCleanerSpec) DeepCopy() *JaegerEsIndexCleanerSpec { + if in == nil { + return nil + } + out := new(JaegerEsIndexCleanerSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JaegerIngesterSpec) DeepCopyInto(out *JaegerIngesterSpec) { + *out = *in + in.Options.DeepCopyInto(&out.Options) + in.JaegerCommonSpec.DeepCopyInto(&out.JaegerCommonSpec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JaegerIngesterSpec. +func (in *JaegerIngesterSpec) DeepCopy() *JaegerIngesterSpec { + if in == nil { + return nil + } + out := new(JaegerIngesterSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JaegerIngressSpec) DeepCopyInto(out *JaegerIngressSpec) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } + in.JaegerCommonSpec.DeepCopyInto(&out.JaegerCommonSpec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JaegerIngressSpec. +func (in *JaegerIngressSpec) DeepCopy() *JaegerIngressSpec { + if in == nil { + return nil + } + out := new(JaegerIngressSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JaegerList) DeepCopyInto(out *JaegerList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Jaeger, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JaegerList. +func (in *JaegerList) DeepCopy() *JaegerList { + if in == nil { + return nil + } + out := new(JaegerList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *JaegerList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JaegerQuerySpec) DeepCopyInto(out *JaegerQuerySpec) { + *out = *in + in.Options.DeepCopyInto(&out.Options) + in.JaegerCommonSpec.DeepCopyInto(&out.JaegerCommonSpec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JaegerQuerySpec. +func (in *JaegerQuerySpec) DeepCopy() *JaegerQuerySpec { + if in == nil { + return nil + } + out := new(JaegerQuerySpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JaegerSamplingSpec) DeepCopyInto(out *JaegerSamplingSpec) { + *out = *in + in.Options.DeepCopyInto(&out.Options) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JaegerSamplingSpec. +func (in *JaegerSamplingSpec) DeepCopy() *JaegerSamplingSpec { + if in == nil { + return nil + } + out := new(JaegerSamplingSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JaegerSpec) DeepCopyInto(out *JaegerSpec) { + *out = *in + in.AllInOne.DeepCopyInto(&out.AllInOne) + in.Query.DeepCopyInto(&out.Query) + in.Collector.DeepCopyInto(&out.Collector) + in.Ingester.DeepCopyInto(&out.Ingester) + in.Agent.DeepCopyInto(&out.Agent) + in.UI.DeepCopyInto(&out.UI) + in.Sampling.DeepCopyInto(&out.Sampling) + in.Storage.DeepCopyInto(&out.Storage) + in.Ingress.DeepCopyInto(&out.Ingress) + in.JaegerCommonSpec.DeepCopyInto(&out.JaegerCommonSpec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JaegerSpec. +func (in *JaegerSpec) DeepCopy() *JaegerSpec { + if in == nil { + return nil + } + out := new(JaegerSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JaegerStatus) DeepCopyInto(out *JaegerStatus) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JaegerStatus. +func (in *JaegerStatus) DeepCopy() *JaegerStatus { + if in == nil { + return nil + } + out := new(JaegerStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JaegerStorageSpec) DeepCopyInto(out *JaegerStorageSpec) { + *out = *in + in.Options.DeepCopyInto(&out.Options) + in.CassandraCreateSchema.DeepCopyInto(&out.CassandraCreateSchema) + in.SparkDependencies.DeepCopyInto(&out.SparkDependencies) + in.EsIndexCleaner.DeepCopyInto(&out.EsIndexCleaner) + in.Elasticsearch.DeepCopyInto(&out.Elasticsearch) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JaegerStorageSpec. +func (in *JaegerStorageSpec) DeepCopy() *JaegerStorageSpec { + if in == nil { + return nil + } + out := new(JaegerStorageSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *JaegerUISpec) DeepCopyInto(out *JaegerUISpec) { + *out = *in + in.Options.DeepCopyInto(&out.Options) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JaegerUISpec. +func (in *JaegerUISpec) DeepCopy() *JaegerUISpec { + if in == nil { + return nil + } + out := new(JaegerUISpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Options) DeepCopyInto(out *Options) { + *out = *in + if in.opts != nil { + in, out := &in.opts, &out.opts + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Options. +func (in *Options) DeepCopy() *Options { + if in == nil { + return nil + } + out := new(Options) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/controller/add_legacy.go b/pkg/controller/add_legacy.go new file mode 100644 index 000000000..9486dd84d --- /dev/null +++ b/pkg/controller/add_legacy.go @@ -0,0 +1,10 @@ +package controller + +import ( + "github.com/jaegertracing/jaeger-operator/pkg/controller/legacy" +) + +func init() { + // AddToManagerFuncs is a list of functions to create controllers and add them to a manager. + AddToManagerFuncs = append(AddToManagerFuncs, legacy.Add) +} diff --git a/pkg/controller/legacy/legacy_controller.go b/pkg/controller/legacy/legacy_controller.go new file mode 100644 index 000000000..356dacac9 --- /dev/null +++ b/pkg/controller/legacy/legacy_controller.go @@ -0,0 +1,84 @@ +package legacy + +import ( + "context" + + log "github.com/sirupsen/logrus" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" + + "github.com/jaegertracing/jaeger-operator/pkg/apis/io/v1alpha1" +) + +// Add creates a new Jaeger Controller and adds it to the Manager. The Manager will set fields on the Controller +// and Start it when the Manager is Started. +func Add(mgr manager.Manager) error { + return add(mgr, newReconciler(mgr)) +} + +// newReconciler returns a new reconcile.Reconciler +func newReconciler(mgr manager.Manager) reconcile.Reconciler { + return &ReconcileJaeger{client: mgr.GetClient(), scheme: mgr.GetScheme()} +} + +// add adds a new Controller to mgr with r as the reconcile.Reconciler +func add(mgr manager.Manager, r reconcile.Reconciler) error { + // Create a new controller + c, err := controller.New("legacy-controller", mgr, controller.Options{Reconciler: r}) + if err != nil { + return err + } + + // Watch for changes to primary resource Jaeger + err = c.Watch(&source.Kind{Type: &v1alpha1.Jaeger{}}, &handler.EnqueueRequestForObject{}) + if err != nil { + return err + } + + return nil +} + +var _ reconcile.Reconciler = &ReconcileJaeger{} + +// ReconcileJaeger reconciles a Jaeger object +type ReconcileJaeger 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 +} + +// Reconcile reads that state of the cluster for a Jaeger object and makes changes based on the state read +// and what is in the Jaeger.Spec +// Note: +// The Controller will requeue the Request to be processed again if the returned error is non-nil or +// Result.Requeue is true, otherwise upon completion it will remove the work from the queue. +func (r *ReconcileJaeger) Reconcile(request reconcile.Request) (reconcile.Result, error) { + // Fetch the Jaeger instance + instance := &v1alpha1.Jaeger{} + err := r.client.Get(context.Background(), request.NamespacedName, instance) + if err != nil { + if errors.IsNotFound(err) { + // Request object not found, could have been deleted after reconcile request. + // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. + // Return and don't requeue + return reconcile.Result{}, nil + } + // Error reading the object - requeue the request. + return reconcile.Result{}, err + } + + log.WithFields(log.Fields{ + "namespace": request.Namespace, + "instance": request.Name, + }).Warn("Attention: you are using a legacy CR for Jaeger. Use the apiVersion 'jaegertracing.io/v1' instead of 'io.jaegertracing/v1alpha1'") + + // reconcile in a few seconds, to get the status object updated + return reconcile.Result{}, nil +}