|  | 
|  | 1 | +package k8s | 
|  | 2 | + | 
|  | 3 | +import ( | 
|  | 4 | +	"context" | 
|  | 5 | +	"fmt" | 
|  | 6 | + | 
|  | 7 | +	"k8s.io/apimachinery/pkg/api/errors" | 
|  | 8 | +	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 
|  | 9 | +	eventingv1 "knative.dev/eventing/pkg/apis/eventing/v1" | 
|  | 10 | +	"knative.dev/func/pkg/k8s" | 
|  | 11 | +	"knative.dev/func/pkg/knative" | 
|  | 12 | + | 
|  | 13 | +	fn "knative.dev/func/pkg/functions" | 
|  | 14 | +) | 
|  | 15 | + | 
|  | 16 | +type Describer struct { | 
|  | 17 | +	verbose bool | 
|  | 18 | +} | 
|  | 19 | + | 
|  | 20 | +func NewDescriber(verbose bool) *Describer { | 
|  | 21 | +	return &Describer{ | 
|  | 22 | +		verbose: verbose, | 
|  | 23 | +	} | 
|  | 24 | +} | 
|  | 25 | + | 
|  | 26 | +// Describe a function by name. Note that the consuming API uses domain style | 
|  | 27 | +// notation, whereas Kubernetes restricts to label-syntax, which is thus | 
|  | 28 | +// escaped. Therefor as a knative (kube) implementation detail proper full | 
|  | 29 | +// names have to be escaped on the way in and unescaped on the way out. ex: | 
|  | 30 | +// www.example-site.com -> www-example--site-com | 
|  | 31 | +func (d *Describer) Describe(ctx context.Context, name, namespace string) (fn.Instance, error) { | 
|  | 32 | +	if namespace == "" { | 
|  | 33 | +		return fn.Instance{}, fmt.Errorf("function namespace is required when describing %q", name) | 
|  | 34 | +	} | 
|  | 35 | + | 
|  | 36 | +	clientset, err := k8s.NewKubernetesClientset() | 
|  | 37 | +	if err != nil { | 
|  | 38 | +		return fn.Instance{}, fmt.Errorf("unable to create k8s client: %v", err) | 
|  | 39 | +	} | 
|  | 40 | + | 
|  | 41 | +	deploymentClient := clientset.AppsV1().Deployments(namespace) | 
|  | 42 | +	eventingClient, err := knative.NewEventingClient(namespace) | 
|  | 43 | +	if err != nil { | 
|  | 44 | +		return fn.Instance{}, fmt.Errorf("unable to create eventing client: %v", err) | 
|  | 45 | +	} | 
|  | 46 | + | 
|  | 47 | +	deployment, err := deploymentClient.Get(ctx, name, metav1.GetOptions{}) | 
|  | 48 | +	if err != nil { | 
|  | 49 | +		return fn.Instance{}, fmt.Errorf("unable to get deployment %q: %v", name, err) | 
|  | 50 | +	} | 
|  | 51 | + | 
|  | 52 | +	primaryRouteURL := fmt.Sprintf("%s.%s.svc", name, namespace) // TODO: full URL with scheme? | 
|  | 53 | + | 
|  | 54 | +	description := fn.Instance{ | 
|  | 55 | +		Name:      name, | 
|  | 56 | +		Namespace: namespace, | 
|  | 57 | +		Route:     primaryRouteURL, | 
|  | 58 | +		Routes:    []string{primaryRouteURL}, | 
|  | 59 | +	} | 
|  | 60 | + | 
|  | 61 | +	triggers, err := eventingClient.ListTriggers(ctx) | 
|  | 62 | +	// IsNotFound -- Eventing is probably not installed on the cluster | 
|  | 63 | +	if err != nil && !errors.IsNotFound(err) { | 
|  | 64 | +		return description, nil | 
|  | 65 | +	} else if err != nil { | 
|  | 66 | +		return fn.Instance{}, fmt.Errorf("unable to list triggers: %v", err) | 
|  | 67 | +	} | 
|  | 68 | + | 
|  | 69 | +	triggerMatches := func(t *eventingv1.Trigger) bool { | 
|  | 70 | +		return t.Spec.Subscriber.Ref != nil && | 
|  | 71 | +			t.Spec.Subscriber.Ref.Name == name && | 
|  | 72 | +			t.Spec.Subscriber.Ref.APIVersion == "v1" && | 
|  | 73 | +			t.Spec.Subscriber.Ref.Kind == "Service" | 
|  | 74 | +	} | 
|  | 75 | + | 
|  | 76 | +	subscriptions := make([]fn.Subscription, 0, len(triggers.Items)) | 
|  | 77 | +	for _, trigger := range triggers.Items { | 
|  | 78 | +		if triggerMatches(&trigger) { | 
|  | 79 | +			filterAttrs := trigger.Spec.Filter.Attributes | 
|  | 80 | +			subscription := fn.Subscription{ | 
|  | 81 | +				Source: filterAttrs["source"], | 
|  | 82 | +				Type:   filterAttrs["type"], | 
|  | 83 | +				Broker: trigger.Spec.Broker, | 
|  | 84 | +			} | 
|  | 85 | +			subscriptions = append(subscriptions, subscription) | 
|  | 86 | +		} | 
|  | 87 | +	} | 
|  | 88 | + | 
|  | 89 | +	description.Subscriptions = subscriptions | 
|  | 90 | + | 
|  | 91 | +	// Populate labels from the deployment | 
|  | 92 | +	if deployment.Labels != nil { | 
|  | 93 | +		description.Labels = deployment.Labels | 
|  | 94 | +	} | 
|  | 95 | + | 
|  | 96 | +	return description, nil | 
|  | 97 | +} | 
0 commit comments