feat: add OpenShift related Annotations & Labels (#1106)

* feat: add OpenShift related Annotations & Labels

Signed-off-by: Zbynek Roubalik <zroubalik@gmail.com>

* fix test

Signed-off-by: Zbynek Roubalik <zroubalik@gmail.com>
This commit is contained in:
Zbynek Roubalik 2022-07-13 20:19:14 +02:00 committed by GitHub
parent f4537dd3d5
commit b4b4cc34c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 146 additions and 23 deletions

View File

@ -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)

View File

@ -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():

View File

@ -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, "{{") {

38
openshift/metadata.go Normal file
View File

@ -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
}

View File

@ -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 {