|
| 1 | +/* |
| 2 | + * Copyright 2019 The original author or authors |
| 3 | + * |
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | + * you may not use this file except in compliance with the License. |
| 6 | + * You may obtain a copy of the License at |
| 7 | + * |
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | + * |
| 10 | + * Unless required by applicable law or agreed to in writing, software |
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | + * See the License for the specific language governing permissions and |
| 14 | + * limitations under the License. |
| 15 | + */ |
| 16 | + |
| 17 | +package main |
| 18 | + |
| 19 | +import ( |
| 20 | + "fmt" |
| 21 | + "io" |
| 22 | + "strconv" |
| 23 | + "strings" |
| 24 | + |
| 25 | + "github.com/spf13/cobra" |
| 26 | + "k8s.io/apimachinery/pkg/util/validation" |
| 27 | + |
| 28 | + "github.com/deislabs/duffle/pkg/loader" |
| 29 | +) |
| 30 | + |
| 31 | +const relocateDesc = ` |
| 32 | +Relocates the images referenced by a bundle and creates a new bundle with an updated image map. |
| 33 | +
|
| 34 | +The --repository-prefix flag determines the repositories for the relocated images. |
| 35 | +Each image tagged with a name starting with the given prefix and pushed to the repository. |
| 36 | +
|
| 37 | +For example, if the repository-prefix is example.com/user, the image istio/proxyv2 is relocated |
| 38 | +to a name starting with example.com/user/ and pushed to a repository hosted by example.com. |
| 39 | +` |
| 40 | + |
| 41 | +type relocateCmd struct { |
| 42 | + bundle string |
| 43 | + dest string |
| 44 | + repoPrefix string |
| 45 | + out io.Writer |
| 46 | + //verbose bool |
| 47 | + //insecure bool |
| 48 | +} |
| 49 | + |
| 50 | +func newRelocateCmd(w io.Writer) *cobra.Command { |
| 51 | + relocate := &relocateCmd{out: w} |
| 52 | + |
| 53 | + cmd := &cobra.Command{ |
| 54 | + Use: "relocate [INPUT-BUNDLE] [OUTPUT-BUNDLE]", |
| 55 | + Short: "relocate images in a CNAB bundle", |
| 56 | + Long: relocateDesc, |
| 57 | + Example: `duffle relocate helloworld hellorelocated --repository-prefix example.com/user`, |
| 58 | + Args: cobra.ExactArgs(2), |
| 59 | + PreRunE: func(cmd *cobra.Command, args []string) error { |
| 60 | + // validate --repository-prefix if it is set, otherwise allow flag omission to be diagnosed as such |
| 61 | + if cmd.Flags().Changed("repository-prefix") { |
| 62 | + if err := FlagValidRepository("repository-prefix")(cmd); err != nil { |
| 63 | + return err |
| 64 | + } |
| 65 | + } |
| 66 | + |
| 67 | + return nil |
| 68 | + }, |
| 69 | + RunE: func(cmd *cobra.Command, args []string) error { |
| 70 | + relocate.bundle = args[0] |
| 71 | + relocate.dest = args[1] |
| 72 | + |
| 73 | + return relocate.run() |
| 74 | + }, |
| 75 | + } |
| 76 | + |
| 77 | + f := cmd.Flags() |
| 78 | + f.StringVarP(&relocate.repoPrefix, "repository-prefix", "r", "", "a prefix for relocated image names") |
| 79 | + cmd.MarkFlagRequired("repository-prefix") |
| 80 | + //f.BoolVarP(&relocate.verbose, "verbose", "v", false, "Verbose output") |
| 81 | + //f.BoolVarP(&relocate.insecure, "insecure", "k", false, "Do not verify the bundle (INSECURE)") |
| 82 | + |
| 83 | + return cmd |
| 84 | +} |
| 85 | + |
| 86 | +func (ex *relocateCmd) run() error { |
| 87 | + bundlefile, l, err := ex.setup() |
| 88 | + if err != nil { |
| 89 | + return err |
| 90 | + } |
| 91 | + if err := ex.Relocate(bundlefile, l); err != nil { |
| 92 | + return err |
| 93 | + } |
| 94 | + |
| 95 | + return nil |
| 96 | +} |
| 97 | + |
| 98 | +func (ex *relocateCmd) Relocate(bundlefile string, l loader.Loader) error { |
| 99 | + return nil |
| 100 | +} |
| 101 | + |
| 102 | +func (ex *relocateCmd) setup() (string, loader.Loader, error) { |
| 103 | + //bundlefile, err := resolveBundleFilePath(ex.bundle, ex.home.String(), ex.bundleIsFile, ex.insecure) |
| 104 | + //if err != nil { |
| 105 | + // return "", nil, err |
| 106 | + //} |
| 107 | + // |
| 108 | + //l, err := getLoader(ex.home.String(), ex.insecure) |
| 109 | + //if err != nil { |
| 110 | + // return "", nil, err |
| 111 | + //} |
| 112 | + |
| 113 | + return "", nil, nil |
| 114 | +} |
| 115 | + |
| 116 | +type FlagsValidator func(cmd *cobra.Command) error |
| 117 | + |
| 118 | +func FlagValidRepository(flagName string) FlagsValidator { |
| 119 | + return func(cmd *cobra.Command) error { |
| 120 | + repositoryValue := cmd.Flag(flagName).Value.String() |
| 121 | + |
| 122 | + if strings.HasSuffix(repositoryValue, "/") || strings.Contains(repositoryValue, "//") { |
| 123 | + return fmt.Errorf("invalid repository: %s", repositoryValue) |
| 124 | + } |
| 125 | + |
| 126 | + for i, part := range strings.Split(repositoryValue, "/") { |
| 127 | + if i != 0 { |
| 128 | + if strings.ContainsAny(part, ":@\" ") { |
| 129 | + return fmt.Errorf("invalid repository: %s", repositoryValue) |
| 130 | + } |
| 131 | + continue |
| 132 | + } |
| 133 | + |
| 134 | + authorityParts := strings.Split(part, ":") |
| 135 | + if len(authorityParts) > 2 { |
| 136 | + return fmt.Errorf("invalid repository hostname: %s", part) |
| 137 | + } |
| 138 | + if errs := validation.IsDNS1123Subdomain(authorityParts[0]); len(errs) > 0 { |
| 139 | + return fmt.Errorf("invalid repository hostname: %s", strings.Join(errs, "; ")) |
| 140 | + } |
| 141 | + if len(authorityParts) == 2 { |
| 142 | + portNumber, err := strconv.Atoi(authorityParts[1]) |
| 143 | + if err != nil { |
| 144 | + return fmt.Errorf("invalid repository port number: %s", authorityParts[1]) |
| 145 | + } |
| 146 | + |
| 147 | + if errs := validation.IsValidPortNum(portNumber); len(errs) > 0 { |
| 148 | + return fmt.Errorf("invalid repository port number: %s", strings.Join(errs, "; ")) |
| 149 | + } |
| 150 | + } |
| 151 | + } |
| 152 | + |
| 153 | + return nil |
| 154 | + } |
| 155 | +} |
0 commit comments