diff --git a/.generated_docs b/.generated_docs
index 8d5233ff08a..262efbfd7f3 100644
--- a/.generated_docs
+++ b/.generated_docs
@@ -22,6 +22,7 @@ docs/man/man1/kubectl-config-view.1
docs/man/man1/kubectl-config.1
docs/man/man1/kubectl-convert.1
docs/man/man1/kubectl-cordon.1
+docs/man/man1/kubectl-create-configmap.1
docs/man/man1/kubectl-create-namespace.1
docs/man/man1/kubectl-create-secret-docker-registry.1
docs/man/man1/kubectl-create-secret-generic.1
@@ -73,6 +74,7 @@ docs/user-guide/kubectl/kubectl_config_view.md
docs/user-guide/kubectl/kubectl_convert.md
docs/user-guide/kubectl/kubectl_cordon.md
docs/user-guide/kubectl/kubectl_create.md
+docs/user-guide/kubectl/kubectl_create_configmap.md
docs/user-guide/kubectl/kubectl_create_namespace.md
docs/user-guide/kubectl/kubectl_create_secret.md
docs/user-guide/kubectl/kubectl_create_secret_docker-registry.md
diff --git a/contrib/completions/bash/kubectl b/contrib/completions/bash/kubectl
index eab1812c4ec..8a6791f4a6d 100644
--- a/contrib/completions/bash/kubectl
+++ b/contrib/completions/bash/kubectl
@@ -621,12 +621,69 @@ _kubectl_create_secret()
must_have_one_noun=()
}
+_kubectl_create_configmap()
+{
+ last_command="kubectl_create_configmap"
+ commands=()
+
+ flags=()
+ two_word_flags=()
+ flags_with_completion=()
+ flags_completion=()
+
+ flags+=("--dry-run")
+ flags+=("--from-file=")
+ flags+=("--from-literal=")
+ flags+=("--generator=")
+ flags+=("--no-headers")
+ flags+=("--output=")
+ two_word_flags+=("-o")
+ flags+=("--output-version=")
+ flags+=("--save-config")
+ flags+=("--schema-cache-dir=")
+ flags+=("--show-all")
+ flags+=("-a")
+ flags+=("--show-labels")
+ flags+=("--sort-by=")
+ flags+=("--template=")
+ two_word_flags+=("-t")
+ flags+=("--validate")
+ flags+=("--alsologtostderr")
+ flags+=("--api-version=")
+ flags+=("--certificate-authority=")
+ flags+=("--client-certificate=")
+ flags+=("--client-key=")
+ flags+=("--cluster=")
+ flags+=("--context=")
+ flags+=("--insecure-skip-tls-verify")
+ flags+=("--kubeconfig=")
+ flags+=("--log-backtrace-at=")
+ flags+=("--log-dir=")
+ flags+=("--log-flush-frequency=")
+ flags+=("--logtostderr")
+ flags+=("--match-server-version")
+ flags+=("--namespace=")
+ flags+=("--password=")
+ flags+=("--server=")
+ two_word_flags+=("-s")
+ flags+=("--stderrthreshold=")
+ flags+=("--token=")
+ flags+=("--user=")
+ flags+=("--username=")
+ flags+=("--v=")
+ flags+=("--vmodule=")
+
+ must_have_one_flag=()
+ must_have_one_noun=()
+}
+
_kubectl_create()
{
last_command="kubectl_create"
commands=()
commands+=("namespace")
commands+=("secret")
+ commands+=("configmap")
flags=()
two_word_flags=()
diff --git a/docs/man/man1/kubectl-create-configmap.1 b/docs/man/man1/kubectl-create-configmap.1
new file mode 100644
index 00000000000..f5b636fb4f7
--- /dev/null
+++ b/docs/man/man1/kubectl-create-configmap.1
@@ -0,0 +1,211 @@
+.TH "KUBERNETES" "1" " kubernetes User Manuals" "Eric Paris" "Jan 2015" ""
+
+
+.SH NAME
+.PP
+kubectl create configmap \- Create a configMap from a local file, directory or literal value.
+
+
+.SH SYNOPSIS
+.PP
+\fBkubectl create configmap\fP [OPTIONS]
+
+
+.SH DESCRIPTION
+.PP
+Create a configmap based on a file, directory, or specified literal value.
+
+.PP
+A single configmap may package one or more key/value pairs.
+
+.PP
+When creating a configmap based on a file, the key will default to the basename of the file, and the value will
+default to the file content. If the basename is an invalid key, you may specify an alternate key.
+
+.PP
+When creating a configmap based on a directory, each file whose basename is a valid key in the directory will be
+packaged into the configmap. Any directory entries except regular files are ignored (e.g. subdirectories,
+symlinks, devices, pipes, etc).
+
+
+.SH OPTIONS
+.PP
+\fB\-\-dry\-run\fP=false
+ If true, only print the object that would be sent, without sending it.
+
+.PP
+\fB\-\-from\-file\fP=[]
+ Key files can be specified using their file path, in which case a default name will be given to them, or optionally with a name and file path, in which case the given name will be used. Specifying a directory will iterate each named file in the directory that is a valid configmap key.
+
+.PP
+\fB\-\-from\-literal\fP=[]
+ Specify a key and literal value to insert in configmap (i.e. mykey=somevalue)
+
+.PP
+\fB\-\-generator\fP="configmap/v1"
+ The name of the API generator to use.
+
+.PP
+\fB\-\-no\-headers\fP=false
+ When using the default output, don't print headers.
+
+.PP
+\fB\-o\fP, \fB\-\-output\fP=""
+ Output format. One of: json|yaml|wide|name|go\-template=...|go\-template\-file=...|jsonpath=...|jsonpath\-file=... See golang template [
+\[la]http://golang.org/pkg/text/template/#pkg-overview\[ra]] and jsonpath template [
+\[la]http://releases.k8s.io/HEAD/docs/user-guide/jsonpath.md\[ra]].
+
+.PP
+\fB\-\-output\-version\fP=""
+ Output the formatted object with the given version (default api\-version).
+
+.PP
+\fB\-\-save\-config\fP=false
+ If true, the configuration of current object will be saved in its annotation. This is useful when you want to perform kubectl apply on this object in the future.
+
+.PP
+\fB\-\-schema\-cache\-dir\fP="\~/.kube/schema"
+ If non\-empty, load/store cached API schemas in this directory, default is '$HOME/.kube/schema'
+
+.PP
+\fB\-a\fP, \fB\-\-show\-all\fP=false
+ When printing, show all resources (default hide terminated pods.)
+
+.PP
+\fB\-\-show\-labels\fP=false
+ When printing, show all labels as the last column (default hide labels column)
+
+.PP
+\fB\-\-sort\-by\fP=""
+ If non\-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. '{.metadata.name}'). The field in the API resource specified by this JSONPath expression must be an integer or a string.
+
+.PP
+\fB\-\-template\fP=""
+ Template string or path to template file to use when \-o=go\-template, \-o=go\-template\-file. The template format is golang templates [
+\[la]http://golang.org/pkg/text/template/#pkg-overview\[ra]].
+
+.PP
+\fB\-\-validate\fP=true
+ If true, use a schema to validate the input before sending it
+
+
+.SH OPTIONS INHERITED FROM PARENT COMMANDS
+.PP
+\fB\-\-alsologtostderr\fP=false
+ log to standard error as well as files
+
+.PP
+\fB\-\-api\-version\fP=""
+ The API version to use when talking to the server
+
+.PP
+\fB\-\-certificate\-authority\fP=""
+ Path to a cert. file for the certificate authority.
+
+.PP
+\fB\-\-client\-certificate\fP=""
+ Path to a client certificate file for TLS.
+
+.PP
+\fB\-\-client\-key\fP=""
+ Path to a client key file for TLS.
+
+.PP
+\fB\-\-cluster\fP=""
+ The name of the kubeconfig cluster to use
+
+.PP
+\fB\-\-context\fP=""
+ The name of the kubeconfig context to use
+
+.PP
+\fB\-\-insecure\-skip\-tls\-verify\fP=false
+ If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure.
+
+.PP
+\fB\-\-kubeconfig\fP=""
+ Path to the kubeconfig file to use for CLI requests.
+
+.PP
+\fB\-\-log\-backtrace\-at\fP=:0
+ when logging hits line file:N, emit a stack trace
+
+.PP
+\fB\-\-log\-dir\fP=""
+ If non\-empty, write log files in this directory
+
+.PP
+\fB\-\-log\-flush\-frequency\fP=5s
+ Maximum number of seconds between log flushes
+
+.PP
+\fB\-\-logtostderr\fP=true
+ log to standard error instead of files
+
+.PP
+\fB\-\-match\-server\-version\fP=false
+ Require server version to match client version
+
+.PP
+\fB\-\-namespace\fP=""
+ If present, the namespace scope for this CLI request.
+
+.PP
+\fB\-\-password\fP=""
+ Password for basic authentication to the API server.
+
+.PP
+\fB\-s\fP, \fB\-\-server\fP=""
+ The address and port of the Kubernetes API server
+
+.PP
+\fB\-\-stderrthreshold\fP=2
+ logs at or above this threshold go to stderr
+
+.PP
+\fB\-\-token\fP=""
+ Bearer token for authentication to the API server.
+
+.PP
+\fB\-\-user\fP=""
+ The name of the kubeconfig user to use
+
+.PP
+\fB\-\-username\fP=""
+ Username for basic authentication to the API server.
+
+.PP
+\fB\-\-v\fP=0
+ log level for V logs
+
+.PP
+\fB\-\-vmodule\fP=
+ comma\-separated list of pattern=N settings for file\-filtered logging
+
+
+.SH EXAMPLE
+.PP
+.RS
+
+.nf
+ # Create a new configmap named my\-config with keys for each file in folder bar
+ $ kubectl create configmap generic my\-config \-\-from\-file=path/to/bar
+
+ # Create a new configmap named my\-config with specified keys instead of names on disk
+ $ kubectl create configmap generic my\-config \-\-from\-file=ssh\-privatekey=\~/.ssh/id\_rsa \-\-from\-file=ssh\-publickey=\~/.ssh/id\_rsa.pub
+
+ # Create a new configMap named my\-config with key1=config1 and key2=config2
+ $ kubectl create configmap generic my\-config \-\-from\-literal=key1=config1 \-\-from\-literal=key2=config2
+
+.fi
+.RE
+
+
+.SH SEE ALSO
+.PP
+\fBkubectl\-create(1)\fP,
+
+
+.SH HISTORY
+.PP
+January 2015, Originally compiled by Eric Paris (eparis at redhat dot com) based on the kubernetes source material, but hopefully they have been automatically generated since!
diff --git a/docs/man/man1/kubectl-create-secret-generic.1 b/docs/man/man1/kubectl-create-secret-generic.1
index 61ec48456e4..14b42a83a44 100644
--- a/docs/man/man1/kubectl-create-secret-generic.1
+++ b/docs/man/man1/kubectl-create-secret-generic.1
@@ -13,7 +13,7 @@ kubectl create secret generic \- Create a secret from a local file, directory or
.SH DESCRIPTION
.PP
-Create a secret based on a file, directory, or specified literal value
+Create a secret based on a file, directory, or specified literal value.
.PP
A single secret may package one or more key/value pairs.
diff --git a/docs/man/man1/kubectl-create.1 b/docs/man/man1/kubectl-create.1
index 958b3ec2c71..383d3017316 100644
--- a/docs/man/man1/kubectl-create.1
+++ b/docs/man/man1/kubectl-create.1
@@ -156,7 +156,7 @@ $ cat pod.json | kubectl create \-f \-
.SH SEE ALSO
.PP
-\fBkubectl(1)\fP, \fBkubectl\-create\-namespace(1)\fP, \fBkubectl\-create\-secret(1)\fP,
+\fBkubectl(1)\fP, \fBkubectl\-create\-namespace(1)\fP, \fBkubectl\-create\-secret(1)\fP, \fBkubectl\-create\-configmap(1)\fP,
.SH HISTORY
diff --git a/docs/user-guide/kubectl/kubectl_create.md b/docs/user-guide/kubectl/kubectl_create.md
index 4ac999e08e7..311eec64cfc 100644
--- a/docs/user-guide/kubectl/kubectl_create.md
+++ b/docs/user-guide/kubectl/kubectl_create.md
@@ -99,10 +99,11 @@ $ cat pod.json | kubectl create -f -
### SEE ALSO
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
+* [kubectl create configmap](kubectl_create_configmap.md) - Create a configMap from a local file, directory or literal value.
* [kubectl create namespace](kubectl_create_namespace.md) - Create a namespace with the specified name.
* [kubectl create secret](kubectl_create_secret.md) - Create a secret using specified subcommand.
-###### Auto generated by spf13/cobra on 22-Jan-2016
+###### Auto generated by spf13/cobra on 17-Feb-2016
[]()
diff --git a/docs/user-guide/kubectl/kubectl_create_configmap.md b/docs/user-guide/kubectl/kubectl_create_configmap.md
new file mode 100644
index 00000000000..00117f729ac
--- /dev/null
+++ b/docs/user-guide/kubectl/kubectl_create_configmap.md
@@ -0,0 +1,121 @@
+
+
+
+
+
+
+
+
+
+
+
PLEASE NOTE: This document applies to the HEAD of the source tree
+
+If you are using a released version of Kubernetes, you should
+refer to the docs that go with that version.
+
+Documentation for other releases can be found at
+[releases.k8s.io](http://releases.k8s.io).
+
+--
+
+
+
+
+
+## kubectl create configmap
+
+Create a configMap from a local file, directory or literal value.
+
+### Synopsis
+
+
+Create a configmap based on a file, directory, or specified literal value.
+
+A single configmap may package one or more key/value pairs.
+
+When creating a configmap based on a file, the key will default to the basename of the file, and the value will
+default to the file content. If the basename is an invalid key, you may specify an alternate key.
+
+When creating a configmap based on a directory, each file whose basename is a valid key in the directory will be
+packaged into the configmap. Any directory entries except regular files are ignored (e.g. subdirectories,
+symlinks, devices, pipes, etc).
+
+
+```
+kubectl create configmap NAME [--type=string] [--from-file=[key=]source] [--from-literal=key1=value1] [--dry-run]
+```
+
+### Examples
+
+```
+ # Create a new configmap named my-config with keys for each file in folder bar
+ $ kubectl create configmap generic my-config --from-file=path/to/bar
+
+ # Create a new configmap named my-config with specified keys instead of names on disk
+ $ kubectl create configmap generic my-config --from-file=ssh-privatekey=~/.ssh/id_rsa --from-file=ssh-publickey=~/.ssh/id_rsa.pub
+
+ # Create a new configMap named my-config with key1=config1 and key2=config2
+ $ kubectl create configmap generic my-config --from-literal=key1=config1 --from-literal=key2=config2
+```
+
+### Options
+
+```
+ --dry-run[=false]: If true, only print the object that would be sent, without sending it.
+ --from-file=[]: Key files can be specified using their file path, in which case a default name will be given to them, or optionally with a name and file path, in which case the given name will be used. Specifying a directory will iterate each named file in the directory that is a valid configmap key.
+ --from-literal=[]: Specify a key and literal value to insert in configmap (i.e. mykey=somevalue)
+ --generator="configmap/v1": The name of the API generator to use.
+ --no-headers[=false]: When using the default output, don't print headers.
+ -o, --output="": Output format. One of: json|yaml|wide|name|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=... See golang template [http://golang.org/pkg/text/template/#pkg-overview] and jsonpath template [http://releases.k8s.io/HEAD/docs/user-guide/jsonpath.md].
+ --output-version="": Output the formatted object with the given version (default api-version).
+ --save-config[=false]: If true, the configuration of current object will be saved in its annotation. This is useful when you want to perform kubectl apply on this object in the future.
+ --schema-cache-dir="~/.kube/schema": If non-empty, load/store cached API schemas in this directory, default is '$HOME/.kube/schema'
+ -a, --show-all[=false]: When printing, show all resources (default hide terminated pods.)
+ --show-labels[=false]: When printing, show all labels as the last column (default hide labels column)
+ --sort-by="": If non-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. '{.metadata.name}'). The field in the API resource specified by this JSONPath expression must be an integer or a string.
+ --template="": Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview].
+ --validate[=true]: If true, use a schema to validate the input before sending it
+```
+
+### Options inherited from parent commands
+
+```
+ --alsologtostderr[=false]: log to standard error as well as files
+ --api-version="": The API version to use when talking to the server
+ --certificate-authority="": Path to a cert. file for the certificate authority.
+ --client-certificate="": Path to a client certificate file for TLS.
+ --client-key="": Path to a client key file for TLS.
+ --cluster="": The name of the kubeconfig cluster to use
+ --context="": The name of the kubeconfig context to use
+ --insecure-skip-tls-verify[=false]: If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure.
+ --kubeconfig="": Path to the kubeconfig file to use for CLI requests.
+ --log-backtrace-at=:0: when logging hits line file:N, emit a stack trace
+ --log-dir="": If non-empty, write log files in this directory
+ --log-flush-frequency=5s: Maximum number of seconds between log flushes
+ --logtostderr[=true]: log to standard error instead of files
+ --match-server-version[=false]: Require server version to match client version
+ --namespace="": If present, the namespace scope for this CLI request.
+ --password="": Password for basic authentication to the API server.
+ -s, --server="": The address and port of the Kubernetes API server
+ --stderrthreshold=2: logs at or above this threshold go to stderr
+ --token="": Bearer token for authentication to the API server.
+ --user="": The name of the kubeconfig user to use
+ --username="": Username for basic authentication to the API server.
+ --v=0: log level for V logs
+ --vmodule=: comma-separated list of pattern=N settings for file-filtered logging
+```
+
+### SEE ALSO
+
+* [kubectl create](kubectl_create.md) - Create a resource by filename or stdin
+
+###### Auto generated by spf13/cobra on 18-Feb-2016
+
+
+[]()
+
diff --git a/docs/user-guide/kubectl/kubectl_create_secret_generic.md b/docs/user-guide/kubectl/kubectl_create_secret_generic.md
index f65f0897be3..2104a51a8ac 100644
--- a/docs/user-guide/kubectl/kubectl_create_secret_generic.md
+++ b/docs/user-guide/kubectl/kubectl_create_secret_generic.md
@@ -35,7 +35,7 @@ Create a secret from a local file, directory or literal value.
-Create a secret based on a file, directory, or specified literal value
+Create a secret based on a file, directory, or specified literal value.
A single secret may package one or more key/value pairs.
@@ -116,7 +116,7 @@ kubectl create secret generic NAME [--type=string] [--from-file=[key=]source] [-
* [kubectl create secret](kubectl_create_secret.md) - Create a secret using specified subcommand.
-###### Auto generated by spf13/cobra on 14-Feb-2016
+###### Auto generated by spf13/cobra on 18-Feb-2016
[]()
diff --git a/hack/test-cmd.sh b/hack/test-cmd.sh
index 1a0c527519e..565fafb3c6c 100755
--- a/hack/test-cmd.sh
+++ b/hack/test-cmd.sh
@@ -823,6 +823,34 @@ __EOF__
# Clean up
kubectl delete namespace test-secrets
+ ######################
+ # ConfigMap #
+ ######################
+
+ kubectl create -f docs/user-guide/configmap/config-map.yaml
+ kube::test::get_object_assert configmap "{{range.items}}{{$id_field}}{{end}}" 'test-configmap'
+ kubectl delete configmap test-configmap "${kube_flags[@]}"
+
+ ### Create a new namespace
+ # Pre-condition: the test-configmaps namespace does not exist
+ kube::test::get_object_assert 'namespaces' '{{range.items}}{{ if eq $id_field \"test-configmaps\" }}found{{end}}{{end}}:' ':'
+ # Command
+ kubectl create namespace test-configmaps
+ # Post-condition: namespace 'test-configmaps' is created.
+ kube::test::get_object_assert 'namespaces/test-configmaps' "{{$id_field}}" 'test-configmaps'
+
+ ### Create a generic configmap in a specific namespace
+ # Pre-condition: no configmaps namespace exists
+ kube::test::get_object_assert 'configmaps --namespace=test-configmaps' "{{range.items}}{{$id_field}}:{{end}}" ''
+ # Command
+ kubectl create configmap test-configmap --from-literal=key1=value1 --namespace=test-configmaps
+ # Post-condition: configmap exists and has expected values
+ kube::test::get_object_assert 'configmap/test-configmap --namespace=test-configmaps' "{{$id_field}}" 'test-configmap'
+ [[ "$(kubectl get configmap/test-configmap --namespace=test-configmaps -o yaml "${kube_flags[@]}" | grep 'key1: value1')" ]]
+ # Clean-up
+ kubectl delete configmap test-configmap --namespace=test-configmaps
+ kubectl delete namespace test-configmaps
+
#################
# Pod templates #
#################
@@ -1271,14 +1299,6 @@ __EOF__
kube::test::get_object_assert rs "{{range.items}}{{$id_field}}:{{end}}" ''
- ######################
- # ConfigMap #
- ######################
-
- kubectl create -f docs/user-guide/configmap/config-map.yaml
- kube::test::get_object_assert configmap "{{range.items}}{{$id_field}}{{end}}" 'test-configmap'
- kubectl delete configmap test-configmap "${kube_flags[@]}"
-
######################
# Multiple Resources #
######################
diff --git a/pkg/kubectl/cmd/create.go b/pkg/kubectl/cmd/create.go
index 6c4061ea04f..612ebd19194 100644
--- a/pkg/kubectl/cmd/create.go
+++ b/pkg/kubectl/cmd/create.go
@@ -78,6 +78,7 @@ func NewCmdCreate(f *cmdutil.Factory, out io.Writer) *cobra.Command {
// create subcommands
cmd.AddCommand(NewCmdCreateNamespace(f, out))
cmd.AddCommand(NewCmdCreateSecret(f, out))
+ cmd.AddCommand(NewCmdCreateConfigMap(f, out))
return cmd
}
diff --git a/pkg/kubectl/cmd/create_configmap.go b/pkg/kubectl/cmd/create_configmap.go
new file mode 100644
index 00000000000..99a7dcc2c7c
--- /dev/null
+++ b/pkg/kubectl/cmd/create_configmap.go
@@ -0,0 +1,96 @@
+/*
+Copyright 2016 The Kubernetes Authors 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 cmd
+
+import (
+ "fmt"
+ "io"
+
+ "github.com/spf13/cobra"
+
+ "k8s.io/kubernetes/pkg/kubectl"
+ cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
+)
+
+const (
+ configMapLong = `Create a configmap based on a file, directory, or specified literal value.
+
+A single configmap may package one or more key/value pairs.
+
+When creating a configmap based on a file, the key will default to the basename of the file, and the value will
+default to the file content. If the basename is an invalid key, you may specify an alternate key.
+
+When creating a configmap based on a directory, each file whose basename is a valid key in the directory will be
+packaged into the configmap. Any directory entries except regular files are ignored (e.g. subdirectories,
+symlinks, devices, pipes, etc).
+`
+
+ configMapExample = ` # Create a new configmap named my-config with keys for each file in folder bar
+ $ kubectl create configmap generic my-config --from-file=path/to/bar
+
+ # Create a new configmap named my-config with specified keys instead of names on disk
+ $ kubectl create configmap generic my-config --from-file=ssh-privatekey=~/.ssh/id_rsa --from-file=ssh-publickey=~/.ssh/id_rsa.pub
+
+ # Create a new configMap named my-config with key1=config1 and key2=config2
+ $ kubectl create configmap generic my-config --from-literal=key1=config1 --from-literal=key2=config2`
+)
+
+// ConfigMap is a command to ease creating ConfigMaps.
+func NewCmdCreateConfigMap(f *cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
+ cmd := &cobra.Command{
+ Use: "configmap NAME [--type=string] [--from-file=[key=]source] [--from-literal=key1=value1] [--dry-run]",
+ Short: "Create a configMap from a local file, directory or literal value.",
+ Long: configMapLong,
+ Example: configMapExample,
+ Run: func(cmd *cobra.Command, args []string) {
+ err := CreateConfigMap(f, cmdOut, cmd, args)
+ cmdutil.CheckErr(err)
+ },
+ }
+ cmdutil.AddApplyAnnotationFlags(cmd)
+ cmdutil.AddValidateFlags(cmd)
+ cmdutil.AddPrinterFlags(cmd)
+ cmdutil.AddGeneratorFlags(cmd, cmdutil.ConfigMapV1GeneratorName)
+ cmd.Flags().StringSlice("from-file", []string{}, "Key files can be specified using their file path, in which case a default name will be given to them, or optionally with a name and file path, in which case the given name will be used. Specifying a directory will iterate each named file in the directory that is a valid configmap key.")
+ cmd.Flags().StringSlice("from-literal", []string{}, "Specify a key and literal value to insert in configmap (i.e. mykey=somevalue)")
+ return cmd
+}
+
+// CreateConfigMap is the implementation of the create configmap generic command.
+func CreateConfigMap(f *cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, args []string) error {
+ name, err := NameFromCommandArgs(cmd, args)
+ if err != nil {
+ return err
+ }
+ var generator kubectl.StructuredGenerator
+ switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName {
+ case cmdutil.ConfigMapV1GeneratorName:
+ generator = &kubectl.ConfigMapGeneratorV1{
+ Name: name,
+ FileSources: cmdutil.GetFlagStringSlice(cmd, "from-file"),
+ LiteralSources: cmdutil.GetFlagStringSlice(cmd, "from-literal"),
+ }
+ default:
+ return cmdutil.UsageError(cmd, fmt.Sprintf("Generator: %s not supported.", generatorName))
+ }
+ return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{
+ Name: name,
+ StructuredGenerator: generator,
+ DryRun: cmdutil.GetFlagBool(cmd, "dry-run"),
+ OutputFormat: cmdutil.GetFlagString(cmd, "output"),
+ })
+}
diff --git a/pkg/kubectl/cmd/create_configmap_test.go b/pkg/kubectl/cmd/create_configmap_test.go
new file mode 100644
index 00000000000..9c3971e36ac
--- /dev/null
+++ b/pkg/kubectl/cmd/create_configmap_test.go
@@ -0,0 +1,54 @@
+/*
+Copyright 2016 The Kubernetes Authors 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 cmd
+
+import (
+ "bytes"
+ "net/http"
+ "testing"
+
+ "k8s.io/kubernetes/pkg/api"
+ "k8s.io/kubernetes/pkg/client/unversioned/fake"
+)
+
+func TestCreateConfigMap(t *testing.T) {
+ configMap := &api.ConfigMap{}
+ configMap.Name = "my-configmap"
+ f, tf, codec := NewAPIFactory()
+ tf.Printer = &testPrinter{}
+ tf.Client = &fake.RESTClient{
+ Codec: codec,
+ Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
+ switch p, m := req.URL.Path, req.Method; {
+ case p == "/namespaces/test/configmaps" && m == "POST":
+ return &http.Response{StatusCode: 201, Body: objBody(codec, configMap)}, nil
+ default:
+ t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
+ return nil, nil
+ }
+ }),
+ }
+ tf.Namespace = "test"
+ buf := bytes.NewBuffer([]byte{})
+ cmd := NewCmdCreateConfigMap(f, buf)
+ cmd.Flags().Set("output", "name")
+ cmd.Run(cmd, []string{configMap.Name})
+ expectedOutput := "configmap/" + configMap.Name + "\n"
+ if buf.String() != expectedOutput {
+ t.Errorf("expected output: %s, but got: %s", buf.String(), expectedOutput)
+ }
+}
diff --git a/pkg/kubectl/cmd/create_secret.go b/pkg/kubectl/cmd/create_secret.go
index 0a4c66b18a0..6cba3ba07b6 100644
--- a/pkg/kubectl/cmd/create_secret.go
+++ b/pkg/kubectl/cmd/create_secret.go
@@ -43,7 +43,7 @@ func NewCmdCreateSecret(f *cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
const (
secretLong = `
-Create a secret based on a file, directory, or specified literal value
+Create a secret based on a file, directory, or specified literal value.
A single secret may package one or more key/value pairs.
@@ -87,7 +87,7 @@ func NewCmdCreateSecretGeneric(f *cmdutil.Factory, cmdOut io.Writer) *cobra.Comm
return cmd
}
-// CreateSecretGeneric is the implementation the create secret generic command
+// CreateSecretGeneric is the implementation of the create secret generic command
func CreateSecretGeneric(f *cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, args []string) error {
name, err := NameFromCommandArgs(cmd, args)
if err != nil {
diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go
index 9fbd31a4a3f..ca6413b75d8 100644
--- a/pkg/kubectl/cmd/util/factory.go
+++ b/pkg/kubectl/cmd/util/factory.go
@@ -142,6 +142,7 @@ const (
NamespaceV1GeneratorName = "namespace/v1"
SecretV1GeneratorName = "secret/v1"
SecretForDockerRegistryV1GeneratorName = "secret-for-docker-registry/v1"
+ ConfigMapV1GeneratorName = "configmap/v1"
)
// DefaultGenerators returns the set of default generators for use in Factory instances
diff --git a/pkg/kubectl/configmap.go b/pkg/kubectl/configmap.go
new file mode 100644
index 00000000000..04ed4aa6a45
--- /dev/null
+++ b/pkg/kubectl/configmap.go
@@ -0,0 +1,212 @@
+/*
+Copyright 2016 The Kubernetes Authors 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 kubectl
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path"
+ "strings"
+
+ "k8s.io/kubernetes/pkg/api"
+ "k8s.io/kubernetes/pkg/api/validation"
+ "k8s.io/kubernetes/pkg/runtime"
+)
+
+// ConfigMapGeneratorV1 supports stable generation of a configMap.
+type ConfigMapGeneratorV1 struct {
+ // Name of configMap (required)
+ Name string
+ // Type of configMap (optional)
+ Type string
+ // FileSources to derive the configMap from (optional)
+ FileSources []string
+ // LiteralSources to derive the configMap from (optional)
+ LiteralSources []string
+}
+
+// Ensure it supports the generator pattern that uses parameter injection.
+var _ Generator = &ConfigMapGeneratorV1{}
+
+// Ensure it supports the generator pattern that uses parameters specified during construction.
+var _ StructuredGenerator = &ConfigMapGeneratorV1{}
+
+// Generate returns a configMap using the specified parameters.
+func (s ConfigMapGeneratorV1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
+ err := ValidateParams(s.ParamNames(), genericParams)
+ if err != nil {
+ return nil, err
+ }
+ delegate := &ConfigMapGeneratorV1{}
+ fromFileStrings, found := genericParams["from-file"]
+ if found {
+ fromFileArray, isArray := fromFileStrings.([]string)
+ if !isArray {
+ return nil, fmt.Errorf("expected []string, found :%v", fromFileStrings)
+ }
+ delegate.FileSources = fromFileArray
+ delete(genericParams, "from-file")
+ }
+ fromLiteralStrings, found := genericParams["from-literal"]
+ if found {
+ fromLiteralArray, isArray := fromLiteralStrings.([]string)
+ if !isArray {
+ return nil, fmt.Errorf("expected []string, found :%v", fromFileStrings)
+ }
+ delegate.LiteralSources = fromLiteralArray
+ delete(genericParams, "from-literal")
+ }
+ params := map[string]string{}
+ for key, value := range genericParams {
+ strVal, isString := value.(string)
+ if !isString {
+ return nil, fmt.Errorf("expected string, saw %v for '%s'", value, key)
+ }
+ params[key] = strVal
+ }
+ delegate.Name = params["name"]
+ delegate.Type = params["type"]
+ return delegate.StructuredGenerate()
+}
+
+// ParamNames returns the set of supported input parameters when using the parameter injection generator pattern.
+func (s ConfigMapGeneratorV1) ParamNames() []GeneratorParam {
+ return []GeneratorParam{
+ {"name", true},
+ {"type", false},
+ {"from-file", false},
+ {"from-literal", false},
+ {"force", false},
+ }
+}
+
+// StructuredGenerate outputs a configMap object using the configured fields.
+func (s ConfigMapGeneratorV1) StructuredGenerate() (runtime.Object, error) {
+ if err := s.validate(); err != nil {
+ return nil, err
+ }
+ configMap := &api.ConfigMap{}
+ configMap.Name = s.Name
+ configMap.Data = map[string]string{}
+ if len(s.FileSources) > 0 {
+ if err := handleConfigMapFromFileSources(configMap, s.FileSources); err != nil {
+ return nil, err
+ }
+ }
+ if len(s.LiteralSources) > 0 {
+ if err := handleConfigMapFromLiteralSources(configMap, s.LiteralSources); err != nil {
+ return nil, err
+ }
+ }
+ return configMap, nil
+}
+
+// validate validates required fields are set to support structured generation.
+func (s ConfigMapGeneratorV1) validate() error {
+ if len(s.Name) == 0 {
+ return fmt.Errorf("name must be specified")
+ }
+ return nil
+}
+
+// handleConfigMapFromLiteralSources adds the specified literal source
+// information into the provided configMap.
+func handleConfigMapFromLiteralSources(configMap *api.ConfigMap, literalSources []string) error {
+ for _, literalSource := range literalSources {
+ keyName, value, err := parseLiteralSource(literalSource)
+ if err != nil {
+ return err
+ }
+ err = addKeyFromLiteralToConfigMap(configMap, keyName, value)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// handleConfigMapFromFileSources adds the specified file source information
+// into the provided configMap
+func handleConfigMapFromFileSources(configMap *api.ConfigMap, fileSources []string) error {
+ for _, fileSource := range fileSources {
+ keyName, filePath, err := parseFileSource(fileSource)
+ if err != nil {
+ return err
+ }
+ info, err := os.Stat(filePath)
+ if err != nil {
+ switch err := err.(type) {
+ case *os.PathError:
+ return fmt.Errorf("error reading %s: %v", filePath, err.Err)
+ default:
+ return fmt.Errorf("error reading %s: %v", filePath, err)
+ }
+ }
+ if info.IsDir() {
+ if strings.Contains(fileSource, "=") {
+ return fmt.Errorf("cannot give a key name for a directory path.")
+ }
+ fileList, err := ioutil.ReadDir(filePath)
+ if err != nil {
+ return fmt.Errorf("error listing files in %s: %v", filePath, err)
+ }
+ for _, item := range fileList {
+ itemPath := path.Join(filePath, item.Name())
+ if item.Mode().IsRegular() {
+ keyName = item.Name()
+ err = addKeyFromFileToConfigMap(configMap, keyName, itemPath)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ } else {
+ err = addKeyFromFileToConfigMap(configMap, keyName, filePath)
+ if err != nil {
+ return err
+ }
+ }
+ }
+
+ return nil
+}
+
+// addKeyFromFileToConfigMap adds a key with the given name to a ConfigMap, populating
+// the value with the content of the given file path, or returns an error.
+func addKeyFromFileToConfigMap(configMap *api.ConfigMap, keyName, filePath string) error {
+ data, err := ioutil.ReadFile(filePath)
+ if err != nil {
+ return err
+ }
+ return addKeyFromLiteralToConfigMap(configMap, keyName, string(data))
+}
+
+// addKeyFromLiteralToConfigMap adds the given key and data to the given config map,
+// returning an error if the key is not valid or if the key already exists.
+func addKeyFromLiteralToConfigMap(configMap *api.ConfigMap, keyName, data string) error {
+ // Note, the rules for ConfigMap keys are the exact same as the ones for SecretKeys
+ // to be consistent; validation.IsSecretKey is used here intentionally.
+ if !validation.IsSecretKey(keyName) {
+ return fmt.Errorf("%v is not a valid key name for a configMap", keyName)
+ }
+ if _, entryExists := configMap.Data[keyName]; entryExists {
+ return fmt.Errorf("cannot add key %s, another key by that name already exists: %v.", keyName, configMap.Data)
+ }
+ configMap.Data[keyName] = data
+ return nil
+}
diff --git a/pkg/kubectl/configmap_test.go b/pkg/kubectl/configmap_test.go
new file mode 100644
index 00000000000..2fc936c9ec3
--- /dev/null
+++ b/pkg/kubectl/configmap_test.go
@@ -0,0 +1,108 @@
+/*
+Copyright 2016 The Kubernetes Authors 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 kubectl
+
+import (
+ "reflect"
+ "testing"
+
+ "k8s.io/kubernetes/pkg/api"
+)
+
+func TestConfigMapGenerate(t *testing.T) {
+ tests := []struct {
+ params map[string]interface{}
+ expected *api.ConfigMap
+ expectErr bool
+ }{
+ {
+ params: map[string]interface{}{
+ "name": "foo",
+ },
+ expected: &api.ConfigMap{
+ ObjectMeta: api.ObjectMeta{
+ Name: "foo",
+ },
+ Data: map[string]string{},
+ },
+ expectErr: false,
+ },
+ {
+ params: map[string]interface{}{
+ "name": "foo",
+ "type": "my-type",
+ },
+ expected: &api.ConfigMap{
+ ObjectMeta: api.ObjectMeta{
+ Name: "foo",
+ },
+ Data: map[string]string{},
+ },
+ expectErr: false,
+ },
+ {
+ params: map[string]interface{}{
+ "name": "foo",
+ "from-literal": []string{"key1=value1", "key2=value2"},
+ },
+ expected: &api.ConfigMap{
+ ObjectMeta: api.ObjectMeta{
+ Name: "foo",
+ },
+ Data: map[string]string{
+ "key1": "value1",
+ "key2": "value2",
+ },
+ },
+ expectErr: false,
+ },
+ {
+ params: map[string]interface{}{
+ "name": "foo",
+ "from-literal": []string{"key1value1"},
+ },
+ expectErr: true,
+ },
+ {
+ params: map[string]interface{}{
+ "name": "foo",
+ "from-file": []string{"key1=/file=2"},
+ },
+ expectErr: true,
+ },
+ {
+ params: map[string]interface{}{
+ "name": "foo",
+ "from-file": []string{"key1==value"},
+ },
+ expectErr: true,
+ },
+ }
+ generator := ConfigMapGeneratorV1{}
+ for _, test := range tests {
+ obj, err := generator.Generate(test.params)
+ if !test.expectErr && err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+ if test.expectErr && err != nil {
+ continue
+ }
+ if !reflect.DeepEqual(obj.(*api.ConfigMap), test.expected) {
+ t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", test.expected, obj.(*api.ConfigMap))
+ }
+ }
+}
diff --git a/pkg/kubectl/kubectl.go b/pkg/kubectl/kubectl.go
index 2f9c7c4e87f..60b280174a6 100644
--- a/pkg/kubectl/kubectl.go
+++ b/pkg/kubectl/kubectl.go
@@ -18,6 +18,9 @@ limitations under the License.
package kubectl
import (
+ "errors"
+ "fmt"
+ "path"
"strings"
"k8s.io/kubernetes/pkg/api"
@@ -120,3 +123,36 @@ func expandResourceShortcut(resource unversioned.GroupVersionResource) unversion
}
return resource
}
+
+// parseFileSource parses the source given. Acceptable formats include:
+//
+// 1. source-path: the basename will become the key name
+// 2. source-name=source-path: the source-name will become the key name and source-path is the path to the key file
+//
+// Key names cannot include '='.
+func parseFileSource(source string) (keyName, filePath string, err error) {
+ numSeparators := strings.Count(source, "=")
+ switch {
+ case numSeparators == 0:
+ return path.Base(source), source, nil
+ case numSeparators == 1 && strings.HasPrefix(source, "="):
+ return "", "", fmt.Errorf("key name for file path %v missing.", strings.TrimPrefix(source, "="))
+ case numSeparators == 1 && strings.HasSuffix(source, "="):
+ return "", "", fmt.Errorf("file path for key name %v missing.", strings.TrimSuffix(source, "="))
+ case numSeparators > 1:
+ return "", "", errors.New("Key names or file paths cannot contain '='.")
+ default:
+ components := strings.Split(source, "=")
+ return components[0], components[1], nil
+ }
+}
+
+// parseLiteralSource parses the source key=val pair
+func parseLiteralSource(source string) (keyName, value string, err error) {
+ items := strings.Split(source, "=")
+ if len(items) != 2 {
+ return "", "", fmt.Errorf("invalid literal source %v, expected key=value", source)
+ }
+
+ return items[0], items[1], nil
+}
diff --git a/pkg/kubectl/kubectl_test.go b/pkg/kubectl/kubectl_test.go
new file mode 100644
index 00000000000..6381b492067
--- /dev/null
+++ b/pkg/kubectl/kubectl_test.go
@@ -0,0 +1,194 @@
+/*
+Copyright 2016 The Kubernetes Authors 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 kubectl
+
+import (
+ "testing"
+)
+
+func TestParseFileSource(t *testing.T) {
+ cases := []struct {
+ name string
+ input string
+ key string
+ filepath string
+ err bool
+ }{
+ {
+ name: "success 1",
+ input: "boo=zoo",
+ key: "boo",
+ filepath: "zoo",
+ err: false,
+ },
+ {
+ name: "success 2",
+ input: "boo=/path/to/zoo",
+ key: "boo",
+ filepath: "/path/to/zoo",
+ err: false,
+ },
+ {
+ name: "success 3",
+ input: "boo-2=/1/2/3/4/5/zab.txt",
+ key: "boo-2",
+ filepath: "/1/2/3/4/5/zab.txt",
+ err: false,
+ },
+ {
+ name: "success 4",
+ input: "boo-=this/seems/weird.txt",
+ key: "boo-",
+ filepath: "this/seems/weird.txt",
+ err: false,
+ },
+ {
+ name: "success 5",
+ input: "-key=some/path",
+ key: "-key",
+ filepath: "some/path",
+ err: false,
+ },
+ {
+ name: "invalid 1",
+ input: "key==some/path",
+ err: true,
+ },
+ {
+ name: "invalid 2",
+ input: "=key=some/path",
+ err: true,
+ },
+ {
+ name: "invalid 3",
+ input: "==key=/some/other/path",
+ err: true,
+ },
+ {
+ name: "invalid 4",
+ input: "=key",
+ err: true,
+ },
+ {
+ name: "invalid 5",
+ input: "key=",
+ err: true,
+ },
+ }
+
+ for _, tc := range cases {
+ key, filepath, err := parseFileSource(tc.input)
+ if err != nil {
+ if tc.err {
+ continue
+ }
+
+ t.Errorf("%v: unexpected error: %v", tc.name, err)
+ continue
+ }
+
+ if tc.err {
+ t.Errorf("%v: unexpected success", tc.name)
+ continue
+ }
+
+ if e, a := tc.key, key; e != a {
+ t.Errorf("%v: expected key %v; got %v", tc.name, e, a)
+ continue
+ }
+
+ if e, a := tc.filepath, filepath; e != a {
+ t.Errorf("%v: expected filepath %v; got %v", tc.name, e, a)
+ }
+ }
+}
+
+func TestParseLiteralSource(t *testing.T) {
+ cases := []struct {
+ name string
+ input string
+ key string
+ value string
+ err bool
+ }{
+ {
+ name: "success 1",
+ input: "key=value",
+ key: "key",
+ value: "value",
+ err: false,
+ },
+ {
+ name: "success 2",
+ input: "key=value/with/slashes",
+ key: "key",
+ value: "value/with/slashes",
+ err: false,
+ },
+ {
+ name: "err 1",
+ input: "key==value",
+ err: true,
+ },
+ {
+ name: "err 2",
+ input: "key=value=",
+ err: true,
+ },
+ {
+ name: "err 3",
+ input: "key2=value==",
+ err: true,
+ },
+ {
+ name: "err 4",
+ input: "==key",
+ err: true,
+ },
+ {
+ name: "err 5",
+ input: "=key=",
+ err: true,
+ },
+ }
+
+ for _, tc := range cases {
+ key, value, err := parseLiteralSource(tc.input)
+ if err != nil {
+ if tc.err {
+ continue
+ }
+
+ t.Errorf("%v: unexpected error: %v", tc.name, err)
+ continue
+ }
+
+ if tc.err {
+ t.Errorf("%v: unexpected success", tc.name)
+ continue
+ }
+
+ if e, a := tc.key, key; e != a {
+ t.Errorf("%v: expected key %v; got %v", tc.name, e, a)
+ continue
+ }
+
+ if e, a := tc.value, value; e != a {
+ t.Errorf("%v: expected value %v; got %v", tc.name, e, a)
+ }
+ }
+}
diff --git a/pkg/kubectl/secret.go b/pkg/kubectl/secret.go
index 62de7900874..e5b7cc33e78 100644
--- a/pkg/kubectl/secret.go
+++ b/pkg/kubectl/secret.go
@@ -17,7 +17,6 @@ limitations under the License.
package kubectl
import (
- "errors"
"fmt"
"io/ioutil"
"os"
@@ -206,33 +205,3 @@ func addKeyFromLiteralToSecret(secret *api.Secret, keyName string, data []byte)
secret.Data[keyName] = data
return nil
}
-
-// parseFileSource parses the source given. Acceptable formats include:
-// source-name=source-path, where source-name will become the key name and source-path is the path to the key file
-// source-path, where source-path is a path to a file or directory, and key names will default to file names
-// Key names cannot include '='.
-func parseFileSource(source string) (keyName, filePath string, err error) {
- numSeparators := strings.Count(source, "=")
- switch {
- case numSeparators == 0:
- return path.Base(source), source, nil
- case numSeparators == 1 && strings.HasPrefix(source, "="):
- return "", "", fmt.Errorf("key name for file path %v missing.", strings.TrimPrefix(source, "="))
- case numSeparators == 1 && strings.HasSuffix(source, "="):
- return "", "", fmt.Errorf("file path for key name %v missing.", strings.TrimSuffix(source, "="))
- case numSeparators > 1:
- return "", "", errors.New("Key names or file paths cannot contain '='.")
- default:
- components := strings.Split(source, "=")
- return components[0], components[1], nil
- }
-}
-
-// parseLiteralSource parses the source key=val pair
-func parseLiteralSource(source string) (keyName, value string, err error) {
- items := strings.Split(source, "=")
- if len(items) != 2 {
- return "", "", fmt.Errorf("invalid literal source %v, expected key=value", source)
- }
- return items[0], items[1], nil
-}