Skip to content

Commit d37451e

Browse files
committed
Add implementations for Lister, Describer and Remover for raw deployments
1 parent 326d6a6 commit d37451e

File tree

14 files changed

+264
-30
lines changed

14 files changed

+264
-30
lines changed

cmd/client.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515
fn "knative.dev/func/pkg/functions"
1616
fnhttp "knative.dev/func/pkg/http"
1717
"knative.dev/func/pkg/k8s"
18-
"knative.dev/func/pkg/knative"
1918
"knative.dev/func/pkg/oci"
2019
"knative.dev/func/pkg/pipelines/tekton"
2120
)
@@ -67,9 +66,9 @@ func NewClient(cfg ClientConfig, options ...fn.Option) (*fn.Client, func()) {
6766
fn.WithTransport(t),
6867
fn.WithRepositoriesPath(config.RepositoriesPath()),
6968
fn.WithBuilder(buildpacks.NewBuilder(buildpacks.WithVerbose(cfg.Verbose))),
70-
fn.WithRemover(knative.NewRemover(cfg.Verbose)),
71-
fn.WithDescriber(knative.NewDescriber(cfg.Verbose)),
72-
fn.WithLister(knative.NewLister(cfg.Verbose)),
69+
fn.WithRemover(knativedeployer.NewRemover(cfg.Verbose)),
70+
fn.WithDescriber(knativedeployer.NewDescriber(cfg.Verbose)),
71+
fn.WithLister(knativedeployer.NewLister(cfg.Verbose)),
7372
fn.WithDeployer(d),
7473
fn.WithPipelinesProvider(pp),
7574
fn.WithPusher(docker.NewPusher(

cmd/completion_util.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ import (
1010

1111
"github.com/spf13/cobra"
1212

13+
knativedeployer "knative.dev/func/pkg/deployer/knative"
1314
fn "knative.dev/func/pkg/functions"
14-
"knative.dev/func/pkg/knative"
1515
)
1616

1717
func CompleteFunctionList(cmd *cobra.Command, args []string, toComplete string) (strings []string, directive cobra.ShellCompDirective) {
18-
lister := knative.NewLister(false)
18+
lister := knativedeployer.NewLister(false)
1919

2020
list, err := lister.List(cmd.Context(), "")
2121
if err != nil {

cmd/deploy.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import (
1616
"k8s.io/apimachinery/pkg/api/resource"
1717
"knative.dev/client/pkg/util"
1818
"knative.dev/func/pkg/deployer"
19+
k8sdeployer "knative.dev/func/pkg/deployer/k8s"
20+
knativedeployer "knative.dev/func/pkg/deployer/knative"
1921

2022
"knative.dev/func/pkg/builders"
2123
"knative.dev/func/pkg/config"
@@ -813,17 +815,23 @@ func (c deployConfig) clientOptions() ([]fn.Option, error) {
813815
deployType = deployer.KnativeDeployerName // default to knative for backwards compatibility
814816
}
815817

816-
var d fn.Deployer
817818
switch deployType {
818819
case deployer.KnativeDeployerName:
819-
d = newKnativeDeployer(c.Verbose)
820+
o = append(o,
821+
fn.WithDeployer(newKnativeDeployer(c.Verbose)),
822+
fn.WithRemover(knativedeployer.NewRemover(c.Verbose)),
823+
fn.WithDescriber(knativedeployer.NewDescriber(c.Verbose)),
824+
fn.WithLister(knativedeployer.NewLister(c.Verbose)))
820825
case deployer.KubernetesDeployerName:
821-
d = newK8sDeployer(c.Verbose)
826+
o = append(o,
827+
fn.WithDeployer(newK8sDeployer(c.Verbose)),
828+
fn.WithRemover(k8sdeployer.NewRemover(c.Verbose)),
829+
fn.WithDescriber(k8sdeployer.NewDescriber(c.Verbose)),
830+
fn.WithLister(k8sdeployer.NewLister(c.Verbose)))
822831
default:
823832
return o, fmt.Errorf("unsupported deploy type: %s (supported: %s, %s)", deployType, deployer.KnativeDeployerName, deployer.KubernetesDeployerName)
824833
}
825834

826-
o = append(o, fn.WithDeployer(d))
827835
return o, nil
828836
}
829837

pkg/deployer/integration_test_helper.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import (
2424
)
2525

2626
// Basic happy path test of deploy->describe->list->re-deploy->delete.
27-
func IntegrationTest(t *testing.T, deployer fn.Deployer) {
27+
func IntegrationTest(t *testing.T, deployer fn.Deployer, remover fn.Remover, lister fn.Lister, describer fn.Describer) {
2828
var err error
2929
functionName := "fn-testing"
3030

@@ -194,7 +194,6 @@ func IntegrationTest(t *testing.T, deployer fn.Deployer) {
194194
t.Error("config-map was not mounted")
195195
}
196196

197-
describer := knative.NewDescriber(false)
198197
instance, err := describer.Describe(ctx, functionName, namespace)
199198
if err != nil {
200199
t.Fatal(err)
@@ -228,7 +227,6 @@ func IntegrationTest(t *testing.T, deployer fn.Deployer) {
228227
}
229228
}
230229

231-
lister := knative.NewLister(false)
232230
list, err := lister.List(ctx, namespace)
233231
if err != nil {
234232
t.Fatal(err)
@@ -271,7 +269,6 @@ func IntegrationTest(t *testing.T, deployer fn.Deployer) {
271269
t.Error("environment variable was not set from config-map")
272270
}
273271

274-
remover := knative.NewRemover(false)
275272
err = remover.Remove(ctx, functionName, namespace)
276273
if err != nil {
277274
t.Fatal(err)

pkg/deployer/k8s/describer.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
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+
}

pkg/deployer/k8s/integration_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,9 @@ import (
1111
)
1212

1313
func TestIntegration(t *testing.T) {
14-
deployer.IntegrationTest(t, k8s.NewDeployer(k8s.WithDeployerVerbose(false)))
14+
deployer.IntegrationTest(t,
15+
k8s.NewDeployer(k8s.WithDeployerVerbose(false)),
16+
k8s.NewRemover(false),
17+
k8s.NewLister(false),
18+
k8s.NewDescriber(false))
1519
}

pkg/deployer/k8s/lister.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package k8s
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
v1 "k8s.io/api/apps/v1"
8+
corev1 "k8s.io/api/core/v1"
9+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10+
fn "knative.dev/func/pkg/functions"
11+
"knative.dev/func/pkg/k8s"
12+
)
13+
14+
type Lister struct {
15+
verbose bool
16+
}
17+
18+
func NewLister(verbose bool) *Lister {
19+
return &Lister{verbose: verbose}
20+
}
21+
22+
// List functions, optionally specifying a namespace.
23+
func (l *Lister) List(ctx context.Context, namespace string) ([]fn.ListItem, error) {
24+
clientset, err := k8s.NewKubernetesClientset()
25+
if err != nil {
26+
return nil, fmt.Errorf("could not setup kubernetes clientset: %w", err)
27+
}
28+
29+
deploymentClient := clientset.AppsV1().Deployments(namespace)
30+
serviceClient := clientset.CoreV1().Services(namespace)
31+
deployments, err := deploymentClient.List(ctx, metav1.ListOptions{
32+
LabelSelector: "function.knative.dev/name",
33+
})
34+
if err != nil {
35+
return nil, fmt.Errorf("could not list deployments: %w", err)
36+
}
37+
38+
items := []fn.ListItem{}
39+
for _, deployment := range deployments.Items {
40+
41+
// get status
42+
ready := corev1.ConditionUnknown
43+
for _, con := range deployment.Status.Conditions {
44+
if con.Type == v1.DeploymentAvailable {
45+
ready = con.Status
46+
break
47+
}
48+
}
49+
50+
service, err := serviceClient.Get(ctx, deployment.Name, metav1.GetOptions{})
51+
if err != nil {
52+
return nil, fmt.Errorf("could not get service: %w", err)
53+
}
54+
55+
runtimeLabel := ""
56+
listItem := fn.ListItem{
57+
Name: service.Name,
58+
Namespace: service.Namespace,
59+
Runtime: runtimeLabel,
60+
URL: fmt.Sprintf("%s.%s.svc", service.Name, service.Namespace), // TODO: do we want the full URL with scheme here?
61+
Ready: string(ready),
62+
}
63+
64+
items = append(items, listItem)
65+
}
66+
67+
return items, nil
68+
}

pkg/deployer/k8s/remover.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package k8s
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
8+
apiErrors "k8s.io/apimachinery/pkg/api/errors"
9+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10+
fn "knative.dev/func/pkg/functions"
11+
"knative.dev/func/pkg/k8s"
12+
)
13+
14+
func NewRemover(verbose bool) *Remover {
15+
return &Remover{
16+
verbose: verbose,
17+
}
18+
}
19+
20+
type Remover struct {
21+
verbose bool
22+
}
23+
24+
func (remover *Remover) Remove(ctx context.Context, name, ns string) error {
25+
if ns == "" {
26+
fmt.Fprintf(os.Stderr, "no namespace defined when trying to delete a function in knative remover\n")
27+
return fn.ErrNamespaceRequired
28+
}
29+
30+
clientset, err := k8s.NewKubernetesClientset()
31+
if err != nil {
32+
return fmt.Errorf("could not setup kubernetes clientset: %w", err)
33+
}
34+
35+
deploymentClient := clientset.AppsV1().Deployments(ns)
36+
serviceClient := clientset.CoreV1().Services(ns)
37+
38+
err = deploymentClient.Delete(ctx, name, metav1.DeleteOptions{})
39+
if err != nil {
40+
if apiErrors.IsNotFound(err) {
41+
return fn.ErrFunctionNotFound
42+
}
43+
return fmt.Errorf("k8s remover failed to delete the deployment: %v", err)
44+
}
45+
46+
err = serviceClient.Delete(ctx, name, metav1.DeleteOptions{})
47+
if err != nil {
48+
if apiErrors.IsNotFound(err) {
49+
return fn.ErrFunctionNotFound
50+
}
51+
return fmt.Errorf("k8s remover failed to delete the service: %v", err)
52+
}
53+
54+
return nil
55+
}

pkg/knative/describer.go renamed to pkg/deployer/knative/describer.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"k8s.io/apimachinery/pkg/api/errors"
88
clientservingv1 "knative.dev/client/pkg/serving/v1"
99
eventingv1 "knative.dev/eventing/pkg/apis/eventing/v1"
10+
"knative.dev/func/pkg/knative"
1011

1112
fn "knative.dev/func/pkg/functions"
1213
)
@@ -32,12 +33,12 @@ func (d *Describer) Describe(ctx context.Context, name, namespace string) (descr
3233
return
3334
}
3435

35-
servingClient, err := NewServingClient(namespace)
36+
servingClient, err := knative.NewServingClient(namespace)
3637
if err != nil {
3738
return
3839
}
3940

40-
eventingClient, err := NewEventingClient(namespace)
41+
eventingClient, err := knative.NewEventingClient(namespace)
4142
if err != nil {
4243
return
4344
}

pkg/deployer/knative/integration_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,9 @@ import (
1111
)
1212

1313
func TestIntegration(t *testing.T) {
14-
deployer.IntegrationTest(t, knative.NewDeployer(knative.WithDeployerVerbose(false)))
14+
deployer.IntegrationTest(t,
15+
knative.NewDeployer(knative.WithDeployerVerbose(false)),
16+
knative.NewRemover(false),
17+
knative.NewLister(false),
18+
knative.NewDescriber(false))
1519
}

0 commit comments

Comments
 (0)