diff --git a/pkg/kn/commands/service.go b/pkg/kn/commands/service.go index fb2a4fef1..c8ca48f82 100644 --- a/pkg/kn/commands/service.go +++ b/pkg/kn/commands/service.go @@ -25,5 +25,6 @@ func NewServiceCommand(p *KnParams) *cobra.Command { } serviceCmd.PersistentFlags().StringP("namespace", "n", "default", "Namespace to use.") serviceCmd.AddCommand(NewServiceListCommand(p)) + serviceCmd.AddCommand(NewServiceDescribeCommand(p)) return serviceCmd } diff --git a/pkg/kn/commands/service_describe.go b/pkg/kn/commands/service_describe.go new file mode 100644 index 000000000..b5c365230 --- /dev/null +++ b/pkg/kn/commands/service_describe.go @@ -0,0 +1,65 @@ +// Copyright © 2019 The Knative 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 commands + +import ( + "errors" + + "github.com/spf13/cobra" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/cli-runtime/pkg/genericclioptions" +) + +func NewServiceDescribeCommand(p *KnParams) *cobra.Command { + + serviceDescribePrintFlags := genericclioptions.NewPrintFlags("").WithDefaultOutput("yaml") + serviceDescribeCommand := &cobra.Command{ + Use: "describe NAME", + Short: "Describe available services.", + RunE: func(cmd *cobra.Command, args []string) error { + client, err := p.ServingFactory() + if err != nil { + return err + } + + if len(args) < 1 { + return errors.New("requires the service name.") + } + + namespace := cmd.Flag("namespace").Value.String() + describeService, err := client.Services(namespace).Get(args[0], v1.GetOptions{}) + if err != nil { + return err + } + + printer, err := serviceDescribePrintFlags.ToPrinter() + if err != nil { + return err + } + describeService.GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{ + Group: "knative.dev", + Version: "v1alpha1", + Kind: "Service"}) + err = printer.PrintObj(describeService, cmd.OutOrStdout()) + if err != nil { + return err + } + return nil + }, + } + serviceDescribePrintFlags.AddFlags(serviceDescribeCommand) + return serviceDescribeCommand +} diff --git a/pkg/kn/commands/service_describe_test.go b/pkg/kn/commands/service_describe_test.go new file mode 100644 index 000000000..0d5f8ff5c --- /dev/null +++ b/pkg/kn/commands/service_describe_test.go @@ -0,0 +1,95 @@ +// Copyright © 2019 The Knative 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 commands + +import ( + "bytes" + "testing" + "encoding/json" + + "sigs.k8s.io/yaml" + "k8s.io/apimachinery/pkg/api/equality" + "github.com/knative/serving/pkg/apis/serving/v1alpha1" + serving "github.com/knative/serving/pkg/client/clientset/versioned/typed/serving/v1alpha1" + "github.com/knative/serving/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + client_testing "k8s.io/client-go/testing" +) + +func fakeServiceDescribe(args []string, response *v1alpha1.Service) (action client_testing.Action, output string, err error) { + buf := new(bytes.Buffer) + fakeServing := &fake.FakeServingV1alpha1{&client_testing.Fake{}} + cmd := NewKnCommand(KnParams{ + Output: buf, + ServingFactory: func() (serving.ServingV1alpha1Interface, error) { return fakeServing, nil }, + }) + fakeServing.AddReactor("*", "*", + func(a client_testing.Action) (bool, runtime.Object, error) { + action = a + return true, response, nil + }) + cmd.SetArgs(args) + err = cmd.Execute() + if err != nil { + return + } + output = buf.String() + return +} + +func TestEmptyServiceDescribe(t *testing.T) { + _, _, err := fakeServiceDescribe([]string{"service", "describe"}, &v1alpha1.Service{}) + expectedError := "requires the service name." + if err == nil || err.Error() != expectedError { + t.Fatal("expect to fail with missing service name") + } +} + +func TestServiceDescribeDefaultOutput(t *testing.T) { + expectedService := v1alpha1.Service{ + TypeMeta: metav1.TypeMeta{ + Kind: "Service", + APIVersion: "knative.dev/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "default", + }, + } + action, output, err := fakeServiceDescribe([]string{"service", "describe", "test-foo"}, &expectedService) + if err != nil { + t.Fatal(err) + } + if action == nil { + t.Fatal("No action") + } else if !action.Matches("get", "services") { + t.Fatalf("Bad action %v", action) + } + + jsonData, err := yaml.YAMLToJSON([]byte(output)) + if err != nil { + t.Fatal(err) + } + var returnedService v1alpha1.Service + err = json.Unmarshal(jsonData, &returnedService) + if err != nil { + t.Fatal(err) + } + + if !equality.Semantic.DeepEqual(expectedService, returnedService) { + t.Fatal("mismatched objects") + } +}