Skip to content

Commit

Permalink
Show tags / versions when doing odo catalog list
Browse files Browse the repository at this point in the history
When using `odo catalog list` the tags will now be listed:

```
github.com/redhat-developer/odo  add-versioning ✗                                                                                                                                                                                                                                                                                                                    7d ⚑  ⍉
▶ ./odo catalog list
NAME        TAGS
dotnet      2.0,latest
httpd       2.4,latest
nginx       1.10,1.12,1.8,latest
nodejs      0.10,4,6,8,latest
perl        5.16,5.20,5.24,latest
php         5.5,5.6,7.0,7.1,latest
python      2.7,3.3,3.4,3.5,3.6,latest
ruby        2.0,2.2,2.3,2.4,latest
wildfly     10.0,10.1,8.1,9.0,latest

```
  • Loading branch information
cdrage committed Jul 25, 2018
1 parent f81cddb commit 5dc99d1
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 20 deletions.
12 changes: 10 additions & 2 deletions cmd/catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ package cmd

import (
"fmt"
"os"
"strings"
"text/tabwriter"

"github.com/redhat-developer/odo/pkg/catalog"
"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -34,10 +38,14 @@ var catalogListCmd = &cobra.Command{
case 0:
fmt.Printf("No deployable components found\n")
default:
fmt.Println("The following components can be deployed:")

w := tabwriter.NewWriter(os.Stdout, 5, 2, 3, ' ', tabwriter.TabIndent)
fmt.Fprintln(w, "NAME", "\t", "TAGS")
for _, component := range catalogList {
fmt.Printf("- %v\n", component)
fmt.Fprintln(w, component.Name, "\t", strings.Join(component.Tags, ","))
}
w.Flush()

}
},
}
Expand Down
53 changes: 35 additions & 18 deletions pkg/catalog/catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,22 @@ import (
log "github.com/sirupsen/logrus"
)

type CatalogImage struct {
Name string
Tags []string
}

// List lists all the available component types
func List(client *occlient.Client) ([]string, error) {
var catalogList []string
imageStreams, err := getDefaultBuilderImages(client)
func List(client *occlient.Client) ([]CatalogImage, error) {

catalogList, err := getDefaultBuilderImages(client)
if err != nil {
return nil, errors.Wrap(err, "unable to get image streams")
}
catalogList = append(catalogList, imageStreams...)

// TODO: uncomment when component create supports template creation
//clusterServiceClasses, err := client.GetClusterServiceClassExternalNames()
//if err != nil {
// return nil, errors.Wrap(err, "unable to get cluster service classes")
//}
//catalogList = append(catalogList, clusterServiceClasses...)
if len(catalogList) == 0 {
return nil, errors.New("unable to retrieve any catalog images from the OpenShift cluster")
}

return catalogList, nil
}
Expand All @@ -37,8 +38,8 @@ func Search(client *occlient.Client, name string) ([]string, error) {

// do a partial search in all the components
for _, component := range componentList {
if strings.Contains(component, name) {
result = append(result, component)
if strings.Contains(component.Name, name) {
result = append(result, component.Name)
}
}

Expand All @@ -53,7 +54,7 @@ func Exists(client *occlient.Client, componentType string) (bool, error) {
}

for _, supported := range catalogList {
if componentType == supported {
if componentType == supported.Name {
return true, nil
}
}
Expand All @@ -62,27 +63,43 @@ func Exists(client *occlient.Client, componentType string) (bool, error) {

// getDefaultBuilderImages returns the default builder images available in the
// openshift namespace
func getDefaultBuilderImages(client *occlient.Client) ([]string, error) {
func getDefaultBuilderImages(client *occlient.Client) ([]CatalogImage, error) {
imageStreams, err := client.GetImageStreams(occlient.OpenShiftNameSpace)
if err != nil {
return nil, errors.Wrap(err, "unable to get Image Streams")
}

var builderImages []string
var builderImages []CatalogImage

// Get builder images from the available imagestreams
outer:
for _, imageStream := range imageStreams {
var allTags []string
buildImage := false

for _, tag := range imageStream.Spec.Tags {

allTags = append(allTags, tag.Name)

// Check to see if it is a "builder" image
if _, ok := tag.Annotations["tags"]; ok {
for _, t := range strings.Split(tag.Annotations["tags"], ",") {

// If the tag has "builder" then we will add the image to the list
if t == "builder" {
builderImages = append(builderImages, imageStream.Name)
continue outer
buildImage = true
}
}
}

}

// Append to the list of images if a "builder" tag was found
if buildImage {
builderImages = append(builderImages, CatalogImage{Name: imageStream.Name, Tags: allTags})
}

}

log.Debugf("Found builder images: %v", builderImages)
return builderImages, nil
}
121 changes: 121 additions & 0 deletions pkg/catalog/catalog_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package catalog

import (
"reflect"
"testing"

imagev1 "github.com/openshift/api/image/v1"
"github.com/redhat-developer/odo/pkg/occlient"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
ktesting "k8s.io/client-go/testing"
)

// Function taken from occlient_test.go
// fakeImageStream gets imagestream for the reactor
func fakeImageStream(imageName string, namespace string, tags []string) *imagev1.ImageStream {
image := &imagev1.ImageStream{
ObjectMeta: metav1.ObjectMeta{
Name: imageName,
Namespace: namespace,
},
Status: imagev1.ImageStreamStatus{
Tags: []imagev1.NamedTagEventList{
{
Tag: "latest",
Items: []imagev1.TagEvent{
{DockerImageReference: "example/" + imageName + ":latest"},
{Generation: 1},
{Image: imageName + "@sha256:9579a93ee"},
},
},
},
},
}

for _, tag := range tags {
imageTag := imagev1.TagReference{Name: tag, Annotations: map[string]string{"tags": "builder"}}
image.Spec.Tags = append(image.Spec.Tags, imageTag)
}

return image
}

// Function taken from occlient_test.go
// fakeImageStreams lists the imagestreams for the reactor
func fakeImageStreams(imageName string, namespace string, tags []string) *imagev1.ImageStreamList {
return &imagev1.ImageStreamList{
Items: []imagev1.ImageStream{*fakeImageStream(imageName, namespace, tags)},
}
}

func TestList(t *testing.T) {
type args struct {
name string
namespace string
tags []string
}
tests := []struct {
name string
args args
wantErr bool
wantTags []string
}{
{
name: "Case 1: Valid image output with one tag",
args: args{
name: "foobar",
namespace: "openshift",
tags: []string{"latest"},
},
wantErr: false,
wantTags: []string{"latest"},
},
{
name: "Case 2: Valid image output with multiple tags",
args: args{
name: "foobar",
namespace: "openshift",
tags: []string{"1.0.0", "1.0.1", "0.0.1", "latest"},
},
wantErr: false,
wantTags: []string{"1.0.0", "1.0.1", "0.0.1", "latest"},
},
{
name: "Case 3: Invalid image output with no tags",
args: args{
name: "foobar",
namespace: "foo",
tags: []string{},
},
wantErr: true,
wantTags: []string{},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

// Fake the client with the appropriate arguments
client, fakeClientSet := occlient.FakeNew()
fakeClientSet.ImageClientset.PrependReactor("list", "imagestreams", func(action ktesting.Action) (bool, runtime.Object, error) {
return true, fakeImageStreams(tt.args.name, tt.args.namespace, tt.args.tags), nil
})

// The function we are testing
output, err := List(client)

//Checks for error in positive cases
if !tt.wantErr == (err != nil) {
t.Errorf("component List() unexpected error %v, wantErr %v", err, tt.wantErr)
}

// Check if the output is the same as what's expected (tags)
// and only if output is more than 0 (something is actually returned)
if len(output) > 0 && !(reflect.DeepEqual(output[0].Tags, tt.wantTags)) {
t.Errorf("expected tags: %s, got: %s", tt.wantTags, output[0].Tags)
}

})
}
}

0 comments on commit 5dc99d1

Please sign in to comment.