Skip to content

Commit ca22ec6

Browse files
authored
[#302] add controller-configs (#322)
* add config files * resolve import cycle * resolve import cycle * fix style * fix tests * add more tests and support helm * fix empty test case * fix ci * fix test * update dep * more logs * always remember to re-init * add resourceAnnotations
1 parent 7e87d3a commit ca22ec6

12 files changed

Lines changed: 212 additions & 26 deletions

File tree

.github/workflows/project.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ jobs:
66
runs-on: ubuntu-latest
77
strategy:
88
matrix:
9-
go-version: [1.13, 1.14]
9+
go-version: [1.13, 1.14, 1.15, 1.16, 1.17]
1010
steps:
1111
- name: clean disk
1212
run: |
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: function-mesh-controller-manager-configs
5+
labels:
6+
app.kubernetes.io/name: { { template "function-mesh-operator.name" . } }
7+
app.kubernetes.io/managed-by: { { .Release.Service } }
8+
app.kubernetes.io/instance: { { .Release.Name } }
9+
app.kubernetes.io/component: controller-manager
10+
helm.sh/chart: { { .Chart.Name } }-{{ .Chart.Version | replace "+" "_" }}
11+
data:
12+
configs.yaml: |
13+
{{- if .Values.controllerManager.runnerImages }}
14+
runnerImages:
15+
{{ toYaml .Values.controllerManager.runnerImages | indent 6 }}
16+
{{- end }}
17+
{{- if .Values.controllerManager.resourceLabels }}
18+
resourceLabels:
19+
{{ toYaml .Values.controllerManager.resourceLabels | indent 6 }}
20+
{{- end }}
21+
{{- if .Values.controllerManager.resourceAnnotations }}
22+
resourceAnnotations:
23+
{{ toYaml .Values.controllerManager.resourceAnnotations | indent 6 }}
24+
{{- end }}

charts/function-mesh-operator/templates/controller-manager-deployment.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,18 @@ spec:
3939
{{- end }}
4040
command:
4141
- /manager
42+
args:
43+
- --enable-leader-election
44+
- --config-file=/etc/config/config.yaml
4245
env:
4346
- name: NAMESPACE
4447
valueFrom:
4548
fieldRef:
4649
fieldPath: metadata.namespace
50+
volumeMounts:
51+
- name: cfg
52+
mountPath: /etc/config/config.yaml
53+
subPath: config.yaml
4754
{{- with .Values.controllerManager.nodeSelector }}
4855
nodeSelector:
4956
{{ toYaml . | indent 8 }}
@@ -59,3 +66,8 @@ spec:
5966
{{- if .Values.controllerManager.priorityClassName }}
6067
priorityClassName: {{ .Values.controllerManager.priorityClassName }}
6168
{{- end }}
69+
volumes:
70+
- name: cfg
71+
configMap:
72+
name: function-mesh-controller-manager-configs
73+
defaultMode: 420

charts/function-mesh-operator/values.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,12 @@ controllerManager:
4646
selector: []
4747
# - k1==v1
4848
# - k2!=v2
49+
# default runner images for different language runtime
50+
# runnerImages:
51+
# java: streamnative/pulsar-functions-java-runner:2.9.0.0-rc-4
52+
# python: streamnative/pulsar-functions-python-runner:2.9.0.0-rc-4
53+
# go: streamnative/pulsar-functions-go-runner:2.9.0.0-rc-4
54+
# resource labels applied to each function/connector managed by this controller
55+
# resourceLabels: {}
56+
# resource annotations applied to each function/connector managed by this controller
57+
# resourceAnnotations: {}

controllers/spec/common.go

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,8 @@ func MakePodTemplate(container *corev1.Container, volumes []corev1.Volume,
142142
}
143143
return &corev1.PodTemplateSpec{
144144
ObjectMeta: metav1.ObjectMeta{
145-
Labels: mergeLabels(labels, policy.Labels),
146-
Annotations: generateAnnotations(policy.Annotations),
145+
Labels: mergeLabels(labels, Configs.ResourceLabels, policy.Labels),
146+
Annotations: generateAnnotations(Configs.ResourceAnnotations, policy.Annotations),
147147
},
148148
Spec: corev1.PodSpec{
149149
InitContainers: policy.InitContainers,
@@ -191,17 +191,13 @@ func MakeGoFunctionCommand(downloadPath, goExecFilePath string, function *v1alph
191191
strings.Join(getProcessGoRuntimeArgs(goExecFilePath, function), " ")
192192
if downloadPath != "" {
193193
// prepend download command if the downPath is provided
194-
downloadCommand := strings.Join(getDownloadCommand(downloadPath, goExecFilePath, function.Spec.Pulsar.AuthSecret != "", function.Spec.Pulsar.TLSSecret != ""), " ")
194+
downloadCommand := strings.Join(getDownloadCommand(downloadPath, goExecFilePath,
195+
function.Spec.Pulsar.AuthSecret != "", function.Spec.Pulsar.TLSSecret != ""), " ")
195196
processCommand = downloadCommand + " && ls -al && pwd &&" + processCommand
196197
}
197198
return []string{"sh", "-c", processCommand}
198199
}
199200

200-
func ComputeConfigHash(config map[string]interface{}) (string, error) {
201-
202-
return "", nil
203-
}
204-
205201
func getDownloadCommand(downloadPath, componentPackage string, authProvided, tlsProvided bool) []string {
206202
// The download path is the path that the package saved in the pulsar.
207203
// By default, it's the path that the package saved in the pulsar, we can use package name
@@ -599,30 +595,30 @@ func generatePodVolumes(podVolumes []corev1.Volume, producerConf *v1alpha1.Produ
599595
return volumes
600596
}
601597

602-
func mergeLabels(label1, label2 map[string]string) map[string]string {
603-
label := make(map[string]string)
598+
func mergeLabels(labels ...map[string]string) map[string]string {
599+
merged := make(map[string]string)
604600

605-
for k, v := range label1 {
606-
label[k] = v
607-
}
608-
609-
for k, v := range label2 {
610-
label[k] = v
601+
for _, m := range labels {
602+
for k, v := range m {
603+
merged[k] = v
604+
}
611605
}
612606

613-
return label
607+
return merged
614608
}
615609

616-
func generateAnnotations(customAnnotations map[string]string) map[string]string {
610+
func generateAnnotations(customAnnotations ...map[string]string) map[string]string {
617611
annotations := make(map[string]string)
618612

619613
// controlled annotations
620614
annotations[AnnotationPrometheusScrape] = "true"
621615
annotations[AnnotationPrometheusPort] = strconv.Itoa(int(MetricsPort.ContainerPort))
622616

623617
// customized annotations which may override any previous set annotations
624-
for k, v := range customAnnotations {
625-
annotations[k] = v
618+
for _, custom := range customAnnotations {
619+
for k, v := range custom {
620+
annotations[k] = v
621+
}
626622
}
627623

628624
return annotations
@@ -634,11 +630,11 @@ func getFunctionRunnerImage(spec *v1alpha1.FunctionSpec) string {
634630
if img != "" {
635631
return img
636632
} else if runtime.Java != nil && runtime.Java.Jar != "" {
637-
return DefaultJavaRunnerImage
633+
return Configs.RunnerImages.Java
638634
} else if runtime.Python != nil && runtime.Python.Py != "" {
639-
return DefaultPythonRunnerImage
635+
return Configs.RunnerImages.Python
640636
} else if runtime.Golang != nil && runtime.Golang.Go != "" {
641-
return DefaultGoRunnerImage
637+
return Configs.RunnerImages.Go
642638
}
643639
return DefaultRunnerImage
644640
}
@@ -650,7 +646,7 @@ func getSinkRunnerImage(spec *v1alpha1.SinkSpec) string {
650646
}
651647
if spec.Runtime.Java.Jar != "" && spec.Runtime.Java.JarLocation != "" &&
652648
hasPackageNamePrefix(spec.Runtime.Java.JarLocation) {
653-
return DefaultJavaRunnerImage
649+
return Configs.RunnerImages.Java
654650
}
655651
return DefaultRunnerImage
656652
}
@@ -662,7 +658,7 @@ func getSourceRunnerImage(spec *v1alpha1.SourceSpec) string {
662658
}
663659
if spec.Runtime.Java.Jar != "" && spec.Runtime.Java.JarLocation != "" &&
664660
hasPackageNamePrefix(spec.Runtime.Java.JarLocation) {
665-
return DefaultJavaRunnerImage
661+
return Configs.RunnerImages.Java
666662
}
667663
return DefaultRunnerImage
668664
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package spec
19+
20+
import (
21+
"io/ioutil"
22+
23+
"gopkg.in/yaml.v3"
24+
)
25+
26+
type RunnerImages struct {
27+
Java string `yaml:"java,omitempty"`
28+
Python string `yaml:"python,omitempty"`
29+
Go string `yaml:"go,omitempty"`
30+
}
31+
32+
type ControllerConfigs struct {
33+
RunnerImages RunnerImages `yaml:"runnerImages,omitempty"`
34+
ResourceLabels map[string]string `yaml:"resourceLabels,omitempty"`
35+
ResourceAnnotations map[string]string `yaml:"resourceAnnotations,omitempty"`
36+
}
37+
38+
var Configs = DefaultConfigs()
39+
40+
func DefaultConfigs() *ControllerConfigs {
41+
return &ControllerConfigs{
42+
RunnerImages: RunnerImages{
43+
Java: DefaultJavaRunnerImage,
44+
Python: DefaultPythonRunnerImage,
45+
Go: DefaultGoRunnerImage,
46+
},
47+
}
48+
}
49+
50+
func ParseControllerConfigs(configFilePath string) error {
51+
yamlFile, err := ioutil.ReadFile(configFilePath)
52+
if err != nil {
53+
return err
54+
}
55+
if len(yamlFile) == 0 {
56+
return nil
57+
}
58+
err = yaml.Unmarshal(yamlFile, Configs)
59+
if err != nil {
60+
return err
61+
}
62+
63+
return nil
64+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package spec
19+
20+
import (
21+
"testing"
22+
23+
"gotest.tools/assert"
24+
)
25+
26+
func TestParseConfigFiles(t *testing.T) {
27+
Configs = DefaultConfigs()
28+
err := ParseControllerConfigs("../../testdata/controller_configs.yaml")
29+
if err != nil {
30+
t.Errorf("ParseControllerConfigs failed: %v", err)
31+
}
32+
assert.Assert(t, Configs != nil)
33+
assert.Assert(t, Configs.RunnerImages.Java == "streamnative/pulsar-functions-java-runner:latest")
34+
assert.Assert(t, Configs.RunnerImages.Python == "streamnative/pulsar-functions-python-runner:latest")
35+
assert.Assert(t, Configs.RunnerImages.Go == "streamnative/pulsar-functions-go-runner:latest")
36+
assert.Assert(t, len(Configs.ResourceLabels) == 2)
37+
assert.Assert(t, len(Configs.ResourceAnnotations) == 1)
38+
assert.Assert(t, Configs.ResourceLabels["functionmesh.io/managedBy"] == "function-mesh")
39+
assert.Assert(t, Configs.ResourceLabels["foo"] == "bar")
40+
assert.Assert(t, Configs.ResourceAnnotations["fooAnnotation"] == "barAnnotation")
41+
}
42+
43+
func TestParseEmptyConfigFiles(t *testing.T) {
44+
Configs = DefaultConfigs()
45+
err := ParseControllerConfigs("../../testdata/empty_controller_configs.yaml")
46+
if err != nil {
47+
t.Errorf("ParseControllerConfigs failed: %v", err)
48+
}
49+
assert.Assert(t, Configs != nil)
50+
t.Log("Configs", Configs)
51+
assert.Assert(t, Configs.RunnerImages.Java == DefaultJavaRunnerImage)
52+
assert.Assert(t, Configs.RunnerImages.Python == DefaultPythonRunnerImage)
53+
assert.Assert(t, Configs.RunnerImages.Go == DefaultGoRunnerImage)
54+
assert.Assert(t, len(Configs.ResourceLabels) == 0)
55+
assert.Assert(t, len(Configs.ResourceAnnotations) == 0)
56+
}

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ require (
1313
github.com/streamnative/pulsarctl v0.4.3-0.20220104092115-5af28d815290
1414
github.com/stretchr/testify v1.6.1
1515
google.golang.org/protobuf v1.25.0
16+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
17+
gotest.tools v2.2.0+incompatible
1618
k8s.io/api v0.18.6
1719
k8s.io/apimachinery v0.18.6
1820
k8s.io/client-go v0.18.6

go.sum

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,7 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
784784
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
785785
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
786786
gotest.tools v0.0.0-20181223230014-1083505acf35/go.mod h1:R//lfYlUuTOTfblYI3lGoAAAebUdzjvbmQsuB7Ykd90=
787+
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
787788
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
788789
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
789790
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

main.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import (
2121
"flag"
2222
"os"
2323

24+
"github.com/streamnative/function-mesh/controllers/spec"
25+
2426
"k8s.io/apimachinery/pkg/runtime"
2527
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
2628
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
@@ -50,6 +52,7 @@ func main() {
5052
var leaderElectionID string
5153
var certDir string
5254
var enableLeaderElection bool
55+
var configFile string
5356
flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
5457
flag.StringVar(&leaderElectionID, "leader-election-id", "a3f45fce.functionmesh.io",
5558
"the name of the configmap that leader election will use for holding the leader lock.")
@@ -58,10 +61,20 @@ func main() {
5861
"Enabling this will ensure there is only one active controller manager.")
5962
flag.StringVar(&certDir, "cert-dir", "",
6063
"CertDir is the directory that contains the server key and certificate.\n\tif not set, webhook server would look up the server key and certificate in\n\t{TempDir}/k8s-webhook-server/serving-certs. The server key and certificate\n\tmust be named tls.key and tls.crt, respectively.")
64+
flag.StringVar(&configFile, "config-file", "",
65+
"config file path for controller manager")
6166
flag.Parse()
6267

6368
ctrl.SetLogger(zap.New(zap.UseDevMode(true)))
6469

70+
if configFile != "" {
71+
err := spec.ParseControllerConfigs(configFile)
72+
if err != nil {
73+
setupLog.Error(err, "unable to parse the controller configs")
74+
os.Exit(1)
75+
}
76+
}
77+
6578
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
6679
Scheme: scheme,
6780
MetricsBindAddress: metricsAddr,

0 commit comments

Comments
 (0)