Skip to content

Commit b154215

Browse files
committed
Refactor label parsing to set OF labels last
**What** - Ensure that internal OF labels are set after user labels to ensure that users do no override these internal values - Refactor the getMinReplicaCount to work with the labels pointer, this helps simplify the control flow in the handler methods - Add constants for the label keys and update all references to those keys throughout Closes openfaas#209 Signed-off-by: Lucas Roesler <[email protected]>
1 parent a65b904 commit b154215

File tree

6 files changed

+155
-50
lines changed

6 files changed

+155
-50
lines changed

handlers/delete.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ func MakeDeleteHandler(functionNamespace string, clientset *kubernetes.Clientset
6464

6565
func isFunction(deployment *v1beta1.Deployment) bool {
6666
if deployment != nil {
67-
if _, found := deployment.Labels["faas_function"]; found {
67+
if _, found := deployment.Labels[OFFunctionNameLabel]; found {
6868
return true
6969
}
7070
}

handlers/deploy.go

Lines changed: 4 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212
"os"
1313
"path/filepath"
1414
"regexp"
15-
"strconv"
1615
"strings"
1716

1817
"github.com/openfaas/faas/gateway/requests"
@@ -28,9 +27,6 @@ import (
2827
// watchdogPort for the OpenFaaS function watchdog
2928
const watchdogPort = 8080
3029

31-
// initialReplicasCount how many replicas to start of creating for a function
32-
const initialReplicasCount = 1
33-
3430
// ValidateDeployRequest validates that the service name is valid for Kubernetes
3531
func ValidateDeployRequest(request *requests.CreateFunctionRequest) error {
3632
// Regex for RFC-1123 validation:
@@ -164,22 +160,9 @@ func makeDeploymentSpec(request requests.CreateFunctionRequest, existingSecrets
164160
FailureThreshold: 3,
165161
}
166162

167-
initialReplicas := int32p(initialReplicasCount)
168-
labels := map[string]string{
169-
"faas_function": request.Service,
170-
}
171-
172-
if request.Labels != nil {
173-
if min := getMinReplicaCount(*request.Labels); min != nil {
174-
initialReplicas = min
175-
}
176-
for k, v := range *request.Labels {
177-
labels[k] = v
178-
}
179-
}
180-
163+
initialReplicas := getMinReplicaCount(request.Labels)
164+
labels := parseLabels(request.Service, request.Labels)
181165
nodeSelector := createSelector(request.Constraints)
182-
183166
resources, resourceErr := createResources(request)
184167

185168
if resourceErr != nil {
@@ -209,7 +192,7 @@ func makeDeploymentSpec(request requests.CreateFunctionRequest, existingSecrets
209192
Spec: v1beta1.DeploymentSpec{
210193
Selector: &metav1.LabelSelector{
211194
MatchLabels: map[string]string{
212-
"faas_function": request.Service,
195+
OFFunctionNameLabel: request.Service,
213196
},
214197
},
215198
Replicas: initialReplicas,
@@ -282,7 +265,7 @@ func makeServiceSpec(request requests.CreateFunctionRequest) *v1.Service {
282265
Spec: v1.ServiceSpec{
283266
Type: v1.ServiceTypeClusterIP,
284267
Selector: map[string]string{
285-
"faas_function": request.Service,
268+
OFFunctionNameLabel: request.Service,
286269
},
287270
Ports: []v1.ServicePort{
288271
{
@@ -396,19 +379,6 @@ func createResources(request requests.CreateFunctionRequest) (*apiv1.ResourceReq
396379
return resources, nil
397380
}
398381

399-
func getMinReplicaCount(labels map[string]string) *int32 {
400-
if value, exists := labels["com.openfaas.scale.min"]; exists {
401-
minReplicas, err := strconv.Atoi(value)
402-
if err == nil && minReplicas > 0 {
403-
return int32p(int32(minReplicas))
404-
}
405-
406-
log.Println(err)
407-
}
408-
409-
return nil
410-
}
411-
412382
// configureReadOnlyRootFilesystem will create or update the required settings and mounts to ensure
413383
// that the ReadOnlyRootFilesystem setting works as expected, meaning:
414384
// 1. when ReadOnlyRootFilesystem is true, the security context of the container will have ReadOnlyRootFilesystem also

handlers/labels.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package handlers
2+
3+
import (
4+
"log"
5+
"strconv"
6+
)
7+
8+
const (
9+
// initialReplicasCount how many replicas to start of creating for a function, this is
10+
// also used as the default return value for getMinReplicaCount
11+
initialReplicasCount = 1
12+
13+
// OFFunctionNameLabel is the label key used by OpenFaaS to store the function name
14+
// on the resources managed by OpenFaaS for that function. This key is also used to
15+
// denote that a resource is a "Function"
16+
OFFunctionNameLabel = "faas_function"
17+
// OFFunctionMinReplicaCount is a label that user's can set and will be passed to Kubernetes
18+
// as the Deployment replicas value.
19+
OFFunctionMinReplicaCount = "com.openfaas.scale.min"
20+
)
21+
22+
// parseLabels will copy the user request labels and ensure that any required internal labels
23+
// are set appropriately.
24+
func parseLabels(functionName string, requestLables *map[string]string) map[string]string {
25+
labels := map[string]string{}
26+
if requestLables != nil {
27+
for k, v := range *requestLables {
28+
labels[k] = v
29+
}
30+
}
31+
32+
labels[OFFunctionNameLabel] = functionName
33+
34+
return labels
35+
}
36+
37+
// getMinReplicaCount extracts the functions minimum replica count from the user's
38+
// request labels. If the value is not found, this will return the default value, 1.
39+
func getMinReplicaCount(labels *map[string]string) *int32 {
40+
if labels == nil {
41+
return int32p(initialReplicasCount)
42+
}
43+
44+
l := *labels
45+
if value, exists := l[OFFunctionMinReplicaCount]; exists {
46+
minReplicas, err := strconv.Atoi(value)
47+
if err == nil && minReplicas > 0 {
48+
return int32p(int32(minReplicas))
49+
}
50+
51+
log.Println(err)
52+
}
53+
54+
return int32p(initialReplicasCount)
55+
}

handlers/lables_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package handlers
2+
3+
import "testing"
4+
5+
func Test_getMinReplicaCount(t *testing.T) {
6+
scenarios := []struct {
7+
name string
8+
labels *map[string]string
9+
output int
10+
}{
11+
{
12+
name: "nil map returns default",
13+
labels: nil,
14+
output: initialReplicasCount,
15+
},
16+
{
17+
name: "empty map returns default",
18+
labels: &map[string]string{},
19+
output: initialReplicasCount,
20+
},
21+
{
22+
name: "empty map returns default",
23+
labels: &map[string]string{OFFunctionMinReplicaCount: "2"},
24+
output: 2,
25+
},
26+
}
27+
28+
for _, s := range scenarios {
29+
t.Run(s.name, func(t *testing.T) {
30+
output := getMinReplicaCount(s.labels)
31+
if output == nil {
32+
t.Errorf("getMinReplicaCount should not return nil pointer")
33+
}
34+
35+
value := int(*output)
36+
if value != s.output {
37+
t.Errorf("expected: %d, got %d", s.output, value)
38+
}
39+
})
40+
}
41+
}
42+
43+
func Test_parseLabels(t *testing.T) {
44+
scenarios := []struct {
45+
name string
46+
labels *map[string]string
47+
functionName string
48+
output map[string]string
49+
}{
50+
{
51+
name: "nil map returns just the function name",
52+
labels: nil,
53+
functionName: "testFunc",
54+
output: map[string]string{OFFunctionNameLabel: "testFunc"},
55+
},
56+
{
57+
name: "empty map returns just the function name",
58+
labels: &map[string]string{},
59+
functionName: "testFunc",
60+
output: map[string]string{OFFunctionNameLabel: "testFunc"},
61+
},
62+
{
63+
name: "non-empty map does not overwrite the function name label",
64+
labels: &map[string]string{OFFunctionNameLabel: "anotherValue", "customLabel": "test"},
65+
functionName: "testFunc",
66+
output: map[string]string{OFFunctionNameLabel: "testFunc", "customLabel": "test"},
67+
},
68+
}
69+
70+
for _, s := range scenarios {
71+
t.Run(s.name, func(t *testing.T) {
72+
output := parseLabels(s.functionName, s.labels)
73+
if output == nil {
74+
t.Errorf("parseLabels should not return nil map")
75+
}
76+
77+
outputFuncName := output[OFFunctionNameLabel]
78+
if outputFuncName != s.functionName {
79+
t.Errorf("parseLabels should always set the function name: expected %s, got %s", s.functionName, outputFuncName)
80+
}
81+
82+
for key, value := range output {
83+
expectedValue := s.output[key]
84+
if value != expectedValue {
85+
t.Errorf("Incorrect output label for %s, expected: %s, got %s", key, expectedValue, value)
86+
}
87+
}
88+
89+
})
90+
}
91+
}

handlers/reader.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ func getServiceList(functionNamespace string, clientset *kubernetes.Clientset) (
3939
functions := []requests.Function{}
4040

4141
listOpts := metav1.ListOptions{
42-
LabelSelector: "faas_function",
42+
LabelSelector: OFFunctionNameLabel,
4343
}
4444

4545
res, err := clientset.ExtensionsV1beta1().Deployments(functionNamespace).List(listOpts)

handlers/update.go

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -70,23 +70,12 @@ func updateDeploymentSpec(
7070

7171
deployment.Spec.Template.Spec.NodeSelector = createSelector(request.Constraints)
7272

73-
labels := map[string]string{
74-
"faas_function": request.Service,
75-
"uid": fmt.Sprintf("%d", time.Now().Nanosecond()),
76-
}
77-
78-
if request.Labels != nil {
79-
if min := getMinReplicaCount(*request.Labels); min != nil {
80-
deployment.Spec.Replicas = min
81-
}
82-
83-
for k, v := range *request.Labels {
84-
labels[k] = v
85-
}
86-
}
73+
labels := parseLabels(request.Service, request.Labels)
74+
labels["uid"] = fmt.Sprintf("%d", time.Now().Nanosecond())
8775

8876
deployment.Labels = labels
8977
deployment.Spec.Template.ObjectMeta.Labels = labels
78+
deployment.Spec.Replicas = getMinReplicaCount(request.Labels)
9079

9180
deployment.Annotations = annotations
9281
deployment.Spec.Template.Annotations = annotations

0 commit comments

Comments
 (0)