Allows osc users to easily add and remove environment variables
from pods, replication controllers, and deployment controllers.
Environment can be read from args, params, or STDIN. Environment
can be listed, modified in place and output, or modified in place
and sent to the server for update.
Adopts the initial "generation" pattern, which allows the input
to be decorated and then passed to a subsequent program for
further decoration.
| ... | ... |
@@ -399,6 +399,19 @@ osc get deploymentConfigs |
| 399 | 399 |
osc get dc |
| 400 | 400 |
osc create -f test/integration/fixtures/test-deployment-config.json |
| 401 | 401 |
osc describe deploymentConfigs test-deployment-config |
| 402 |
+[ "$(osc env dc/test-deployment-config --list | grep TEST=value)" ] |
|
| 403 |
+[ ! "$(osc env dc/test-deployment-config TEST- --list | grep TEST=value)" ] |
|
| 404 |
+[ "$(osc env dc/test-deployment-config TEST=foo --list | grep TEST=foo)" ] |
|
| 405 |
+[ "$(osc env dc/test-deployment-config OTHER=foo --list | grep TEST=value)" ] |
|
| 406 |
+[ ! "$(osc env dc/test-deployment-config OTHER=foo -c 'ruby' --list | grep OTHER=foo)" ] |
|
| 407 |
+[ "$(osc env dc/test-deployment-config OTHER=foo -c 'ruby*' --list | grep OTHER=foo)" ] |
|
| 408 |
+[ "$(osc env dc/test-deployment-config OTHER=foo -c '*hello*' --list | grep OTHER=foo)" ] |
|
| 409 |
+[ "$(osc env dc/test-deployment-config OTHER=foo -c '*world' --list | grep OTHER=foo)" ] |
|
| 410 |
+[ "$(osc env dc/test-deployment-config OTHER=foo --list | grep OTHER=foo)" ] |
|
| 411 |
+[ "$(osc env dc/test-deployment-config OTHER=foo -o yaml | grep "name: OTHER")" ] |
|
| 412 |
+[ "$(echo "OTHER=foo" | osc env dc/test-deployment-config -e - --list | grep OTHER=foo)" ] |
|
| 413 |
+[ ! "$(echo "#OTHER=foo" | osc env dc/test-deployment-config -e - --list | grep OTHER=foo)" ] |
|
| 414 |
+[ "$(osc env dc/test-deployment-config TEST=bar OTHER=baz BAR-)" ] |
|
| 402 | 415 |
osc deploy test-deployment-config |
| 403 | 416 |
osc delete deploymentConfigs test-deployment-config |
| 404 | 417 |
echo "deploymentConfigs: ok" |
| ... | ... |
@@ -71,6 +71,7 @@ func NewCommandCLI(name, fullName string) *cobra.Command {
|
| 71 | 71 |
cmds.AddCommand(cmd.NewCmdBuildLogs(fullName, f, out)) |
| 72 | 72 |
cmds.AddCommand(cmd.NewCmdDeploy(fullName, f, out)) |
| 73 | 73 |
cmds.AddCommand(cmd.NewCmdRollback(fullName, f, out)) |
| 74 |
+ cmds.AddCommand(cmd.NewCmdEnv(fullName, f, os.Stdin, out)) |
|
| 74 | 75 |
cmds.AddCommand(cmd.NewCmdGet(fullName, f, out)) |
| 75 | 76 |
cmds.AddCommand(cmd.NewCmdDescribe(fullName, f, out)) |
| 76 | 77 |
// Deprecate 'osc apply' with 'osc create' command. |
| 77 | 78 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,419 @@ |
| 0 |
+/* |
|
| 1 |
+Copyright 2014 The Kubernetes Authors All rights reserved. |
|
| 2 |
+ |
|
| 3 |
+Licensed under the Apache License, Version 2.0 (the "License"); |
|
| 4 |
+you may not use this file except in compliance with the License. |
|
| 5 |
+You may obtain a copy of the License at |
|
| 6 |
+ |
|
| 7 |
+ http://www.apache.org/licenses/LICENSE-2.0 |
|
| 8 |
+ |
|
| 9 |
+Unless required by applicable law or agreed to in writing, software |
|
| 10 |
+distributed under the License is distributed on an "AS IS" BASIS, |
|
| 11 |
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 12 |
+See the License for the specific language governing permissions and |
|
| 13 |
+limitations under the License. |
|
| 14 |
+*/ |
|
| 15 |
+ |
|
| 16 |
+package cmd |
|
| 17 |
+ |
|
| 18 |
+import ( |
|
| 19 |
+ "bufio" |
|
| 20 |
+ "fmt" |
|
| 21 |
+ "io" |
|
| 22 |
+ "os" |
|
| 23 |
+ "strings" |
|
| 24 |
+ |
|
| 25 |
+ kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
| 26 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" |
|
| 27 |
+ cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" |
|
| 28 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource" |
|
| 29 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" |
|
| 30 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/util" |
|
| 31 |
+ "github.com/spf13/cobra" |
|
| 32 |
+ |
|
| 33 |
+ //ocutil "github.com/openshift/origin/pkg/cmd/util" |
|
| 34 |
+ "github.com/openshift/origin/pkg/cmd/util/clientcmd" |
|
| 35 |
+) |
|
| 36 |
+ |
|
| 37 |
+const ( |
|
| 38 |
+ envLong = `Update the environment on a pod |
|
| 39 |
+ |
|
| 40 |
+Each container in a pod may have its own environment variable definitions. This command can |
|
| 41 |
+add, update, or remove environment variables from containers in pods or any object that has |
|
| 42 |
+a pod template (replication controllers or deployment configurations). You can specify a |
|
| 43 |
+single object or multiple, and alter environment on all containers or just those that match |
|
| 44 |
+a wildcard. |
|
| 45 |
+ |
|
| 46 |
+If "--env -" is passed, environment variables can be read from STDIN using the standard env |
|
| 47 |
+syntax.` |
|
| 48 |
+ |
|
| 49 |
+ envExample = ` // Update deployment 'registry' with a new environment variable |
|
| 50 |
+ $ %[1]s env dc/registry STORAGE_DIR=/local |
|
| 51 |
+ |
|
| 52 |
+ // List the environment variables defined on a deployment 'registry' |
|
| 53 |
+ $ %[1]s env dc/registry --list |
|
| 54 |
+ |
|
| 55 |
+ // List the environment variables defined on all pods |
|
| 56 |
+ $ %[1]s env pods --all --list |
|
| 57 |
+ |
|
| 58 |
+ // Output a YAML object with updated enviroment for deployment 'registry' |
|
| 59 |
+ // Does not alter the object on the server |
|
| 60 |
+ $ %[1]s env dc/registry STORAGE_DIR=/local -o yaml |
|
| 61 |
+ |
|
| 62 |
+ // Update all replication controllers in the project to have ENV=prod |
|
| 63 |
+ $ %[1]s env replicationControllers --all ENV=prod |
|
| 64 |
+ |
|
| 65 |
+ // Remove the enviroment variable ENV from a pod definition on disk and update the pod on the server |
|
| 66 |
+ $ %[1]s env -f pod.json ENV- |
|
| 67 |
+ |
|
| 68 |
+ // Set some of the local shell environment into a deployment on the server |
|
| 69 |
+ $ env | grep RAILS_ | %[1]s env -e - dc/registry` |
|
| 70 |
+) |
|
| 71 |
+ |
|
| 72 |
+func NewCmdEnv(fullName string, f *clientcmd.Factory, in io.Reader, out io.Writer) *cobra.Command {
|
|
| 73 |
+ var filenames util.StringList |
|
| 74 |
+ var env util.StringList |
|
| 75 |
+ cmd := &cobra.Command{
|
|
| 76 |
+ Use: "env RESOURCE/NAME KEY_1=VAL_1 ... KEY_N=VAL_N [options]", |
|
| 77 |
+ Short: "Update the environment on a resource with a pod template", |
|
| 78 |
+ Long: envLong, |
|
| 79 |
+ Example: fmt.Sprintf(envExample, fullName), |
|
| 80 |
+ Run: func(cmd *cobra.Command, args []string) {
|
|
| 81 |
+ err := RunEnv(f, in, out, cmd, args, env, filenames) |
|
| 82 |
+ if err == errExit {
|
|
| 83 |
+ os.Exit(1) |
|
| 84 |
+ } |
|
| 85 |
+ cmdutil.CheckErr(err) |
|
| 86 |
+ }, |
|
| 87 |
+ } |
|
| 88 |
+ cmd.Flags().StringP("containers", "c", "*", "The names of containers in the selected pod templates to change - may use wildcards")
|
|
| 89 |
+ cmd.Flags().VarP(&env, "env", "e", "Specify key value pairs of environment variables to set into each container.") |
|
| 90 |
+ cmd.Flags().Bool("list", false, "Display the environment and any changes in the standard format")
|
|
| 91 |
+ cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on")
|
|
| 92 |
+ cmd.Flags().Bool("all", false, "select all resources in the namespace of the specified resource types")
|
|
| 93 |
+ cmd.Flags().VarP(&filenames, "filename", "f", "Filename, directory, or URL to file to use to edit the resource.") |
|
| 94 |
+ cmd.Flags().Bool("overwrite", true, "If true, allow environment to be overwritten, otherwise reject updates that overwrite existing environment.")
|
|
| 95 |
+ cmd.Flags().String("resource-version", "", "If non-empty, the labels update will only succeed if this is the current resource-version for the object. Only valid when specifying a single resource.")
|
|
| 96 |
+ cmd.Flags().StringP("output", "o", "", "Display the changed objects instead of updating them. One of: json|yaml.")
|
|
| 97 |
+ cmd.Flags().String("output-version", "", "Output the changed objects with the given version (default api-version).")
|
|
| 98 |
+ return cmd |
|
| 99 |
+} |
|
| 100 |
+ |
|
| 101 |
+func updateObject(info *resource.Info, updateFn func(runtime.Object) (runtime.Object, error)) (runtime.Object, error) {
|
|
| 102 |
+ helper := resource.NewHelper(info.Client, info.Mapping) |
|
| 103 |
+ |
|
| 104 |
+ obj, err := updateFn(info.Object) |
|
| 105 |
+ if err != nil {
|
|
| 106 |
+ return nil, err |
|
| 107 |
+ } |
|
| 108 |
+ data, err := helper.Codec.Encode(obj) |
|
| 109 |
+ if err != nil {
|
|
| 110 |
+ return nil, err |
|
| 111 |
+ } |
|
| 112 |
+ |
|
| 113 |
+ _, err = helper.Update(info.Namespace, info.Name, true, data) |
|
| 114 |
+ if err != nil {
|
|
| 115 |
+ return nil, err |
|
| 116 |
+ } |
|
| 117 |
+ return obj, nil |
|
| 118 |
+} |
|
| 119 |
+ |
|
| 120 |
+func validateNoOverwrites(meta *kapi.ObjectMeta, labels map[string]string) error {
|
|
| 121 |
+ for key := range labels {
|
|
| 122 |
+ if value, found := meta.Labels[key]; found {
|
|
| 123 |
+ return fmt.Errorf("'%s' already has a value (%s), and --overwrite is false", key, value)
|
|
| 124 |
+ } |
|
| 125 |
+ } |
|
| 126 |
+ return nil |
|
| 127 |
+} |
|
| 128 |
+ |
|
| 129 |
+func parseEnv(spec []string, defaultReader io.Reader) ([]kapi.EnvVar, []string, error) {
|
|
| 130 |
+ env := []kapi.EnvVar{}
|
|
| 131 |
+ exists := util.NewStringSet() |
|
| 132 |
+ var remove []string |
|
| 133 |
+ for _, envSpec := range spec {
|
|
| 134 |
+ switch {
|
|
| 135 |
+ case envSpec == "-": |
|
| 136 |
+ if defaultReader == nil {
|
|
| 137 |
+ return nil, nil, fmt.Errorf("when '-' is used, STDIN must be open")
|
|
| 138 |
+ } |
|
| 139 |
+ fileEnv, err := readEnv(defaultReader) |
|
| 140 |
+ if err != nil {
|
|
| 141 |
+ return nil, nil, err |
|
| 142 |
+ } |
|
| 143 |
+ env = append(env, fileEnv...) |
|
| 144 |
+ case strings.Index(envSpec, "=") != -1: |
|
| 145 |
+ parts := strings.SplitN(envSpec, "=", 2) |
|
| 146 |
+ if len(parts) != 2 {
|
|
| 147 |
+ return nil, nil, fmt.Errorf("invalid environment variable: %v", envSpec)
|
|
| 148 |
+ } |
|
| 149 |
+ exists.Insert(parts[0]) |
|
| 150 |
+ env = append(env, kapi.EnvVar{
|
|
| 151 |
+ Name: parts[0], |
|
| 152 |
+ Value: parts[1], |
|
| 153 |
+ }) |
|
| 154 |
+ case strings.HasSuffix(envSpec, "-"): |
|
| 155 |
+ remove = append(remove, envSpec[:len(envSpec)-1]) |
|
| 156 |
+ default: |
|
| 157 |
+ return nil, nil, fmt.Errorf("unknown environment variable: %v", envSpec)
|
|
| 158 |
+ } |
|
| 159 |
+ } |
|
| 160 |
+ for _, removeLabel := range remove {
|
|
| 161 |
+ if _, found := exists[removeLabel]; found {
|
|
| 162 |
+ return nil, nil, fmt.Errorf("can not both modify and remove an environment variable in the same command")
|
|
| 163 |
+ } |
|
| 164 |
+ } |
|
| 165 |
+ return env, remove, nil |
|
| 166 |
+} |
|
| 167 |
+ |
|
| 168 |
+func readEnv(r io.Reader) ([]kapi.EnvVar, error) {
|
|
| 169 |
+ env := []kapi.EnvVar{}
|
|
| 170 |
+ scanner := bufio.NewScanner(r) |
|
| 171 |
+ for scanner.Scan() {
|
|
| 172 |
+ envSpec := scanner.Text() |
|
| 173 |
+ if pos := strings.Index(envSpec, "#"); pos != -1 {
|
|
| 174 |
+ envSpec = envSpec[:pos] |
|
| 175 |
+ } |
|
| 176 |
+ if strings.Index(envSpec, "=") != -1 {
|
|
| 177 |
+ parts := strings.SplitN(envSpec, "=", 2) |
|
| 178 |
+ if len(parts) != 2 {
|
|
| 179 |
+ return nil, fmt.Errorf("invalid environment variable: %v", envSpec)
|
|
| 180 |
+ } |
|
| 181 |
+ env = append(env, kapi.EnvVar{
|
|
| 182 |
+ Name: parts[0], |
|
| 183 |
+ Value: parts[1], |
|
| 184 |
+ }) |
|
| 185 |
+ } |
|
| 186 |
+ } |
|
| 187 |
+ if err := scanner.Err(); err != nil && err != io.EOF {
|
|
| 188 |
+ return nil, err |
|
| 189 |
+ } |
|
| 190 |
+ return env, nil |
|
| 191 |
+} |
|
| 192 |
+ |
|
| 193 |
+func RunEnv(f *clientcmd.Factory, in io.Reader, out io.Writer, cmd *cobra.Command, args []string, envParams, filenames util.StringList) error {
|
|
| 194 |
+ resources, envArgs := []string{}, []string{}
|
|
| 195 |
+ first := true |
|
| 196 |
+ for _, s := range args {
|
|
| 197 |
+ isEnv := strings.Contains(s, "=") || strings.HasSuffix(s, "-") |
|
| 198 |
+ switch {
|
|
| 199 |
+ case first && isEnv: |
|
| 200 |
+ first = false |
|
| 201 |
+ fallthrough |
|
| 202 |
+ case !first && isEnv: |
|
| 203 |
+ envArgs = append(envArgs, s) |
|
| 204 |
+ case first && !isEnv: |
|
| 205 |
+ resources = append(resources, s) |
|
| 206 |
+ case !first && !isEnv: |
|
| 207 |
+ return cmdutil.UsageError(cmd, "all resources must be specified before environment changes: %s", s) |
|
| 208 |
+ } |
|
| 209 |
+ } |
|
| 210 |
+ if len(resources) < 1 {
|
|
| 211 |
+ return cmdutil.UsageError(cmd, "one or more resources must be specified as <resource> <name> or <resource>/<name>") |
|
| 212 |
+ } |
|
| 213 |
+ |
|
| 214 |
+ containerMatch := cmdutil.GetFlagString(cmd, "containers") |
|
| 215 |
+ list := cmdutil.GetFlagBool(cmd, "list") |
|
| 216 |
+ selector := cmdutil.GetFlagString(cmd, "selector") |
|
| 217 |
+ all := cmdutil.GetFlagBool(cmd, "all") |
|
| 218 |
+ //overwrite := cmdutil.GetFlagBool(cmd, "overwrite") |
|
| 219 |
+ resourceVersion := cmdutil.GetFlagString(cmd, "resource-version") |
|
| 220 |
+ outputFormat := cmdutil.GetFlagString(cmd, "output") |
|
| 221 |
+ |
|
| 222 |
+ if list && len(outputFormat) > 0 {
|
|
| 223 |
+ return cmdutil.UsageError(cmd, "--list and --output may not be specified together") |
|
| 224 |
+ } |
|
| 225 |
+ |
|
| 226 |
+ clientConfig, err := f.ClientConfig() |
|
| 227 |
+ if err != nil {
|
|
| 228 |
+ return err |
|
| 229 |
+ } |
|
| 230 |
+ outputVersion := cmdutil.OutputVersion(cmd, clientConfig.Version) |
|
| 231 |
+ |
|
| 232 |
+ cmdNamespace, err := f.DefaultNamespace() |
|
| 233 |
+ if err != nil {
|
|
| 234 |
+ return err |
|
| 235 |
+ } |
|
| 236 |
+ |
|
| 237 |
+ env, remove, err := parseEnv(append(envParams, envArgs...), in) |
|
| 238 |
+ if err != nil {
|
|
| 239 |
+ return err |
|
| 240 |
+ } |
|
| 241 |
+ |
|
| 242 |
+ mapper, typer := f.Object() |
|
| 243 |
+ b := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). |
|
| 244 |
+ ContinueOnError(). |
|
| 245 |
+ NamespaceParam(cmdNamespace).DefaultNamespace(). |
|
| 246 |
+ FilenameParam(filenames...). |
|
| 247 |
+ SelectorParam(selector). |
|
| 248 |
+ ResourceTypeOrNameArgs(all, resources...). |
|
| 249 |
+ Flatten() |
|
| 250 |
+ |
|
| 251 |
+ one := false |
|
| 252 |
+ infos, err := b.Do().IntoSingular(&one).Infos() |
|
| 253 |
+ if err != nil {
|
|
| 254 |
+ return err |
|
| 255 |
+ } |
|
| 256 |
+ // only apply resource version locking on a single resource |
|
| 257 |
+ if !one && len(resourceVersion) > 0 {
|
|
| 258 |
+ return cmdutil.UsageError(cmd, "--resource-version may only be used with a single resource") |
|
| 259 |
+ } |
|
| 260 |
+ |
|
| 261 |
+ skipped := 0 |
|
| 262 |
+ for _, info := range infos {
|
|
| 263 |
+ ok, err := f.UpdatePodSpecForObject(info.Object, func(spec *kapi.PodSpec) error {
|
|
| 264 |
+ containers := selectContainers(spec.Containers, containerMatch) |
|
| 265 |
+ if len(containers) == 0 {
|
|
| 266 |
+ fmt.Fprintf(cmd.Out(), "warning: %s/%s does not have any containers matching %q\n", info.Mapping.Resource, info.Name, containerMatch) |
|
| 267 |
+ return nil |
|
| 268 |
+ } |
|
| 269 |
+ for _, c := range containers {
|
|
| 270 |
+ c.Env = updateEnv(c.Env, env, remove) |
|
| 271 |
+ |
|
| 272 |
+ if list {
|
|
| 273 |
+ fmt.Fprintf(out, "# %s %s, container %s\n", info.Mapping.Resource, info.Name, c.Name) |
|
| 274 |
+ for _, env := range c.Env {
|
|
| 275 |
+ // if env.ValueFrom != nil && env.ValueFrom.FieldRef != nil {
|
|
| 276 |
+ // fmt.Fprintf(cmd.Out(), "%s= # calculated from pod %s %s\n", env.Name, env.ValueFrom.FieldRef.FieldPath, env.ValueFrom.FieldRef.APIVersion) |
|
| 277 |
+ // continue |
|
| 278 |
+ // } |
|
| 279 |
+ fmt.Fprintf(out, "%s=%s\n", env.Name, env.Value) |
|
| 280 |
+ |
|
| 281 |
+ } |
|
| 282 |
+ } |
|
| 283 |
+ } |
|
| 284 |
+ return nil |
|
| 285 |
+ }) |
|
| 286 |
+ if !ok {
|
|
| 287 |
+ skipped++ |
|
| 288 |
+ continue |
|
| 289 |
+ } |
|
| 290 |
+ if err != nil {
|
|
| 291 |
+ fmt.Fprintf(cmd.Out(), "error: %s/%s %v\n", info.Mapping.Resource, info.Name, err) |
|
| 292 |
+ continue |
|
| 293 |
+ } |
|
| 294 |
+ } |
|
| 295 |
+ if one && skipped == len(infos) {
|
|
| 296 |
+ return fmt.Errorf("the %s %s is not a pod or does not have a pod template", infos[0].Mapping.Resource, infos[0].Name)
|
|
| 297 |
+ } |
|
| 298 |
+ |
|
| 299 |
+ if list {
|
|
| 300 |
+ return nil |
|
| 301 |
+ } |
|
| 302 |
+ |
|
| 303 |
+ objects, err := resource.AsVersionedObject(infos, false, outputVersion) |
|
| 304 |
+ if err != nil {
|
|
| 305 |
+ return err |
|
| 306 |
+ } |
|
| 307 |
+ |
|
| 308 |
+ if len(outputFormat) != 0 {
|
|
| 309 |
+ p, _, err := kubectl.GetPrinter(outputFormat, "") |
|
| 310 |
+ if err != nil {
|
|
| 311 |
+ return err |
|
| 312 |
+ } |
|
| 313 |
+ return p.PrintObj(objects, out) |
|
| 314 |
+ } |
|
| 315 |
+ |
|
| 316 |
+ failed := false |
|
| 317 |
+ for _, info := range infos {
|
|
| 318 |
+ data, err := info.Mapping.Codec.Encode(info.Object) |
|
| 319 |
+ if err != nil {
|
|
| 320 |
+ fmt.Fprintf(cmd.Out(), "Error: %v\n", err) |
|
| 321 |
+ failed = true |
|
| 322 |
+ continue |
|
| 323 |
+ } |
|
| 324 |
+ obj, err := resource.NewHelper(info.Client, info.Mapping).Update(info.Namespace, info.Name, true, data) |
|
| 325 |
+ if err != nil {
|
|
| 326 |
+ fmt.Fprintf(cmd.Out(), "Error: %v\n", err) |
|
| 327 |
+ failed = true |
|
| 328 |
+ continue |
|
| 329 |
+ } |
|
| 330 |
+ info.Refresh(obj, true) |
|
| 331 |
+ fmt.Fprintf(out, "%s/%s\n", info.Mapping.Resource, info.Name) |
|
| 332 |
+ } |
|
| 333 |
+ if failed {
|
|
| 334 |
+ return errExit |
|
| 335 |
+ } |
|
| 336 |
+ return nil |
|
| 337 |
+} |
|
| 338 |
+ |
|
| 339 |
+func updateEnv(existing []kapi.EnvVar, env []kapi.EnvVar, remove []string) []kapi.EnvVar {
|
|
| 340 |
+ out := []kapi.EnvVar{}
|
|
| 341 |
+ covered := util.NewStringSet(remove...) |
|
| 342 |
+ for _, e := range existing {
|
|
| 343 |
+ if covered.Has(e.Name) {
|
|
| 344 |
+ continue |
|
| 345 |
+ } |
|
| 346 |
+ newer, ok := findEnv(env, e.Name) |
|
| 347 |
+ if ok {
|
|
| 348 |
+ covered.Insert(e.Name) |
|
| 349 |
+ out = append(out, newer) |
|
| 350 |
+ continue |
|
| 351 |
+ } |
|
| 352 |
+ out = append(out, e) |
|
| 353 |
+ } |
|
| 354 |
+ for _, e := range env {
|
|
| 355 |
+ if covered.Has(e.Name) {
|
|
| 356 |
+ continue |
|
| 357 |
+ } |
|
| 358 |
+ covered.Insert(e.Name) |
|
| 359 |
+ out = append(out, e) |
|
| 360 |
+ } |
|
| 361 |
+ return out |
|
| 362 |
+} |
|
| 363 |
+ |
|
| 364 |
+func findEnv(env []kapi.EnvVar, name string) (kapi.EnvVar, bool) {
|
|
| 365 |
+ for _, e := range env {
|
|
| 366 |
+ if e.Name == name {
|
|
| 367 |
+ return e, true |
|
| 368 |
+ } |
|
| 369 |
+ } |
|
| 370 |
+ return kapi.EnvVar{}, false
|
|
| 371 |
+} |
|
| 372 |
+ |
|
| 373 |
+func selectContainers(containers []kapi.Container, spec string) []*kapi.Container {
|
|
| 374 |
+ out := []*kapi.Container{}
|
|
| 375 |
+ for i, c := range containers {
|
|
| 376 |
+ if selectString(c.Name, spec) {
|
|
| 377 |
+ out = append(out, &containers[i]) |
|
| 378 |
+ } |
|
| 379 |
+ } |
|
| 380 |
+ return out |
|
| 381 |
+} |
|
| 382 |
+ |
|
| 383 |
+// selectString returns true if the provided string matches spec, where spec is a string with |
|
| 384 |
+// a non-greedy '*' wildcard operator. |
|
| 385 |
+// TODO: turn into a regex and handle greedy matches and backtracking. |
|
| 386 |
+func selectString(s, spec string) bool {
|
|
| 387 |
+ if spec == "*" {
|
|
| 388 |
+ return true |
|
| 389 |
+ } |
|
| 390 |
+ if !strings.Contains(spec, "*") {
|
|
| 391 |
+ return s == spec |
|
| 392 |
+ } |
|
| 393 |
+ |
|
| 394 |
+ pos := 0 |
|
| 395 |
+ match := true |
|
| 396 |
+ parts := strings.Split(spec, "*") |
|
| 397 |
+ for i, part := range parts {
|
|
| 398 |
+ if len(part) == 0 {
|
|
| 399 |
+ continue |
|
| 400 |
+ } |
|
| 401 |
+ next := strings.Index(s[pos:], part) |
|
| 402 |
+ switch {
|
|
| 403 |
+ // next part not in string |
|
| 404 |
+ case next < pos: |
|
| 405 |
+ fallthrough |
|
| 406 |
+ // first part does not match start of string |
|
| 407 |
+ case i == 0 && pos != 0: |
|
| 408 |
+ fallthrough |
|
| 409 |
+ // last part does not exactly match remaining part of string |
|
| 410 |
+ case i == (len(parts)-1) && len(s) != (len(part)+next): |
|
| 411 |
+ match = false |
|
| 412 |
+ break |
|
| 413 |
+ default: |
|
| 414 |
+ pos = next |
|
| 415 |
+ } |
|
| 416 |
+ } |
|
| 417 |
+ return match |
|
| 418 |
+} |
| ... | ... |
@@ -15,6 +15,7 @@ import ( |
| 15 | 15 |
"github.com/openshift/origin/pkg/api/latest" |
| 16 | 16 |
"github.com/openshift/origin/pkg/client" |
| 17 | 17 |
"github.com/openshift/origin/pkg/cmd/cli/describe" |
| 18 |
+ deployapi "github.com/openshift/origin/pkg/deploy/api" |
|
| 18 | 19 |
|
| 19 | 20 |
"github.com/spf13/pflag" |
| 20 | 21 |
) |
| ... | ... |
@@ -108,6 +109,36 @@ func NewFactory(clientConfig kclientcmd.ClientConfig) *Factory {
|
| 108 | 108 |
return w |
| 109 | 109 |
} |
| 110 | 110 |
|
| 111 |
+// TODO: move to upstream |
|
| 112 |
+func (f *Factory) UpdatePodSpecForObject(obj runtime.Object, fn func(*api.PodSpec) error) (bool, error) {
|
|
| 113 |
+ // TODO: replace with a swagger schema based approach (identify pod template via schema introspection) |
|
| 114 |
+ switch t := obj.(type) {
|
|
| 115 |
+ case *api.Pod: |
|
| 116 |
+ return true, fn(&t.Spec) |
|
| 117 |
+ case *api.PodTemplate: |
|
| 118 |
+ return true, fn(&t.Template.Spec) |
|
| 119 |
+ case *api.ReplicationController: |
|
| 120 |
+ if t.Spec.TemplateRef != nil {
|
|
| 121 |
+ return true, fmt.Errorf("references to pod templates (%s/%s) cannot be updated", t.Spec.TemplateRef.Namespace, t.Spec.TemplateRef.Name)
|
|
| 122 |
+ } |
|
| 123 |
+ if t.Spec.Template == nil {
|
|
| 124 |
+ t.Spec.Template = &api.PodTemplateSpec{}
|
|
| 125 |
+ } |
|
| 126 |
+ return true, fn(&t.Spec.Template.Spec) |
|
| 127 |
+ case *deployapi.DeploymentConfig: |
|
| 128 |
+ template := t.Template.ControllerTemplate |
|
| 129 |
+ if template.TemplateRef != nil {
|
|
| 130 |
+ return true, fmt.Errorf("deployment configs with references to pod templates (%s/%s) cannot be updated", template.TemplateRef.Namespace, template.TemplateRef.Name)
|
|
| 131 |
+ } |
|
| 132 |
+ if template.Template == nil {
|
|
| 133 |
+ template.Template = &api.PodTemplateSpec{}
|
|
| 134 |
+ } |
|
| 135 |
+ return true, fn(&template.Template.Spec) |
|
| 136 |
+ default: |
|
| 137 |
+ return false, fmt.Errorf("the object is not a pod or does not have a pod template")
|
|
| 138 |
+ } |
|
| 139 |
+} |
|
| 140 |
+ |
|
| 111 | 141 |
// Clients returns an OpenShift and Kubernetes client. |
| 112 | 142 |
func (f *Factory) Clients() (*client.Client, *kclient.Client, error) {
|
| 113 | 143 |
kClient, err := f.Client() |
| ... | ... |
@@ -35,7 +35,7 @@ func GetEnv(key string) (string, bool) {
|
| 35 | 35 |
|
| 36 | 36 |
type Environment map[string]string |
| 37 | 37 |
|
| 38 |
-var argumentEnvironment = regexp.MustCompile("^([\\w\\-]+)\\=(.*)$")
|
|
| 38 |
+var argumentEnvironment = regexp.MustCompile("^([\\w\\-_]+)\\=(.*)$")
|
|
| 39 | 39 |
|
| 40 | 40 |
func IsEnvironmentArgument(s string) bool {
|
| 41 | 41 |
return argumentEnvironment.MatchString(s) |