Labels and annotation support for functions and environments (#2113)

1. Support for adding labels and annotations to functions & environment via fission CLI through create & update command.
2. Change ensures labels and annotations assigned to the environment would reflect on pods created via executor type pool manager and new deploy.

Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
This commit is contained in:
Sanket Sudake
2021-07-15 17:22:01 +05:30
committed by GitHub
parent 2e0bb9304a
commit ece0475808
12 changed files with 135 additions and 24 deletions
@@ -47,6 +47,7 @@ import (
fetcherConfig "github.com/fission/fission/pkg/fetcher/config" fetcherConfig "github.com/fission/fission/pkg/fetcher/config"
"github.com/fission/fission/pkg/throttler" "github.com/fission/fission/pkg/throttler"
"github.com/fission/fission/pkg/utils" "github.com/fission/fission/pkg/utils"
"github.com/fission/fission/pkg/utils/maps"
) )
var _ executortype.ExecutorType = &NewDeploy{} var _ executortype.ExecutorType = &NewDeploy{}
@@ -426,7 +427,7 @@ func (deploy *NewDeploy) fnCreate(fn *fv1.Function) (*fscache.FuncSvc, error) {
objName := deploy.getObjName(fn) objName := deploy.getObjName(fn)
deployLabels := deploy.getDeployLabels(fn.ObjectMeta, env.ObjectMeta) deployLabels := deploy.getDeployLabels(fn.ObjectMeta, env.ObjectMeta)
deployAnnotations := deploy.getDeployAnnotations(fn.ObjectMeta) deployAnnotations := deploy.getDeployAnnotations(fn.ObjectMeta, env.ObjectMeta)
// to support backward compatibility, if the function was created in default ns, we fall back to creating the // to support backward compatibility, if the function was created in default ns, we fall back to creating the
// deployment of the function in fission-function ns // deployment of the function in fission-function ns
@@ -666,7 +667,7 @@ func (deploy *NewDeploy) updateFuncDeployment(fn *fv1.Function, env *fv1.Environ
// Therefore, the deployment update will trigger a rolling update. // Therefore, the deployment update will trigger a rolling update.
newDeployment, err := deploy.getDeploymentSpec(fn, env, newDeployment, err := deploy.getDeploymentSpec(fn, env,
existingDepl.Spec.Replicas, // use current replicas instead of minscale in the ExecutionStrategy. existingDepl.Spec.Replicas, // use current replicas instead of minscale in the ExecutionStrategy.
fnObjName, ns, deployLabels, deploy.getDeployAnnotations(fn.ObjectMeta)) fnObjName, ns, deployLabels, deploy.getDeployAnnotations(fn.ObjectMeta, env.ObjectMeta))
if err != nil { if err != nil {
deploy.updateStatus(fn, err, "failed to get new deployment spec while updating function") deploy.updateStatus(fn, err, "failed to get new deployment spec while updating function")
return err return err
@@ -724,7 +725,7 @@ func (deploy *NewDeploy) getObjName(fn *fv1.Function) string {
} }
func (deploy *NewDeploy) getDeployLabels(fnMeta metav1.ObjectMeta, envMeta metav1.ObjectMeta) map[string]string { func (deploy *NewDeploy) getDeployLabels(fnMeta metav1.ObjectMeta, envMeta metav1.ObjectMeta) map[string]string {
return map[string]string{ deployLabels := map[string]string{
fv1.EXECUTOR_TYPE: string(fv1.ExecutorTypeNewdeploy), fv1.EXECUTOR_TYPE: string(fv1.ExecutorTypeNewdeploy),
fv1.ENVIRONMENT_NAME: envMeta.Name, fv1.ENVIRONMENT_NAME: envMeta.Name,
fv1.ENVIRONMENT_NAMESPACE: envMeta.Namespace, fv1.ENVIRONMENT_NAMESPACE: envMeta.Namespace,
@@ -733,13 +734,17 @@ func (deploy *NewDeploy) getDeployLabels(fnMeta metav1.ObjectMeta, envMeta metav
fv1.FUNCTION_NAMESPACE: fnMeta.Namespace, fv1.FUNCTION_NAMESPACE: fnMeta.Namespace,
fv1.FUNCTION_UID: string(fnMeta.UID), fv1.FUNCTION_UID: string(fnMeta.UID),
} }
for k, v := range envMeta.Labels {
deployLabels[k] = v
}
return deployLabels
} }
func (deploy *NewDeploy) getDeployAnnotations(fnMeta metav1.ObjectMeta) map[string]string { func (deploy *NewDeploy) getDeployAnnotations(fnMeta metav1.ObjectMeta, envMeta metav1.ObjectMeta) map[string]string {
return map[string]string{ deployAnnotations := maps.CopyStringMap(envMeta.Annotations)
fv1.EXECUTOR_INSTANCEID_LABEL: deploy.instanceID, deployAnnotations[fv1.EXECUTOR_INSTANCEID_LABEL] = deploy.instanceID
fv1.FUNCTION_RESOURCE_VERSION: fnMeta.ResourceVersion, deployAnnotations[fv1.FUNCTION_RESOURCE_VERSION] = fnMeta.ResourceVersion
} return deployAnnotations
} }
// updateStatus is a function which updates status of update. // updateStatus is a function which updates status of update.
+11 -10
View File
@@ -50,6 +50,7 @@ import (
fetcherClient "github.com/fission/fission/pkg/fetcher/client" fetcherClient "github.com/fission/fission/pkg/fetcher/client"
fetcherConfig "github.com/fission/fission/pkg/fetcher/config" fetcherConfig "github.com/fission/fission/pkg/fetcher/config"
"github.com/fission/fission/pkg/utils" "github.com/fission/fission/pkg/utils"
"github.com/fission/fission/pkg/utils/maps"
) )
type ( type (
@@ -155,19 +156,19 @@ func MakeGenericPool(
} }
func (gp *GenericPool) getEnvironmentPoolLabels() map[string]string { func (gp *GenericPool) getEnvironmentPoolLabels() map[string]string {
return map[string]string{ envLabels := maps.CopyStringMap(gp.env.ObjectMeta.Labels)
fv1.EXECUTOR_TYPE: string(fv1.ExecutorTypePoolmgr), envLabels[fv1.EXECUTOR_TYPE] = string(fv1.ExecutorTypePoolmgr)
fv1.ENVIRONMENT_NAME: gp.env.ObjectMeta.Name, envLabels[fv1.ENVIRONMENT_NAME] = gp.env.ObjectMeta.Name
fv1.ENVIRONMENT_NAMESPACE: gp.env.ObjectMeta.Namespace, envLabels[fv1.ENVIRONMENT_NAMESPACE] = gp.env.ObjectMeta.Namespace
fv1.ENVIRONMENT_UID: string(gp.env.ObjectMeta.UID), envLabels[fv1.ENVIRONMENT_UID] = string(gp.env.ObjectMeta.UID)
"managed": "true", // this allows us to easily find pods managed by the deployment envLabels["managed"] = "true" // this allows us to easily find pods managed by the deployment
} return envLabels
} }
func (gp *GenericPool) getDeployAnnotations() map[string]string { func (gp *GenericPool) getDeployAnnotations() map[string]string {
return map[string]string{ deployAnnotations := maps.CopyStringMap(gp.env.Annotations)
fv1.EXECUTOR_INSTANCEID_LABEL: gp.instanceID, deployAnnotations[fv1.EXECUTOR_INSTANCEID_LABEL] = gp.instanceID
} return deployAnnotations
} }
func (gp *GenericPool) checkMetricsApi() bool { func (gp *GenericPool) checkMetricsApi() bool {
+9 -4
View File
@@ -31,10 +31,13 @@ func Commands() *cobra.Command {
} }
wrapper.SetFlags(createCmd, flag.FlagSet{ wrapper.SetFlags(createCmd, flag.FlagSet{
Required: []flag.Flag{flag.EnvName, flag.EnvImage}, Required: []flag.Flag{flag.EnvName, flag.EnvImage},
Optional: []flag.Flag{flag.EnvPoolsize, flag.EnvBuilderImage, flag.EnvBuildCmd, Optional: []flag.Flag{
flag.EnvPoolsize, flag.EnvBuilderImage, flag.EnvBuildCmd,
flag.RunTimeMinCPU, flag.RunTimeMaxCPU, flag.RunTimeMinMemory, flag.RunTimeMaxMemory, flag.RunTimeMinCPU, flag.RunTimeMaxCPU, flag.RunTimeMinMemory, flag.RunTimeMaxMemory,
flag.EnvTerminationGracePeriod, flag.EnvVersion, flag.EnvImagePullSecret, flag.EnvTerminationGracePeriod, flag.EnvVersion, flag.EnvImagePullSecret, flag.EnvKeepArchive,
flag.EnvExternalNetwork, flag.EnvKeepArchive, flag.NamespaceEnvironment, flag.SpecSave, flag.SpecDry}, flag.NamespaceEnvironment, flag.EnvExternalNetwork,
flag.Labels, flag.Annotation,
flag.SpecSave, flag.SpecDry},
}) })
getCmd := &cobra.Command{ getCmd := &cobra.Command{
@@ -57,7 +60,9 @@ func Commands() *cobra.Command {
Optional: []flag.Flag{flag.EnvImage, flag.EnvPoolsize, Optional: []flag.Flag{flag.EnvImage, flag.EnvPoolsize,
flag.EnvBuilderImage, flag.EnvBuildCmd, flag.EnvImagePullSecret, flag.EnvBuilderImage, flag.EnvBuildCmd, flag.EnvImagePullSecret,
flag.RunTimeMinCPU, flag.RunTimeMaxCPU, flag.RunTimeMinMemory, flag.RunTimeMaxMemory, flag.RunTimeMinCPU, flag.RunTimeMaxCPU, flag.RunTimeMinMemory, flag.RunTimeMaxMemory,
flag.EnvTerminationGracePeriod, flag.EnvKeepArchive, flag.NamespaceEnvironment, flag.EnvExternalNetwork}, flag.EnvTerminationGracePeriod, flag.EnvKeepArchive,
flag.NamespaceEnvironment, flag.EnvExternalNetwork,
flag.Labels, flag.Annotation},
}) })
deleteCmd := &cobra.Command{ deleteCmd := &cobra.Command{
@@ -179,6 +179,10 @@ func createEnvironmentFromCmd(input cli.Input) (*fv1.Environment, error) {
}, },
} }
err = util.ApplyLabelsAndAnnotations(input, &env.ObjectMeta)
if err != nil {
return nil, err
}
err = env.Validate() err = env.Validate()
if err != nil { if err != nil {
return nil, fv1.AggregateValidationErrors("Environment", err) return nil, fv1.AggregateValidationErrors("Environment", err)
@@ -31,6 +31,7 @@ import (
"github.com/fission/fission/pkg/fission-cli/cmd" "github.com/fission/fission/pkg/fission-cli/cmd"
"github.com/fission/fission/pkg/fission-cli/console" "github.com/fission/fission/pkg/fission-cli/console"
flagkey "github.com/fission/fission/pkg/fission-cli/flag/key" flagkey "github.com/fission/fission/pkg/fission-cli/flag/key"
"github.com/fission/fission/pkg/fission-cli/util"
"github.com/fission/fission/pkg/utils" "github.com/fission/fission/pkg/utils"
) )
@@ -66,6 +67,11 @@ func (opts *UpdateSubCommand) complete(input cli.Input) error {
} }
opts.env = env opts.env = env
err = util.ApplyLabelsAndAnnotations(input, &opts.env.ObjectMeta)
if err != nil {
return err
}
return nil return nil
} }
+2 -2
View File
@@ -36,7 +36,7 @@ func Commands() *cobra.Command {
flag.FnExecutorType, flag.FnCfgMap, flag.FnSecret, flag.FnExecutorType, flag.FnCfgMap, flag.FnSecret,
flag.FnSpecializationTimeout, flag.FnExecutionTimeout, flag.FnSpecializationTimeout, flag.FnExecutionTimeout,
flag.FnIdleTimeout, flag.FnConcurrency, flag.FnRequestsPerPod, flag.FnIdleTimeout, flag.FnConcurrency, flag.FnRequestsPerPod,
flag.FnOnceOnly, flag.FnOnceOnly, flag.Labels, flag.Annotation,
// TODO retired pkg & trigger related flags from function cmd // TODO retired pkg & trigger related flags from function cmd
flag.PkgCode, flag.PkgSrcArchive, flag.PkgDeployArchive, flag.PkgCode, flag.PkgSrcArchive, flag.PkgDeployArchive,
@@ -88,7 +88,7 @@ func Commands() *cobra.Command {
flag.FnExecutorType, flag.FnSecret, flag.FnCfgMap, flag.FnExecutorType, flag.FnSecret, flag.FnCfgMap,
flag.FnSpecializationTimeout, flag.FnExecutionTimeout, flag.FnSpecializationTimeout, flag.FnExecutionTimeout,
flag.FnIdleTimeout, flag.FnConcurrency, flag.FnRequestsPerPod, flag.FnIdleTimeout, flag.FnConcurrency, flag.FnRequestsPerPod,
flag.FnOnceOnly, flag.FnOnceOnly, flag.Labels, flag.Annotation,
flag.PkgCode, flag.PkgSrcArchive, flag.PkgDeployArchive, flag.PkgCode, flag.PkgSrcArchive, flag.PkgDeployArchive,
flag.PkgSrcChecksum, flag.PkgDeployChecksum, flag.PkgInsecure, flag.PkgSrcChecksum, flag.PkgDeployChecksum, flag.PkgInsecure,
+5
View File
@@ -309,6 +309,11 @@ func (opts *CreateSubCommand) complete(input cli.Input) error {
}, },
} }
err = util.ApplyLabelsAndAnnotations(input, &opts.function.ObjectMeta)
if err != nil {
return err
}
return nil return nil
} }
+5
View File
@@ -243,6 +243,11 @@ func (opts *UpdateSubCommand) complete(input cli.Input) error {
opts.function = function opts.function = function
err = util.ApplyLabelsAndAnnotations(input, &opts.function.ObjectMeta)
if err != nil {
return err
}
return nil return nil
} }
+3
View File
@@ -74,6 +74,9 @@ var (
KubeContext = Flag{Type: String, Name: flagkey.KubeContext, Usage: "Kubernetes context to be used for the execution of Fission commands", DefaultValue: ""} KubeContext = Flag{Type: String, Name: flagkey.KubeContext, Usage: "Kubernetes context to be used for the execution of Fission commands", DefaultValue: ""}
Labels = Flag{Type: String, Name: flagkey.Labels, Usage: "Comma separated labels to apply to the function. Eg. --labels=\"environment=dev,application=analytics\""}
Annotation = Flag{Type: StringSlice, Name: flagkey.Annotation, Usage: "Annotation to apply to the function. To mention multiple annotations --annotation=\"abc.com/team=dev\" --annotation=\"foo=bar\""}
NamespaceFunction = Flag{Type: String, Name: flagkey.NamespaceFunction, Aliases: []string{"fns"}, Usage: "Namespace for function object", DefaultValue: metav1.NamespaceDefault} NamespaceFunction = Flag{Type: String, Name: flagkey.NamespaceFunction, Aliases: []string{"fns"}, Usage: "Namespace for function object", DefaultValue: metav1.NamespaceDefault}
NamespaceEnvironment = Flag{Type: String, Name: flagkey.NamespaceEnvironment, Aliases: []string{"envns"}, Usage: "Namespace for environment object", DefaultValue: metav1.NamespaceDefault} NamespaceEnvironment = Flag{Type: String, Name: flagkey.NamespaceEnvironment, Aliases: []string{"envns"}, Usage: "Namespace for environment object", DefaultValue: metav1.NamespaceDefault}
NamespacePackage = Flag{Type: String, Name: flagkey.NamespacePackage, Aliases: []string{"pkgns"}, Usage: "Namespace for package object", DefaultValue: metav1.NamespaceDefault} NamespacePackage = Flag{Type: String, Name: flagkey.NamespacePackage, Aliases: []string{"pkgns"}, Usage: "Namespace for package object", DefaultValue: metav1.NamespaceDefault}
+3
View File
@@ -26,6 +26,9 @@ const (
force = "force" force = "force"
Output = "output" Output = "output"
Labels = "labels"
Annotation = "annotation"
NamespaceFunction = "fnNamespace" NamespaceFunction = "fnNamespace"
NamespaceEnvironment = "envNamespace" NamespaceEnvironment = "envNamespace"
NamespacePackage = "pkgNamespace" NamespacePackage = "pkgNamespace"
+50
View File
@@ -32,6 +32,7 @@ import (
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd"
@@ -350,3 +351,52 @@ func UrlForFunction(name, namespace string) string {
} }
return fmt.Sprintf("%v/%v", prefix, name) return fmt.Sprintf("%v/%v", prefix, name)
} }
func ParseAnnotations(annotations []string) (map[string]string, error) {
var invalidAnnotations string
annotationMap := make(map[string]string)
for _, arg := range annotations {
if strings.Contains(arg, "=") && arg[0] != '=' {
parts := strings.SplitN(arg, "=", 2)
if len(parts) == 2 {
annotationMap[parts[0]] = parts[1]
} else {
if invalidAnnotations != "" {
invalidAnnotations = fmt.Sprintf("%s,%s", invalidAnnotations, arg)
} else {
invalidAnnotations = arg
}
}
} else {
if invalidAnnotations != "" {
invalidAnnotations = fmt.Sprintf("%s,%s", invalidAnnotations, arg)
} else {
invalidAnnotations = arg
}
}
}
if invalidAnnotations != "" {
return nil, errors.Errorf("invalid annotations: %s", invalidAnnotations)
}
return annotationMap, nil
}
func ApplyLabelsAndAnnotations(input cli.Input, objectMeta *metav1.ObjectMeta) error {
labelStr := input.String(flagkey.Labels)
if labelStr != "" {
set, err := labels.ConvertSelectorToLabelsMap(labelStr)
if err != nil {
return err
}
objectMeta.Labels = set
}
annotationStr := input.StringSlice(flagkey.Annotation)
if len(annotationStr) > 0 {
set, err := ParseAnnotations(annotationStr)
if err != nil {
return err
}
objectMeta.Annotations = set
}
return nil
}
+24
View File
@@ -0,0 +1,24 @@
/*
Copyright 2021 The Fission 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.
*/
package maps
func CopyStringMap(m map[string]string) map[string]string {
n := make(map[string]string)
for k, v := range m {
n[k] = v
}
return n
}