Skip to content

Commit

Permalink
Adds ability to pick version when deploying
Browse files Browse the repository at this point in the history
This adds the ability to pick both the image as well as the image
version when deploying, for example, using `odo create nodejs:8 foobar`
  • Loading branch information
cdrage committed Jul 30, 2018
1 parent 673390c commit 7aefffd
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 32 deletions.
35 changes: 29 additions & 6 deletions cmd/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,21 +74,44 @@ A full list of component types that can be deployed is available using: 'odo com
os.Exit(1)
}

//We don't have to check it anymore, Args check made sure that args has at least one item
// We don't have to check it anymore, Args check made sure that args has at least one item
// and no more than two

// "Default" values
componentImageName := args[0]
componentType := args[0]
componentVersion := "latest"

// Check if componentType includes ":", if so, then we need to spit it into using versions
if strings.ContainsAny(componentImageName, ":") {
versionSplit := strings.Split(args[0], ":")
componentType = versionSplit[0]
componentVersion = versionSplit[1]
}

// Check to see if the catalog type actually exists
exists, err := catalog.Exists(client, componentType)
checkError(err, "")
if !exists {
fmt.Printf("Invalid component type: %v\nRun 'odo catalog list' to see a list of supported components\n", componentType)
os.Exit(1)
}

// Check to see if that particular version exists
versionExists, err := catalog.VersionExists(client, componentType, componentVersion)
checkError(err, "")
if !versionExists {
fmt.Printf("Invalid component version: %v\nRun 'odo catalog list' to see a list of supported component versions\n", componentVersion)
os.Exit(1)
}

// Retrieve the componentName
componentName := args[0]
if len(args) == 2 {
componentName = args[1]
}
//validate component name

// Validate component name
err = validateName(componentName)
checkError(err, "")
exists, err = component.Exists(client, componentName, applicationName, projectName)
Expand All @@ -99,7 +122,7 @@ A full list of component types that can be deployed is available using: 'odo com
}

if len(componentGit) != 0 {
err := component.CreateFromGit(client, componentName, componentType, componentGit, applicationName)
err := component.CreateFromGit(client, componentName, componentImageName, componentGit, applicationName)
checkError(err, "")
fmt.Printf("Component '%s' was created.\n", componentName)
fmt.Printf("Triggering build from %s.\n\n", componentGit)
Expand All @@ -115,7 +138,7 @@ A full list of component types that can be deployed is available using: 'odo com
fmt.Println("Please provide a path to the directory")
os.Exit(1)
}
err = component.CreateFromPath(client, componentName, componentType, dir, applicationName, "local")
err = component.CreateFromPath(client, componentName, componentImageName, dir, applicationName, "local")
checkError(err, "")
fmt.Printf("Please wait, creating %s component ...\n", componentName)
err = component.Build(client, componentName, applicationName, false, true, stdout)
Expand All @@ -126,7 +149,7 @@ A full list of component types that can be deployed is available using: 'odo com
path, err := filepath.Abs(componentBinary)
checkError(err, "")

err = component.CreateFromPath(client, componentName, componentType, path, applicationName, "binary")
err = component.CreateFromPath(client, componentName, componentImageName, path, applicationName, "binary")
checkError(err, "")
fmt.Printf("Please wait, creating %s component ...\n", componentName)
err = component.Build(client, componentName, applicationName, false, true, stdout)
Expand All @@ -137,7 +160,7 @@ A full list of component types that can be deployed is available using: 'odo com
// we want to use and save absolute path for component
dir, err := filepath.Abs("./")
checkError(err, "")
err = component.CreateFromPath(client, componentName, componentType, dir, applicationName, "local")
err = component.CreateFromPath(client, componentName, componentImageName, dir, applicationName, "local")
checkError(err, "")
fmt.Printf("Please wait, creating %s component ...\n", componentName)
err = component.Build(client, componentName, applicationName, false, true, stdout)
Expand Down
25 changes: 25 additions & 0 deletions pkg/catalog/catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,31 @@ func Exists(client *occlient.Client, componentType string) (bool, error) {
return false, nil
}

// Check if that version exists, returns true if the given version exists, false if not
func VersionExists(client *occlient.Client, componentType string, componentVersion string) (bool, error) {

// Retrieve the catalogList
catalogList, err := List(client)
if err != nil {
return false, errors.Wrapf(err, "unable to list catalog")
}

// Find the component and then return true if the version has been found
for _, supported := range catalogList {
if componentType == supported.Name {
// Now check to see if that version matches that components tag
for _, tag := range supported.Tags {
if componentVersion == tag {
return true, nil
}
}
}
}

// Else return false if nothing is found
return false, nil
}

// getDefaultBuilderImages returns the default builder images available in the
// openshift namespace
func getDefaultBuilderImages(client *occlient.Client) ([]CatalogImage, error) {
Expand Down
49 changes: 33 additions & 16 deletions pkg/component/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,19 @@ func validateSourceType(sourceType string) bool {
return false
}

func CreateFromGit(client *occlient.Client, name string, ctype string, url string, applicationName string) error {
func CreateFromGit(client *occlient.Client, name string, componentImageType string, url string, applicationName string) error {

labels := componentlabels.GetLabels(name, applicationName, true)

// Parse componentImageType before adding to labels
imageName, imageTag, _, err := occlient.ParseImageName(componentImageType)
if err != nil {
return errors.Wrap(err, "unable to parse image name")
}

// save component type as label
labels[componentlabels.ComponentTypeLabel] = ctype
labels[componentlabels.ComponentTypeLabel] = imageName
labels[componentlabels.ComponentTypeVersion] = imageTag

// save source path as annotation
annotations := map[string]string{componentSourceURLAnnotation: url}
Expand All @@ -61,7 +70,7 @@ func CreateFromGit(client *occlient.Client, name string, ctype string, url strin
return errors.Wrapf(err, "unable to create namespaced name")
}

err = client.NewAppS2I(namespacedOpenShiftObject, ctype, url, labels, annotations)
err = client.NewAppS2I(namespacedOpenShiftObject, componentImageType, url, labels, annotations)
if err != nil {
return errors.Wrapf(err, "unable to create git component %s", namespacedOpenShiftObject)
}
Expand All @@ -70,10 +79,18 @@ func CreateFromGit(client *occlient.Client, name string, ctype string, url strin

// CreateFromPath create new component with source or binary from the given local path
// sourceType indicates the source type of the component and can be either local or binary
func CreateFromPath(client *occlient.Client, name string, ctype string, path string, applicationName string, sourceType string) error {
func CreateFromPath(client *occlient.Client, name string, componentImageType string, path string, applicationName string, sourceType string) error {
labels := componentlabels.GetLabels(name, applicationName, true)

// Parse componentImageType before adding to labels
imageName, imageTag, _, err := occlient.ParseImageName(componentImageType)
if err != nil {
return errors.Wrap(err, "unable to parse image name")
}

// save component type as label
labels[componentlabels.ComponentTypeLabel] = ctype
labels[componentlabels.ComponentTypeLabel] = imageName
labels[componentlabels.ComponentTypeVersion] = imageTag

// save source path as annotation
sourceURL := url.URL{Scheme: "file", Path: path}
Expand All @@ -86,7 +103,7 @@ func CreateFromPath(client *occlient.Client, name string, ctype string, path str
return errors.Wrapf(err, "unable to create namespaced name")
}

err = client.BootstrapSupervisoredS2I(namespacedOpenShiftObject, ctype, labels, annotations)
err = client.BootstrapSupervisoredS2I(namespacedOpenShiftObject, componentImageType, labels, annotations)
if err != nil {
return err
}
Expand Down Expand Up @@ -249,25 +266,25 @@ func GetComponentType(client *occlient.Client, componentName string, application

// filter according to component and application name
selector := fmt.Sprintf("%s=%s,%s=%s", componentlabels.ComponentLabel, componentName, applabels.ApplicationLabel, applicationName)
ctypes, err := client.GetLabelValues(projectName, componentlabels.ComponentTypeLabel, selector)
componentImageTypes, err := client.GetLabelValues(projectName, componentlabels.ComponentTypeLabel, selector)
if err != nil {
return "", errors.Wrap(err, "unable to get type of %s component")
}
if len(ctypes) < 1 {
if len(componentImageTypes) < 1 {
// no type returned
return "", errors.Wrap(err, "unable to find type of %s component")

}
// check if all types are the same
// it should be as we are secting only exactly one component, and it doesn't make sense
// to have one component labeled with different component type labels
for _, ctype := range ctypes {
if ctypes[0] != ctype {
for _, componentImageType := range componentImageTypes {
if componentImageTypes[0] != componentImageType {
return "", errors.Wrap(err, "data mismatch: %s component has objects with different types")
}

}
return ctypes[0], nil
return componentImageTypes[0], nil
}

// List lists components in active application
Expand All @@ -282,11 +299,11 @@ func List(client *occlient.Client, applicationName string, projectName string) (
components := []ComponentInfo{}

for _, name := range componentNames {
ctype, err := GetComponentType(client, name, applicationName, projectName)
componentImageType, err := GetComponentType(client, name, applicationName, projectName)
if err != nil {
return nil, errors.Wrapf(err, "unable to list components")
}
components = append(components, ComponentInfo{Name: name, Type: ctype})
components = append(components, ComponentInfo{Name: name, Type: componentImageType})
}

return components, nil
Expand Down Expand Up @@ -524,9 +541,9 @@ func getPortFromService(service *corev1.Service) (int32, error) {
}

// Get Component Description
func GetComponentDesc(client *occlient.Client, currentComponent string, currentApplication string, currentProject string) (componentType string, path string, componentURL string, appStore []storage.StorageInfo, err error) {
func GetComponentDesc(client *occlient.Client, currentComponent string, currentApplication string, currentProject string) (componentImageType string, path string, componentURL string, appStore []storage.StorageInfo, err error) {
// Component Type
componentType, err = GetComponentType(client, currentComponent, currentApplication, currentProject)
componentImageType, err = GetComponentType(client, currentComponent, currentApplication, currentProject)
if err != nil {
return "", "", "", nil, errors.Wrap(err, "unable to get source path")
}
Expand All @@ -549,7 +566,7 @@ func GetComponentDesc(client *occlient.Client, currentComponent string, currentA
return "", "", "", nil, errors.Wrap(err, "unable to get storage list")
}

return componentType, path, componentURL, appStore, nil
return componentImageType, path, componentURL, appStore, nil
}

// Get Component logs
Expand Down
3 changes: 3 additions & 0 deletions pkg/component/labels/labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ const ComponentLabel = "app.kubernetes.io/component-name"
// ComponentTypeLabel is kubernetes that identifies type of a component
const ComponentTypeLabel = "app.kubernetes.io/component-type"

// ComponentTypeLabel is kubernetes that identifies type of a component
const ComponentTypeVersion = "app.kubernetes.io/component-version"

// GetLabels return labels that should be applied to every object for given component in active application
// additional labels are used only for creating object
// if you are creating something use additional=true
Expand Down
9 changes: 5 additions & 4 deletions pkg/occlient/occlient.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ func New(connectionCheck bool) (*Client, error) {
// returns (imageName, tag, digest, error)
// if image is referenced by tag (name:tag) than digest is ""
// if image is referenced by digest (name@digest) than tag is ""
func parseImageName(image string) (string, string, string, error) {
func ParseImageName(image string) (string, string, string, error) {
digestParts := strings.Split(image, "@")
if len(digestParts) == 2 {
// image is references digest
Expand Down Expand Up @@ -418,9 +418,9 @@ func getAppRootVolumeName(dcName string) string {
// gitUrl is the url of the git repo
func (c *Client) NewAppS2I(name string, builderImage string, gitUrl string, labels map[string]string, annotations map[string]string) error {

imageName, imageTag, _, err := parseImageName(builderImage)
imageName, imageTag, _, err := ParseImageName(builderImage)
if err != nil {
return errors.Wrap(err, "unable to create new s2i git build ")
return errors.Wrap(err, "unable to parse image name")
}

containerPorts, err := c.GetExposedPorts(imageName, imageTag)
Expand Down Expand Up @@ -563,7 +563,8 @@ func (c *Client) NewAppS2I(name string, builderImage string, gitUrl string, labe
// Supervisor keeps pod running (runs as pid1), so you it is possible to trigger assembly script inside running pod,
// and than restart application using Supervisor without need to restart whole container.
func (c *Client) BootstrapSupervisoredS2I(name string, builderImage string, labels map[string]string, annotations map[string]string) error {
imageName, imageTag, _, err := parseImageName(builderImage)
imageName, imageTag, _, err := ParseImageName(builderImage)

if err != nil {
return errors.Wrap(err, "unable to create new s2i git build ")
}
Expand Down
12 changes: 6 additions & 6 deletions pkg/occlient/occlient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ func TestAddLabelsToArgs(t *testing.T) {
}
}

func Test_parseImageName(t *testing.T) {
func Test_ParseImageName(t *testing.T) {

tests := []struct {
arg string
Expand Down Expand Up @@ -511,19 +511,19 @@ func Test_parseImageName(t *testing.T) {
for _, tt := range tests {
name := fmt.Sprintf("image name: %s", tt.arg)
t.Run(name, func(t *testing.T) {
got1, got2, got3, err := parseImageName(tt.arg)
got1, got2, got3, err := ParseImageName(tt.arg)
if (err != nil) != tt.wantErr {
t.Errorf("parseImageName() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("ParseImageName() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got1 != tt.want1 {
t.Errorf("parseImageName() got1 = %v, want %v", got1, tt.want1)
t.Errorf("ParseImageName() got1 = %v, want %v", got1, tt.want1)
}
if got2 != tt.want2 {
t.Errorf("parseImageName() got2 = %v, want %v", got2, tt.want2)
t.Errorf("ParseImageName() got2 = %v, want %v", got2, tt.want2)
}
if got3 != tt.want3 {
t.Errorf("parseImageName() got3 = %v, want %v", got3, tt.want3)
t.Errorf("ParseImageName() got3 = %v, want %v", got3, tt.want3)
}
})
}
Expand Down

0 comments on commit 7aefffd

Please sign in to comment.