Skip to content

Commit

Permalink
Image relocation
Browse files Browse the repository at this point in the history
  • Loading branch information
glyn committed Apr 9, 2019
1 parent 48dd2f9 commit ba9559d
Show file tree
Hide file tree
Showing 2 changed files with 163 additions and 0 deletions.
162 changes: 162 additions & 0 deletions cmd/duffle/relocate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/*
* Copyright (c) 2019-Present Pivotal Software, Inc. All Rights Reserved.
*
* 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 main

import (
"fmt"
"io"
"strconv"
"strings"

"github.com/deislabs/duffle/pkg/bundle"
"github.com/deislabs/duffle/pkg/duffle/home"

"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/util/validation"
)

const relocateDesc = `
Relocates the images referenced by a bundle and creates a new bundle with an updated image map.
The --repository-prefix flag determines the repositories for the relocated images.
Each image is tagged with a name starting with the given prefix and pushed to the repository.
For example, if the repository-prefix is example.com/user, the image istio/proxyv2 is relocated
to a name starting with example.com/user/ and pushed to a repository hosted by example.com.
`

type relocateCmd struct {
inputBundle string
home home.Home
outputBundle string
out io.Writer

repoPrefix string
inputBundleIsFile bool
}

func newRelocateCmd(w io.Writer) *cobra.Command {
relocate := &relocateCmd{out: w}

cmd := &cobra.Command{
Use: "relocate [INPUT-BUNDLE] [OUTPUT-BUNDLE]",
Short: "relocate images in a CNAB bundle",
Long: relocateDesc,
Example: `duffle relocate helloworld hellorelocated --repository-prefix example.com/user
duffle relocate path/to/bundle.json relocatedbundle --repository-prefix example.com/user --input-bundle-is-file`,
Args: cobra.ExactArgs(2),
PreRunE: func(cmd *cobra.Command, args []string) error {
// validate --repository-prefix if it is set, otherwise allow flag omission to be diagnosed as such
if cmd.Flags().Changed("repository-prefix") {
if err := flagValidRepository("repository-prefix")(cmd); err != nil {
return err
}
}

return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
relocate.inputBundle = args[0]
relocate.outputBundle = args[1]
relocate.home = home.Home(homePath())

return relocate.run()
},
}

f := cmd.Flags()
f.BoolVarP(&relocate.inputBundleIsFile, "input-bundle-is-file", "", false, "Indicates that the input bundle source is a file path")
f.StringVarP(&relocate.repoPrefix, "repository-prefix", "r", "", "Prefix for relocated image names")
cmd.MarkFlagRequired("repository-prefix")

return cmd
}

func (r *relocateCmd) run() error {
bun, err := r.setup()
if err != nil {
return err
}
if err := r.Relocate(bun); err != nil {
return err
}

return nil
}

func (r *relocateCmd) Relocate(bun *bundle.Bundle) error {
return nil
}

func (r *relocateCmd) setup() (*bundle.Bundle, error) {
bundleFile, err := resolveBundleFilePath(r.inputBundle, r.home.String(), r.inputBundleIsFile)
if err != nil {
return nil, err
}

bun, err := loadBundle(bundleFile)
if err != nil {
return nil, err
}

if err = bun.Validate(); err != nil {
return nil, err
}

return bun, nil
}

type flagsValidator func(cmd *cobra.Command) error

func flagValidRepository(flagName string) flagsValidator {
return func(cmd *cobra.Command) error {
repositoryValue := cmd.Flag(flagName).Value.String()

if strings.HasSuffix(repositoryValue, "/") || strings.Contains(repositoryValue, "//") {
return fmt.Errorf("invalid repository: %s", repositoryValue)
}

for i, part := range strings.Split(repositoryValue, "/") {
if i != 0 {
if strings.ContainsAny(part, ":@\" ") {
return fmt.Errorf("invalid repository: %s", repositoryValue)
}
continue
}

authorityParts := strings.Split(part, ":")
if len(authorityParts) > 2 {
return fmt.Errorf("invalid repository hostname: %s", part)
}
if errs := validation.IsDNS1123Subdomain(authorityParts[0]); len(errs) > 0 {
return fmt.Errorf("invalid repository hostname: %s", strings.Join(errs, "; "))
}
if len(authorityParts) == 2 {
portNumber, err := strconv.Atoi(authorityParts[1])
if err != nil {
return fmt.Errorf("invalid repository port number: %s", authorityParts[1])
}

if errs := validation.IsValidPortNum(portNumber); len(errs) > 0 {
return fmt.Errorf("invalid repository port number: %s", strings.Join(errs, "; "))
}
}
}

return nil
}
}
1 change: 1 addition & 0 deletions cmd/duffle/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func newRootCmd(outputRedirect io.Writer) *cobra.Command {
cmd.AddCommand(newInitCmd(outLog))
cmd.AddCommand(newShowCmd(outLog))
cmd.AddCommand(newListCmd(outLog))
cmd.AddCommand(newRelocateCmd(outLog))
cmd.AddCommand(newVersionCmd(outLog))
cmd.AddCommand(newInstallCmd(outLog))
cmd.AddCommand(newStatusCmd(outLog))
Expand Down

0 comments on commit ba9559d

Please sign in to comment.