Adds labels and annotations from func.yaml to pipeline resources (#1205)

* adds labels from func.yaml to pipeline resources

* Unify function labels across resources (kService and tekton pipelines)

* Adds function annotations to pipeline resources

* Remove refactored function
This commit is contained in:
Gab Satchi 2022-09-09 08:48:25 -04:00 committed by GitHub
parent 17625b7c0d
commit 7e96146840
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 216 additions and 67 deletions

View File

@ -11,6 +11,8 @@ import (
"time"
"gopkg.in/yaml.v2"
fnlabels "knative.dev/kn-plugin-func/k8s/labels"
"knative.dev/pkg/ptr"
)
// FunctionFile is the file used for the serialized form of a function.
@ -344,6 +346,61 @@ func (f Function) ImageWithDigest() string {
return part1 + strings.Split(part2, ":")[0] + "@" + f.ImageDigest
}
// LabelsMap combines default labels with the labels slice provided.
// It will the resulting slice with ValidateLabels and return a key/value map.
// - key: EXAMPLE1 # Label directly from a value
// value: value1
// - key: EXAMPLE2 # Label from the local ENV var
// value: {{ env:MY_ENV }}
func (f Function) LabelsMap() (map[string]string, error) {
defaultLabels := []Label{
{
Key: ptr.String(fnlabels.FunctionKey),
Value: ptr.String(fnlabels.FunctionValue),
},
{
Key: ptr.String(fnlabels.FunctionNameKey),
Value: ptr.String(f.Name),
},
{
Key: ptr.String(fnlabels.FunctionRuntimeKey),
Value: ptr.String(f.Runtime),
},
// --- handle usage of deprecated labels (`boson.dev/function`, `boson.dev/runtime`)
{
Key: ptr.String(fnlabels.DeprecatedFunctionKey),
Value: ptr.String(fnlabels.FunctionValue),
},
{
Key: ptr.String(fnlabels.DeprecatedFunctionRuntimeKey),
Value: ptr.String(f.Runtime),
},
// --- end of handling usage of deprecated runtime labels
}
labels := append(defaultLabels, f.Labels...)
if err := ValidateLabels(labels); len(err) != 0 {
return nil, errors.New(strings.Join(err, " "))
}
l := map[string]string{}
for _, label := range labels {
if label.Value == nil {
l[*label.Key] = ""
} else {
if strings.HasPrefix(*label.Value, "{{") {
// env variable format is validated above in ValidateLabels
match := regLocalEnv.FindStringSubmatch(*label.Value)
l[*label.Key] = os.Getenv(match[1])
} else {
l[*label.Key] = *label.Value
}
}
}
return l, nil
}
// assertEmptyRoot ensures that the directory is empty enough to be used for
// initializing a new function.
func assertEmptyRoot(path string) (err error) {

View File

@ -4,7 +4,11 @@
package function
import (
"reflect"
"testing"
fnlabels "knative.dev/kn-plugin-func/k8s/labels"
. "knative.dev/kn-plugin-func/testing"
)
func TestFunction_ImageWithDigest(t *testing.T) {
@ -82,3 +86,126 @@ func TestFunction_ImageName(t *testing.T) {
}
}
}
func Test_LabelsMap(t *testing.T) {
key1 := "key1"
key2 := "key2"
value1 := "value1"
value2 := "value2"
defer WithEnvVar(t, "BAD_EXAMPLE", ":invalid")()
valueLocalEnvIncorrect4 := "{{env:BAD_EXAMPLE}}"
defer WithEnvVar(t, "GOOD_EXAMPLE", "valid")()
valueLocalEnv4 := "{{env:GOOD_EXAMPLE}}"
tests := []struct {
name string
labels []Label
expectErr bool
expectedMap map[string]string
}{
{
name: "invalid Labels should return err",
labels: []Label{
{
Value: &value1,
},
},
expectErr: true,
},
{
name: "with valid env var",
labels: []Label{
{
Key: &key1,
Value: &valueLocalEnv4,
},
},
expectErr: false,
expectedMap: map[string]string{
key1: "valid",
},
},
{
name: "with invalid env var",
labels: []Label{
{
Key: &key1,
Value: &valueLocalEnvIncorrect4,
},
},
expectErr: true,
},
{
name: "empty labels allowed. returns default labels",
labels: []Label{
{
Key: &key1,
},
},
expectErr: false,
expectedMap: map[string]string{
key1: "",
},
},
{
name: "full set of labels",
labels: []Label{
{
Key: &key1,
Value: &value1,
},
{
Key: &key2,
Value: &value2,
},
},
expectErr: false,
expectedMap: map[string]string{
key1: value1,
key2: value2,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f := Function{
Name: "some-function",
Runtime: "golang",
Labels: tt.labels,
}
got, err := f.LabelsMap()
if tt.expectErr {
if err == nil {
t.Error("expected error but didn't get an error from LabelsMap")
}
} else {
if err != nil {
t.Errorf("got unexpected err: %s", err)
}
}
if err == nil {
defaultLabels := expectedDefaultLabels(f)
for k, v := range defaultLabels {
tt.expectedMap[k] = v
}
if res := reflect.DeepEqual(got, tt.expectedMap); !res {
t.Errorf("mismatch in actual and expected labels return. actual: %#v, expected: %#v", got, tt.expectedMap)
}
}
})
}
}
func expectedDefaultLabels(f Function) map[string]string {
return map[string]string{
fnlabels.FunctionKey: fnlabels.FunctionValue,
fnlabels.FunctionNameKey: f.Name,
fnlabels.FunctionRuntimeKey: f.Runtime,
fnlabels.DeprecatedFunctionKey: fnlabels.FunctionValue,
fnlabels.DeprecatedFunctionRuntimeKey: f.Runtime,
}
}

View File

@ -17,7 +17,7 @@ func GetPersistentVolumeClaim(ctx context.Context, name, namespaceOverride strin
return client.CoreV1().PersistentVolumeClaims(namespace).Get(ctx, name, metav1.GetOptions{})
}
func CreatePersistentVolumeClaim(ctx context.Context, name, namespaceOverride string, labels map[string]string, accessMode corev1.PersistentVolumeAccessMode, resourceRequest resource.Quantity) (err error) {
func CreatePersistentVolumeClaim(ctx context.Context, name, namespaceOverride string, labels map[string]string, annotations map[string]string, accessMode corev1.PersistentVolumeAccessMode, resourceRequest resource.Quantity) (err error) {
client, namespace, err := NewClientAndResolvedNamespace(namespaceOverride)
if err != nil {
return
@ -25,9 +25,10 @@ func CreatePersistentVolumeClaim(ctx context.Context, name, namespaceOverride st
pvc := &corev1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Labels: labels,
Name: name,
Namespace: namespace,
Labels: labels,
Annotations: annotations,
},
Spec: corev1.PersistentVolumeClaimSpec{
AccessModes: []corev1.PersistentVolumeAccessMode{accessMode},

View File

@ -83,7 +83,7 @@ func DeleteSecrets(ctx context.Context, namespaceOverride string, listOptions me
return client.CoreV1().Secrets(namespace).DeleteCollection(ctx, metav1.DeleteOptions{}, listOptions)
}
func EnsureDockerRegistrySecretExist(ctx context.Context, name, namespaceOverride string, labels map[string]string, username, password, server string) (err error) {
func EnsureDockerRegistrySecretExist(ctx context.Context, name, namespaceOverride string, labels map[string]string, annotations map[string]string, username, password, server string) (err error) {
client, namespace, err := NewClientAndResolvedNamespace(namespaceOverride)
if err != nil {
return
@ -110,9 +110,10 @@ func EnsureDockerRegistrySecretExist(ctx context.Context, name, namespaceOverrid
if createSecret || !bytes.Equal(currentSecret.Data[secretKey], dockerConfigJSONContent) {
secret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Labels: labels,
Name: name,
Namespace: namespace,
Labels: labels,
Annotations: annotations,
},
Type: corev1.SecretTypeOpaque,
Data: map[string][]byte{},

View File

@ -22,7 +22,6 @@ import (
fn "knative.dev/kn-plugin-func"
"knative.dev/kn-plugin-func/k8s"
"knative.dev/kn-plugin-func/k8s/labels"
)
const LIVENESS_ENDPOINT = "/health/liveness"
@ -294,10 +293,13 @@ func generateNewService(f fn.Function, decorator DeployDecorator) (*v1.Service,
}
container.VolumeMounts = newVolumeMounts
labels, err := processLabels(f, decorator)
labels, err := f.LabelsMap()
if err != nil {
return nil, err
}
if decorator != nil {
labels = decorator.UpdateLabels(f, labels)
}
annotations := f.Annotations
if decorator != nil {
@ -374,10 +376,14 @@ func updateService(f fn.Function, newEnv []corev1.EnvVar, newEnvFrom []corev1.En
return service, err
}
labels, err := processLabels(f, decorator)
labels, err := f.LabelsMap()
if err != nil {
return service, err
return nil, err
}
if decorator != nil {
labels = decorator.UpdateLabels(f, labels)
}
service.ObjectMeta.Labels = labels
service.Spec.Template.ObjectMeta.Labels = labels
@ -395,54 +401,6 @@ func updateService(f fn.Function, newEnv []corev1.EnvVar, newEnvFrom []corev1.En
}
}
// processLabels generates a map of labels as key/value pairs from a function config
// labels:
// - key: EXAMPLE1 # Label directly from a value
// value: value1
// - key: EXAMPLE2 # Label from the local ENV var
// value: {{ env:MY_ENV }}
func processLabels(f fn.Function, decorator DeployDecorator) (map[string]string, error) {
labels := map[string]string{
labels.FunctionKey: labels.FunctionValue,
labels.FunctionNameKey: f.Name,
labels.FunctionRuntimeKey: f.Runtime,
// --- handle usage of deprecated labels (`boson.dev/function`, `boson.dev/runtime`)
labels.DeprecatedFunctionKey: labels.FunctionValue,
labels.DeprecatedFunctionRuntimeKey: f.Runtime,
// --- end of handling usage of deprecated runtime labels
}
if decorator != nil {
labels = decorator.UpdateLabels(f, labels)
}
for _, label := range f.Labels {
if label.Key != nil && label.Value != nil {
if strings.HasPrefix(*label.Value, "{{") {
slices := strings.Split(strings.Trim(*label.Value, "{} "), ":")
if len(slices) == 2 {
// label from the local ENV var, eg. author={{ env:$USER }}
localValue, err := processLocalEnvValue(*label.Value)
if err != nil {
return nil, err
}
labels[*label.Key] = localValue
continue
}
} else {
// a standard label with key and value, eg. author=alice@example.com
labels[*label.Key] = *label.Value
continue
}
} else if label.Key != nil && label.Value == nil {
labels[*label.Key] = ""
}
}
return labels, nil
}
// processEnvs generates array of EnvVars and EnvFromSources from a function config
// envs:
// - name: EXAMPLE1 # ENV directly from a value

View File

@ -19,7 +19,7 @@ import (
fn "knative.dev/kn-plugin-func"
"knative.dev/kn-plugin-func/docker"
"knative.dev/kn-plugin-func/k8s"
"knative.dev/kn-plugin-func/k8s/labels"
fnlabels "knative.dev/kn-plugin-func/k8s/labels"
"knative.dev/kn-plugin-func/knative"
"knative.dev/pkg/apis"
)
@ -97,12 +97,15 @@ func (pp *PipelinesProvider) Run(ctx context.Context, f fn.Function) error {
pp.namespace = namespace
// let's specify labels that will be applied to every resouce that is created for a Pipeline
labels := map[string]string{labels.FunctionNameKey: f.Name}
labels, err := f.LabelsMap()
if err != nil {
return err
}
if pp.decorator != nil {
labels = pp.decorator.UpdateLabels(f, labels)
}
err = k8s.CreatePersistentVolumeClaim(ctx, getPipelinePvcName(f), pp.namespace, labels, corev1.ReadWriteOnce, *resource.NewQuantity(DefaultPersistentVolumeClaimSize, resource.DecimalSI))
err = k8s.CreatePersistentVolumeClaim(ctx, getPipelinePvcName(f), pp.namespace, labels, f.Annotations, corev1.ReadWriteOnce, *resource.NewQuantity(DefaultPersistentVolumeClaimSize, resource.DecimalSI))
if err != nil {
if !errors.IsAlreadyExists(err) {
return fmt.Errorf("problem creating persistent volume claim: %v", err)
@ -135,7 +138,7 @@ func (pp *PipelinesProvider) Run(ctx context.Context, f fn.Function) error {
registry = authn.DefaultAuthKey
}
err = k8s.EnsureDockerRegistrySecretExist(ctx, getPipelineSecretName(f), pp.namespace, labels, creds.Username, creds.Password, registry)
err = k8s.EnsureDockerRegistrySecretExist(ctx, getPipelineSecretName(f), pp.namespace, labels, f.Annotations, creds.Username, creds.Password, registry)
if err != nil {
return fmt.Errorf("problem in creating secret: %v", err)
}
@ -182,7 +185,7 @@ func (pp *PipelinesProvider) Run(ctx context.Context, f fn.Function) error {
func (pp *PipelinesProvider) Remove(ctx context.Context, f fn.Function) error {
l := k8slabels.SelectorFromSet(k8slabels.Set(map[string]string{labels.FunctionNameKey: f.Name}))
l := k8slabels.SelectorFromSet(k8slabels.Set(map[string]string{fnlabels.FunctionNameKey: f.Name}))
listOptions := metav1.ListOptions{
LabelSelector: l.String(),
}

View File

@ -98,8 +98,9 @@ func generatePipeline(f fn.Function, labels map[string]string) *pplnv1beta1.Pipe
return &pplnv1beta1.Pipeline{
ObjectMeta: v1.ObjectMeta{
Name: pipelineName,
Labels: labels,
Name: pipelineName,
Labels: labels,
Annotations: f.Annotations,
},
Spec: pplnv1beta1.PipelineSpec{
Params: params,
@ -200,6 +201,7 @@ func generatePipelineRun(f fn.Function, labels map[string]string) *pplnv1beta1.P
ObjectMeta: v1.ObjectMeta{
GenerateName: fmt.Sprintf("%s-run-", getPipelineName(f)),
Labels: labels,
Annotations: f.Annotations,
},
Spec: pplnv1beta1.PipelineRunSpec{
PipelineRef: &pplnv1beta1.PipelineRef{