Skip to content

Commit f733c8e

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 d2a9878 commit f733c8e

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:
@@ -135,22 +131,9 @@ func makeDeploymentSpec(request requests.CreateFunctionRequest, existingSecrets
135131
probe = nil
136132
}
137133

138-
initialReplicas := int32p(initialReplicasCount)
139-
labels := map[string]string{
140-
"faas_function": request.Service,
141-
}
142-
143-
if request.Labels != nil {
144-
if min := getMinReplicaCount(*request.Labels); min != nil {
145-
initialReplicas = min
146-
}
147-
for k, v := range *request.Labels {
148-
labels[k] = v
149-
}
150-
}
151-
134+
initialReplicas := getMinReplicaCount(request.Labels)
135+
labels := parseLabels(request.Service, request.Labels)
152136
nodeSelector := createSelector(request.Constraints)
153-
154137
resources, resourceErr := createResources(request)
155138

156139
if resourceErr != nil {
@@ -178,7 +161,7 @@ func makeDeploymentSpec(request requests.CreateFunctionRequest, existingSecrets
178161
Spec: v1beta1.DeploymentSpec{
179162
Selector: &metav1.LabelSelector{
180163
MatchLabels: map[string]string{
181-
"faas_function": request.Service,
164+
OFFunctionNameLabel: request.Service,
182165
},
183166
},
184167
Replicas: initialReplicas,
@@ -245,7 +228,7 @@ func makeServiceSpec(request requests.CreateFunctionRequest) *v1.Service {
245228
Spec: v1.ServiceSpec{
246229
Type: v1.ServiceTypeClusterIP,
247230
Selector: map[string]string{
248-
"faas_function": request.Service,
231+
OFFunctionNameLabel: request.Service,
249232
},
250233
Ports: []v1.ServicePort{
251234
{
@@ -346,16 +329,3 @@ func createResources(request requests.CreateFunctionRequest) (*apiv1.ResourceReq
346329

347330
return resources, nil
348331
}
349-
350-
func getMinReplicaCount(labels map[string]string) *int32 {
351-
if value, exists := labels["com.openfaas.scale.min"]; exists {
352-
minReplicas, err := strconv.Atoi(value)
353-
if err == nil && minReplicas > 0 {
354-
return int32p(int32(minReplicas))
355-
}
356-
357-
log.Println(err)
358-
}
359-
360-
return nil
361-
}

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
@@ -52,23 +52,12 @@ func MakeUpdateHandler(functionNamespace string, clientset *kubernetes.Clientset
5252

5353
deployment.Spec.Template.Spec.NodeSelector = createSelector(request.Constraints)
5454

55-
labels := map[string]string{
56-
"faas_function": request.Service,
57-
"uid": fmt.Sprintf("%d", time.Now().Nanosecond()),
58-
}
59-
60-
if request.Labels != nil {
61-
if min := getMinReplicaCount(*request.Labels); min != nil {
62-
deployment.Spec.Replicas = min
63-
}
64-
65-
for k, v := range *request.Labels {
66-
labels[k] = v
67-
}
68-
}
55+
labels := parseLabels(request.Service, request.Labels)
56+
labels["uid"] = fmt.Sprintf("%d", time.Now().Nanosecond())
6957

7058
deployment.Labels = labels
7159
deployment.Spec.Template.ObjectMeta.Labels = labels
60+
deployment.Spec.Replicas = getMinReplicaCount(request.Labels)
7261

7362
resources, resourceErr := createResources(request)
7463
if resourceErr != nil {

0 commit comments

Comments
 (0)