diff --git a/client_int_test.go b/client_int_test.go index 94c628fb9..29d355419 100644 --- a/client_int_test.go +++ b/client_int_test.go @@ -205,7 +205,7 @@ func TestRemoteRepositories(t *testing.T) { func newClient(verbose bool) *fn.Client { builder := buildpacks.NewBuilder(buildpacks.WithVerbose(verbose)) pusher := docker.NewPusher(docker.WithVerbose(verbose)) - deployer := knative.NewDeployer(DefaultNamespace, verbose) + deployer := knative.NewDeployer(knative.WithDeployerNamespace(DefaultNamespace), knative.WithDeployerVerbose(verbose)) remover := knative.NewRemover(DefaultNamespace, verbose) lister := knative.NewLister(DefaultNamespace, verbose) diff --git a/cmd/client.go b/cmd/client.go index 43e8e00f9..8080e0be2 100644 --- a/cmd/client.go +++ b/cmd/client.go @@ -61,10 +61,12 @@ func NewClientFactory(n func() *fn.Client) ClientFactory { // defer done() func NewClient(cfg ClientConfig, options ...fn.Option) (*fn.Client, func()) { var ( - p = progress.New(cfg.Verbose) // updates the CLI - t = newTransport() // may provide a custom impl which proxies - c = newCredentialsProvider(t) // for accessing registries - o = []fn.Option{ // standard (shared) options for all commands + p = progress.New(cfg.Verbose) // updates the CLI + t = newTransport() // may provide a custom impl which proxies + c = newCredentialsProvider(t) // for accessing registries + d = newKnativeDeployer(cfg.Namespace, cfg.Verbose) + pp = newTektonPipelinesProvider(cfg.Namespace, p, c, cfg.Verbose) + o = []fn.Option{ // standard (shared) options for all commands fn.WithVerbose(cfg.Verbose), fn.WithProgressListener(p), fn.WithTransport(t), @@ -73,12 +75,8 @@ func NewClient(cfg ClientConfig, options ...fn.Option) (*fn.Client, func()) { fn.WithDescriber(knative.NewDescriber(cfg.Namespace, cfg.Verbose)), fn.WithLister(knative.NewLister(cfg.Namespace, cfg.Verbose)), fn.WithRunner(docker.NewRunner(cfg.Verbose)), - fn.WithDeployer(knative.NewDeployer(cfg.Namespace, cfg.Verbose)), - fn.WithPipelinesProvider(tekton.NewPipelinesProvider( - tekton.WithNamespace(cfg.Namespace), - tekton.WithProgressListener(p), - tekton.WithCredentialsProvider(c), - tekton.WithVerbose(cfg.Verbose))), + fn.WithDeployer(d), + fn.WithPipelinesProvider(pp), fn.WithPusher(docker.NewPusher( docker.WithCredentialsProvider(c), docker.WithProgressListener(p), @@ -132,6 +130,35 @@ func newCredentialsProvider(t http.RoundTripper) docker.CredentialsProvider { return creds.NewCredentialsProvider(options...) } +func newTektonPipelinesProvider(namespace string, progress *progress.Bar, creds docker.CredentialsProvider, verbose bool) *tekton.PipelinesProvider { + options := []tekton.Opt{ + tekton.WithNamespace(namespace), + tekton.WithProgressListener(progress), + tekton.WithCredentialsProvider(creds), + tekton.WithVerbose(verbose), + } + + if openshift.IsOpenShift() { + options = append(options, tekton.WithPipelineDecorator(openshift.OpenshiftMetadataDecorator{})) + } + + return tekton.NewPipelinesProvider(options...) + +} + +func newKnativeDeployer(namespace string, verbose bool) fn.Deployer { + options := []knative.DeployerOpt{ + knative.WithDeployerNamespace(namespace), + knative.WithDeployerVerbose(verbose), + } + + if openshift.IsOpenShift() { + options = append(options, knative.WithDeployerDecorator(openshift.OpenshiftMetadataDecorator{})) + } + + return knative.NewDeployer(options...) +} + func GetDefaultRegistry() string { switch { case openshift.IsOpenShift(): diff --git a/knative/deployer.go b/knative/deployer.go index 78177268a..3fa4f9a71 100644 --- a/knative/deployer.go +++ b/knative/deployer.go @@ -28,18 +28,48 @@ import ( const LIVENESS_ENDPOINT = "/health/liveness" const READINESS_ENDPOINT = "/health/readiness" +type DeployDecorator interface { + UpdateAnnotations(fn.Function, map[string]string) map[string]string + UpdateLabels(fn.Function, map[string]string) map[string]string +} + +type DeployerOpt func(*Deployer) + type Deployer struct { // Namespace with which to override that set on the default configuration (such as the ~/.kube/config). // If left blank, deployment will commence to the configured namespace. Namespace string // verbose logging enablement flag. verbose bool + + decorator DeployDecorator } -func NewDeployer(namespaceOverride string, verbose bool) *Deployer { - return &Deployer{ - Namespace: namespaceOverride, - verbose: verbose, +func NewDeployer(opts ...DeployerOpt) *Deployer { + d := &Deployer{} + + for _, opt := range opts { + opt(d) + } + + return d +} + +func WithDeployerNamespace(namespace string) DeployerOpt { + return func(d *Deployer) { + d.Namespace = namespace + } +} + +func WithDeployerVerbose(verbose bool) DeployerOpt { + return func(d *Deployer) { + d.verbose = verbose + } +} + +func WithDeployerDecorator(decorator DeployDecorator) DeployerOpt { + return func(d *Deployer) { + d.decorator = decorator } } @@ -94,7 +124,7 @@ func (d *Deployer) Deploy(ctx context.Context, f fn.Function) (fn.DeploymentResu referencedSecrets := sets.NewString() referencedConfigMaps := sets.NewString() - service, err := generateNewService(f) + service, err := generateNewService(f, d.decorator) if err != nil { err = fmt.Errorf("knative deployer failed to generate the Knative Service: %v", err) return fn.DeploymentResult{}, err @@ -195,7 +225,7 @@ func (d *Deployer) Deploy(ctx context.Context, f fn.Function) (fn.DeploymentResu return fn.DeploymentResult{}, err } - _, err = client.UpdateServiceWithRetry(ctx, f.Name, updateService(f, newEnv, newEnvFrom, newVolumes, newVolumeMounts), 3) + _, err = client.UpdateServiceWithRetry(ctx, f.Name, updateService(f, newEnv, newEnvFrom, newVolumes, newVolumeMounts, d.decorator), 3) if err != nil { err = fmt.Errorf("knative deployer failed to update the Knative Service: %v", err) return fn.DeploymentResult{}, err @@ -240,7 +270,7 @@ func setHealthEndpoints(f fn.Function, c *corev1.Container) *corev1.Container { return c } -func generateNewService(f fn.Function) (*v1.Service, error) { +func generateNewService(f fn.Function, decorator DeployDecorator) (*v1.Service, error) { container := corev1.Container{ Image: f.ImageWithDigest(), } @@ -262,16 +292,21 @@ func generateNewService(f fn.Function) (*v1.Service, error) { } container.VolumeMounts = newVolumeMounts - labels, err := processLabels(f) + labels, err := processLabels(f, decorator) if err != nil { return nil, err } + annotations := f.Annotations + if decorator != nil { + annotations = decorator.UpdateAnnotations(f, annotations) + } + service := &v1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: f.Name, Labels: labels, - Annotations: f.Annotations, + Annotations: annotations, }, Spec: v1.ServiceSpec{ ConfigurationSpec: v1.ConfigurationSpec{ @@ -297,7 +332,7 @@ func generateNewService(f fn.Function) (*v1.Service, error) { return service, nil } -func updateService(f fn.Function, newEnv []corev1.EnvVar, newEnvFrom []corev1.EnvFromSource, newVolumes []corev1.Volume, newVolumeMounts []corev1.VolumeMount) func(service *v1.Service) (*v1.Service, error) { +func updateService(f fn.Function, newEnv []corev1.EnvVar, newEnvFrom []corev1.EnvFromSource, newVolumes []corev1.Volume, newVolumeMounts []corev1.VolumeMount, decorator DeployDecorator) func(service *v1.Service) (*v1.Service, error) { return func(service *v1.Service) (*v1.Service, error) { // Removing the name so the k8s server can fill it in with generated name, // this prevents conflicts in Revision name when updating the KService from multiple places. @@ -305,6 +340,10 @@ func updateService(f fn.Function, newEnv []corev1.EnvVar, newEnvFrom []corev1.En // Don't bother being as clever as we are with env variables // Just set the annotations and labels to be whatever we find in func.yaml + if decorator != nil { + service.ObjectMeta.Annotations = decorator.UpdateAnnotations(f, service.ObjectMeta.Annotations) + } + for k, v := range f.Annotations { service.ObjectMeta.Annotations[k] = v } @@ -328,7 +367,7 @@ func updateService(f fn.Function, newEnv []corev1.EnvVar, newEnvFrom []corev1.En return service, err } - labels, err := processLabels(f) + labels, err := processLabels(f, decorator) if err != nil { return service, err } @@ -354,7 +393,7 @@ func updateService(f fn.Function, newEnv []corev1.EnvVar, newEnvFrom []corev1.En // value: value1 // - key: EXAMPLE2 # Label from the local ENV var // value: {{ env:MY_ENV }} -func processLabels(f fn.Function) (map[string]string, error) { +func processLabels(f fn.Function, decorator DeployDecorator) (map[string]string, error) { labels := map[string]string{ labels.FunctionKey: labels.FunctionValue, labels.FunctionNameKey: f.Name, @@ -365,6 +404,11 @@ func processLabels(f fn.Function) (map[string]string, error) { 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, "{{") { diff --git a/openshift/metadata.go b/openshift/metadata.go new file mode 100644 index 000000000..a8aee74bf --- /dev/null +++ b/openshift/metadata.go @@ -0,0 +1,38 @@ +package openshift + +import ( + fn "knative.dev/kn-plugin-func" +) + +const ( + AnnotationOpenShiftVcsUri = "app.openshift.io/vcs-uri" + AnnotationOpenShiftVcsRef = "app.openshift.io/vcs-ref" + + LabelAppK8sInstance = "app.kubernetes.io/instance" +) + +type OpenshiftMetadataDecorator struct{} + +func (o OpenshiftMetadataDecorator) UpdateAnnotations(f fn.Function, annotations map[string]string) map[string]string { + if annotations == nil { + annotations = map[string]string{} + } + if f.Git.URL != nil { + annotations[AnnotationOpenShiftVcsUri] = *f.Git.URL + } + if f.Git.Revision != nil { + annotations[AnnotationOpenShiftVcsRef] = *f.Git.Revision + } + + return annotations +} + +func (o OpenshiftMetadataDecorator) UpdateLabels(f fn.Function, labels map[string]string) map[string]string { + if labels == nil { + labels = map[string]string{} + } + + labels[LabelAppK8sInstance] = f.Name + + return labels +} diff --git a/pipelines/tekton/pipeplines_provider.go b/pipelines/tekton/pipeplines_provider.go index a134efdda..3f4542591 100644 --- a/pipelines/tekton/pipeplines_provider.go +++ b/pipelines/tekton/pipeplines_provider.go @@ -24,6 +24,10 @@ import ( "knative.dev/pkg/apis" ) +type PipelineDecorator interface { + UpdateLabels(fn.Function, map[string]string) map[string]string +} + type Opt func(*PipelinesProvider) type PipelinesProvider struct { @@ -33,6 +37,7 @@ type PipelinesProvider struct { verbose bool progressListener fn.ProgressListener credentialsProvider docker.CredentialsProvider + decorator PipelineDecorator } func WithNamespace(namespace string) Opt { @@ -59,6 +64,12 @@ func WithVerbose(verbose bool) Opt { } } +func WithPipelineDecorator(decorator PipelineDecorator) Opt { + return func(pp *PipelinesProvider) { + pp.decorator = decorator + } +} + func NewPipelinesProvider(opts ...Opt) *PipelinesProvider { pp := &PipelinesProvider{} @@ -87,6 +98,9 @@ func (pp *PipelinesProvider) Run(ctx context.Context, f fn.Function) error { // 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} + 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)) if err != nil {