Merge pull request #189 from vaikas-google/master

Add support for k8s secrets backed credentials
pull/192/head
Brendan Melville 9 years ago
commit b2ebb24c08

@ -167,6 +167,14 @@ type KubernetesObject struct {
Spec map[string]interface{} `json:"spec"` Spec map[string]interface{} `json:"spec"`
} }
// KubernetesSecret represents a Kubernetes secret
type KubernetesSecret struct {
Kind string `json:"kind"`
ApiVersion string `json:"apiVersion"`
Metadata map[string]string `json:"metadata"`
Data map[string]string `json:"data,omitempty"`
}
// Repository related types // Repository related types
type BasicAuthCredential struct { type BasicAuthCredential struct {
Username string `json:"username"` Username string `json:"username"`
@ -186,7 +194,7 @@ type RegistryCredential struct {
type Registry struct { type Registry struct {
Name string `json:"name,omitempty"` // Friendly name for the registry Name string `json:"name,omitempty"` // Friendly name for the registry
Type RegistryType `json:"type,omitempty"` // Technology implementing the registry Type RegistryType `json:"type,omitempty"` // Technology implementing the registry
URL string `json:"name,omitempty"` // URL to the root of the registry URL string `json:"url,omitempty"` // URL to the root of the registry
Format RegistryFormat `json:"format,omitempty"` // Format of the registry Format RegistryFormat `json:"format,omitempty"` // Format of the registry
CredentialName string `json:"credentialname,omitempty"` // Name of the credential to use CredentialName string `json:"credentialname,omitempty"` // Name of the credential to use
} }

@ -21,8 +21,6 @@ import (
"github.com/kubernetes/deployment-manager/common" "github.com/kubernetes/deployment-manager/common"
"github.com/kubernetes/deployment-manager/expandybird/expander" "github.com/kubernetes/deployment-manager/expandybird/expander"
"github.com/kubernetes/deployment-manager/registry"
"github.com/kubernetes/deployment-manager/util"
"archive/tar" "archive/tar"
"bytes" "bytes"
@ -35,8 +33,6 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
"path"
"regexp"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -46,10 +42,10 @@ var (
deployment_name = flag.String("name", "", "Name of deployment, used for deploy and update commands (defaults to template name)") deployment_name = flag.String("name", "", "Name of deployment, used for deploy and update commands (defaults to template name)")
stdin = flag.Bool("stdin", false, "Reads a configuration from the standard input") stdin = flag.Bool("stdin", false, "Reads a configuration from the standard input")
properties = flag.String("properties", "", "Properties to use when deploying a template (e.g., --properties k1=v1,k2=v2)") properties = flag.String("properties", "", "Properties to use when deploying a template (e.g., --properties k1=v1,k2=v2)")
template_registry = flag.String("registry", "github.com/kubernetes/application-dm-templates", "Registry (github.com/owner/repo)") template_registry = flag.String("registry", "application-dm-templates", "Registry name")
service = flag.String("service", "http://localhost:8001/api/v1/proxy/namespaces/dm/services/manager-service:manager", "URL for deployment manager") service = flag.String("service", "http://localhost:8001/api/v1/proxy/namespaces/dm/services/manager-service:manager", "URL for deployment manager")
binary = flag.String("binary", "../expandybird/expansion/expansion.py", "Path to template expansion binary") binary = flag.String("binary", "../expandybird/expansion/expansion.py", "Path to template expansion binary")
timeout = flag.Int("timeout", 10, "Time in seconds to wait for response") timeout = flag.Int("timeout", 20, "Time in seconds to wait for response")
regex_string = flag.String("regex", "", "Regular expression to filter the templates listed in a template registry") regex_string = flag.String("regex", "", "Regular expression to filter the templates listed in a template registry")
username = flag.String("username", "", "Github user name that overrides GITHUB_USERNAME environment variable") username = flag.String("username", "", "Github user name that overrides GITHUB_USERNAME environment variable")
password = flag.String("password", "", "Github password that overrides GITHUB_PASSWORD environment variable") password = flag.String("password", "", "Github password that overrides GITHUB_PASSWORD environment variable")
@ -67,6 +63,7 @@ var commands = []string{
"deployed-types \t\t Lists the types deployed in the cluster", "deployed-types \t\t Lists the types deployed in the cluster",
"deployed-instances \t Lists the instances of the named type deployed in the cluster", "deployed-instances \t Lists the instances of the named type deployed in the cluster",
"templates \t\t Lists the templates in a given template registry", "templates \t\t Lists the templates in a given template registry",
"registries \t\t Lists the registries available",
"describe \t\t Describes the named template in a given template registry", "describe \t\t Describes the named template in a given template registry",
} }
@ -87,45 +84,6 @@ var usage = func() {
panic("\n") panic("\n")
} }
// TODO(jackgr): Move all registry related operations to the server side.
var registryProvider registry.RegistryProvider
func getRegistryProvider() registry.RegistryProvider {
if registryProvider == nil {
rs := registry.NewInmemRegistryService()
_, err := rs.GetByURL(*template_registry)
if err != nil {
r := newRegistry(*template_registry)
if err := rs.Create(r); err != nil {
panic(fmt.Errorf("cannot configure registry at %s: %s", r.URL, err))
}
}
cp := registry.NewInmemCredentialProvider()
credential := getGithubCredential()
if credential != nil {
if err := cp.SetCredential("default", credential); err != nil {
panic(fmt.Errorf("cannot set credential at %s: %s", "default", err))
}
}
registryProvider = registry.NewRegistryProvider(rs, nil, cp)
}
return registryProvider
}
func newRegistry(URL string) *common.Registry {
tFormat := fmt.Sprintf("%s;%s", common.VersionedRegistry, common.CollectionRegistry)
return &common.Registry{
Name: util.TrimURLScheme(URL),
Type: common.GithubRegistryType,
URL: URL,
Format: common.RegistryFormat(tFormat),
CredentialName: "default",
}
}
func getGithubCredential() *common.RegistryCredential { func getGithubCredential() *common.RegistryCredential {
*apitoken = strings.TrimSpace(*apitoken) *apitoken = strings.TrimSpace(*apitoken)
if *apitoken == "" { if *apitoken == "" {
@ -160,16 +118,6 @@ func getGithubCredential() *common.RegistryCredential {
return nil return nil
} }
func getGithubRegistry() registry.Registry {
provider := getRegistryProvider()
git, err := provider.GetRegistryByShortURL(*template_registry)
if err != nil {
panic(fmt.Errorf("cannot open registry %s: %s", *template_registry, err))
}
return git
}
func main() { func main() {
defer func() { defer func() {
result := recover() result := recover()
@ -192,32 +140,8 @@ func execute() {
switch args[0] { switch args[0] {
case "templates": case "templates":
var regex *regexp.Regexp path := fmt.Sprintf("registries/%s/types", args[1])
if *regex_string != "" { callService(path, "GET", "list templates", nil)
var err error
regex, err = regexp.Compile(*regex_string)
if err != nil {
panic(fmt.Errorf("cannot compile regular expression %s: %s", *regex_string, err))
}
}
git := getGithubRegistry()
types, err := git.ListTypes(regex)
if err != nil {
panic(fmt.Errorf("cannot list templates in registry %s: %s", *template_registry, err))
}
for _, t := range types {
fmt.Printf("%s\n", t.String())
urls, err := git.GetDownloadURLs(t)
if err != nil {
panic(fmt.Errorf("cannot get download urls for %s: %s", t, err))
}
for _, downloadURL := range urls {
fmt.Printf("\t%s\n", downloadURL)
}
}
case "describe": case "describe":
describeType(args) describeType(args)
case "expand": case "expand":
@ -293,6 +217,8 @@ func execute() {
path := fmt.Sprintf("types/%s/instances", url.QueryEscape(tUrl)) path := fmt.Sprintf("types/%s/instances", url.QueryEscape(tUrl))
action := fmt.Sprintf("list deployed instances of type %s", tUrl) action := fmt.Sprintf("list deployed instances of type %s", tUrl)
callService(path, "GET", action, nil) callService(path, "GET", action, nil)
case "registries":
callService("registries", "GET", "list registries", nil)
default: default:
usage() usage()
} }
@ -361,16 +287,15 @@ func describeType(args []string) {
fmt.Println(callHttp(schemaUrl, "GET", "get schema for type ("+tUrls[0]+")", nil)) fmt.Println(callHttp(schemaUrl, "GET", "get schema for type ("+tUrls[0]+")", nil))
} }
// getDownloadURLs returns URLs or empty list if a primitive type. // getDownloadURLs returns URLs for a type in the given registry
func getDownloadURLs(tName string) []string { func getDownloadURLs(tName string) []string {
qName := path.Join(*template_registry, tName) path := fmt.Sprintf("%s/registries/%s/types/%s", *service, *template_registry, url.QueryEscape(tName))
provider := getRegistryProvider() resp := callHttp(path, "GET", "get download urls", nil)
result, err := registry.GetDownloadURLs(provider, qName) u := []string{}
if err != nil { if err := json.Unmarshal([]byte(resp), &u); err != nil {
panic(fmt.Errorf("cannot get URLs for %s: %s\n", tName, err)) panic(fmt.Errorf("Failed to parse JSON response from service: %s", resp))
} }
return u
return result
} }
func loadTemplate(args []string) *common.Template { func loadTemplate(args []string) *common.Template {
@ -408,13 +333,13 @@ func loadTemplate(args []string) *common.Template {
// See if the first argument is a local file. It could either be a type, or it could be a configuration. If // See if the first argument is a local file. It could either be a type, or it could be a configuration. If
// it's a local file, it's configuration. // it's a local file, it's configuration.
if _, err := os.Stat(args[1]); err == nil { if _, err := os.Stat(args[1]); err == nil {
if len(args) > 2 {
template, err = expander.NewTemplateFromFileNames(args[1], args[2:]) template, err = expander.NewTemplateFromFileNames(args[1], args[2:])
} else {
if t, err := registry.ParseType(args[1]); err == nil {
template = buildTemplateFromType(t)
} else { } else {
template, err = expander.NewTemplateFromRootTemplate(args[1]) template, err = expander.NewTemplateFromRootTemplate(args[1])
} }
} else {
template = buildTemplateFromType(args[1])
} }
if err != nil { if err != nil {
@ -430,7 +355,7 @@ func loadTemplate(args []string) *common.Template {
return template return template
} }
func buildTemplateFromType(t registry.Type) *common.Template { func buildTemplateFromType(t string) *common.Template {
props := make(map[string]interface{}) props := make(map[string]interface{})
if *properties != "" { if *properties != "" {
plist := strings.Split(*properties, ",") plist := strings.Split(*properties, ",")
@ -452,11 +377,11 @@ func buildTemplateFromType(t registry.Type) *common.Template {
} }
// Name the deployment after the type name. // Name the deployment after the type name.
name := fmt.Sprintf("%s:%s", t.Name, t.GetVersion()) name := t
config := common.Configuration{Resources: []*common.Resource{&common.Resource{ config := common.Configuration{Resources: []*common.Resource{&common.Resource{
Name: name, Name: name,
Type: getDownloadURLs(t.String())[0], Type: getDownloadURLs(t)[0],
Properties: props, Properties: props,
}}} }}}

@ -38,7 +38,7 @@ if [[ -z $MANAGER ]] ; then
exit 1 exit 1
fi fi
pkill -f $MANAGER pkill -f $MANAGER
nohup $MANAGER > $LOGDIR/manager.log 2>&1 --port=8080 --expanderURL=http://localhost:8081 --deployerURL=http://localhost:8082 & nohup $MANAGER > $LOGDIR/manager.log 2>&1 --port=8080 --kubectl=$KUBECTL --expanderURL=http://localhost:8081 --deployerURL=http://localhost:8082 &
echo echo
echo "Creating dm namespace..." echo "Creating dm namespace..."

@ -15,6 +15,17 @@
FROM golang:1.4 FROM golang:1.4
MAINTAINER Jack Greenfield <jackgr@google.com> MAINTAINER Jack Greenfield <jackgr@google.com>
RUN apt-get update \
&& apt-get autoremove -y \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
WORKDIR /usr/local/bin
ENV KUBE_VERSION v1.0.5
RUN curl -fsSL -o kubectl https://storage.googleapis.com/kubernetes-release/release/$KUBE_VERSION/bin/linux/amd64/kubectl \
&& chmod +x kubectl
RUN mkdir -p "$GOPATH/src/github.com" && chmod 777 "$GOPATH/src/github.com" RUN mkdir -p "$GOPATH/src/github.com" && chmod 777 "$GOPATH/src/github.com"
WORKDIR "$GOPATH/src/github.com" WORKDIR "$GOPATH/src/github.com"
@ -37,4 +48,4 @@ RUN go-wrapper install github.com/kubernetes/deployment-manager/manager/...
EXPOSE 8080 EXPOSE 8080
ENTRYPOINT ["bin/manager"] ENTRYPOINT ["bin/manager", "--kubectl=/usr/local/bin/kubectl"]

@ -67,6 +67,7 @@ var (
deployerName = flag.String("deployer", "resourcifier-service", "The DNS name of the deployer service.") deployerName = flag.String("deployer", "resourcifier-service", "The DNS name of the deployer service.")
deployerURL = flag.String("deployerURL", "", "The URL for the deployer service.") deployerURL = flag.String("deployerURL", "", "The URL for the deployer service.")
credentialFile = flag.String("credentialFile", "", "Local file to use for credentials.") credentialFile = flag.String("credentialFile", "", "Local file to use for credentials.")
credentialSecrets = flag.Bool("credentialSecrets", true, "Use secrets for credentials.")
) )
var backend manager.Manager var backend manager.Manager
@ -79,11 +80,16 @@ func init() {
routes = append(routes, deployments...) routes = append(routes, deployments...)
var credentialProvider common.CredentialProvider var credentialProvider common.CredentialProvider
if *credentialFile != "" { if *credentialFile != "" {
if *credentialSecrets {
panic(fmt.Errorf("Both credentialFile and credentialSecrets are set"))
}
var err error var err error
credentialProvider, err = registry.NewFilebasedCredentialProvider(*credentialFile) credentialProvider, err = registry.NewFilebasedCredentialProvider(*credentialFile)
if err != nil { if err != nil {
panic(fmt.Errorf("cannot create credential provider %s: %s", *credentialFile, err)) panic(fmt.Errorf("cannot create credential provider %s: %s", *credentialFile, err))
} }
} else if *credentialSecrets {
credentialProvider = registry.NewSecretsCredentialProvider()
} else { } else {
credentialProvider = registry.NewInmemCredentialProvider() credentialProvider = registry.NewInmemCredentialProvider()
} }
@ -424,7 +430,11 @@ func getDownloadURLsHandlerFunc(w http.ResponseWriter, r *http.Request) {
return return
} }
util.LogHandlerExitWithJSON(handler, w, c, http.StatusOK) urls := []string{}
for _, u := range c {
urls = append(urls, u.String())
}
util.LogHandlerExitWithJSON(handler, w, urls, http.StatusOK)
} }
func getCredential(w http.ResponseWriter, r *http.Request, handler string) *common.RegistryCredential { func getCredential(w http.ResponseWriter, r *http.Request, handler string) *common.RegistryCredential {

@ -43,6 +43,12 @@ var routes = []Route{}
// port to listen on // port to listen on
var port = flag.Int("port", 8080, "The port to listen on") var port = flag.Int("port", 8080, "The port to listen on")
func init() {
if !flag.Parsed() {
flag.Parse()
}
}
func main() { func main() {
if !flag.Parsed() { if !flag.Parsed() {
flag.Parse() flag.Parse()

@ -23,13 +23,7 @@ import (
"github.com/ghodss/yaml" "github.com/ghodss/yaml"
"github.com/kubernetes/deployment-manager/common" "github.com/kubernetes/deployment-manager/common"
)
/*
"net/url"
"regexp"
"strings"
"sync"
*/)
// CredentialProvider provides credentials for registries. // CredentialProvider provides credentials for registries.
type FilebasedCredentialProvider struct { type FilebasedCredentialProvider struct {

@ -124,7 +124,6 @@ func (g GithubPackageRegistry) GetDownloadURLs(t Type) ([]*url.URL, error) {
} }
} }
} }
return downloadURLs, nil return downloadURLs, nil
} }

@ -59,7 +59,7 @@ type GithubRegistry interface {
type Type struct { type Type struct {
Collection string Collection string
Name string Name string
version SemVer Version SemVer
} }
// NewType initializes a type // NewType initializes a type
@ -98,7 +98,7 @@ func (t Type) String() string {
// GetVersion returns the type version with the letter "v" prepended. // GetVersion returns the type version with the letter "v" prepended.
func (t Type) GetVersion() string { func (t Type) GetVersion() string {
var result string var result string
version := t.version.String() version := t.Version.String()
if version != "0" { if version != "0" {
result = "v" + version result = "v" + version
} }
@ -115,7 +115,7 @@ func (t *Type) SetVersion(version string) error {
return err return err
} }
t.version = s t.Version = s
return nil return nil
} }

@ -0,0 +1,127 @@
/*
Copyright 2015 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 registry
import (
"encoding/base64"
"encoding/json"
"flag"
"fmt"
"log"
"github.com/ghodss/yaml"
"github.com/kubernetes/deployment-manager/common"
"github.com/kubernetes/deployment-manager/util"
)
var (
kubePath = flag.String("kubectl", "./kubectl", "The path to the kubectl binary.")
kubeService = flag.String("service", "", "The DNS name of the kubernetes service.")
kubeServer = flag.String("server", "", "The IP address and optional port of the kubernetes master.")
kubeInsecure = flag.Bool("insecure-skip-tls-verify", false, "Do not check the server's certificate for validity.")
kubeConfig = flag.String("config", "", "Path to a kubeconfig file.")
kubeCertAuth = flag.String("certificate-authority", "", "Path to a file for the certificate authority.")
kubeClientCert = flag.String("client-certificate", "", "Path to a client certificate file.")
kubeClientKey = flag.String("client-key", "", "Path to a client key file.")
kubeToken = flag.String("token", "", "A service account token.")
kubeUsername = flag.String("username", "", "The username to use for basic auth.")
kubePassword = flag.String("password", "", "The password to use for basic auth.")
)
var kubernetesConfig *util.KubernetesConfig
const secretType = "Secret"
// CredentialProvider provides credentials for registries.
type SecretsCredentialProvider struct {
// Actual object that talks to secrets service.
k util.Kubernetes
}
func NewSecretsCredentialProvider() common.CredentialProvider {
kubernetesConfig := &util.KubernetesConfig{
KubePath: *kubePath,
KubeService: *kubeService,
KubeServer: *kubeServer,
KubeInsecure: *kubeInsecure,
KubeConfig: *kubeConfig,
KubeCertAuth: *kubeCertAuth,
KubeClientCert: *kubeClientCert,
KubeClientKey: *kubeClientKey,
KubeToken: *kubeToken,
KubeUsername: *kubeUsername,
KubePassword: *kubePassword,
}
return &SecretsCredentialProvider{util.NewKubernetesKubectl(kubernetesConfig)}
}
func parseCredential(credential string) (*common.RegistryCredential, error) {
var c common.KubernetesSecret
if err := json.Unmarshal([]byte(credential), &c); err != nil {
return nil, fmt.Errorf("cannot unmarshal credential '%s' (%#v)", credential, err)
}
d, err := base64.StdEncoding.DecodeString(c.Data["credential"])
if err != nil {
return nil, fmt.Errorf("cannot unmarshal credential '%s' (%#v)", c, err)
}
// And then finally unmarshal it from yaml to common.RegistryCredential
r := &common.RegistryCredential{}
if err := yaml.Unmarshal(d, &r); err != nil {
return nil, fmt.Errorf("cannot unmarshal credential %s (%#v)", c, err)
}
return r, nil
}
func (scp *SecretsCredentialProvider) GetCredential(name string) (*common.RegistryCredential, error) {
o, err := scp.k.Get(name, secretType)
if err != nil {
return nil, err
}
return parseCredential(o)
}
func (scp *SecretsCredentialProvider) SetCredential(name string, credential *common.RegistryCredential) error {
// Marshal the credential & base64 encode it.
b, err := yaml.Marshal(credential)
if err != nil {
log.Printf("yaml marshal failed for credential: %s: %v", name, err)
return err
}
enc := base64.StdEncoding.EncodeToString(b)
// Then create a kubernetes object out of it
metadata := make(map[string]string)
metadata["name"] = name
data := make(map[string]string)
data["credential"] = enc
obj := &common.KubernetesSecret{
Kind: secretType,
ApiVersion: "v1",
Metadata: metadata,
Data: data,
}
ko, err := yaml.Marshal(obj)
if err != nil {
log.Printf("yaml marshal failed for kubernetes object: %s: %v", name, err)
return err
}
log.Printf("Calling with: %s", string(ko))
o, err := scp.k.Create(string(ko))
log.Printf("Create returned: %s", o)
return err
}

@ -0,0 +1,41 @@
/*
Copyright 2015 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 util
// KubernetesConfiguration defines the configuration options for talking to Kubernetes master
type KubernetesConfig struct {
KubePath string // The path to kubectl binary
KubeService string // DNS name of the kubernetes service
KubeServer string // The IP address and optional port of the kubernetes master
KubeInsecure bool // Do not check the server's certificate for validity
KubeConfig string // Path to a kubeconfig file
KubeCertAuth string // Path to a file for the certificate authority
KubeClientCert string // Path to a client certificate file
KubeClientKey string // Path to a client key file
KubeToken string // A service account token
KubeUsername string // The username to use for basic auth
KubePassword string // The password to use for basic auth
}
// Kubernetes defines the interface for talking to Kubernetes. Currently the
// only implementation is through kubectl, but eventually this could be done
// via direct API calls.
type Kubernetes interface {
Get(name string, resourceType string) (string, error)
Create(resource string) (string, error)
Delete(name string, resourceType string) (string, error)
}

@ -0,0 +1,135 @@
/*
Copyright 2015 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 util
import (
"bytes"
"fmt"
"log"
"net"
"os"
"os/exec"
)
// KubernetesKubectl implements the interface for talking to Kubernetes by wrapping calls
// via kubectl.
type KubernetesKubectl struct {
KubePath string
// Base level arguments to kubectl. User commands/arguments get appended to this.
Arguments []string
}
func NewKubernetesKubectl(config *KubernetesConfig) Kubernetes {
if config.KubePath == "" {
log.Fatalf("kubectl path cannot be empty")
}
// If a configuration file is specified, then it will provide the server
// address and credentials. If not, then we check for the server address
// and credentials as individual flags.
var args []string
if config.KubeConfig != "" {
config.KubeConfig = os.ExpandEnv(config.KubeConfig)
args = append(args, fmt.Sprintf("--kubeconfig=%s", config.KubeConfig))
} else {
if config.KubeServer != "" {
args = append(args, fmt.Sprintf("--server=https://%s", config.KubeServer))
} else if config.KubeService != "" {
addrs, err := net.LookupHost(config.KubeService)
if err != nil || len(addrs) < 1 {
log.Fatalf("cannot resolve DNS name: %v", config.KubeService)
}
args = append(args, fmt.Sprintf("--server=https://%s", addrs[0]))
}
if config.KubeInsecure {
args = append(args, fmt.Sprintf("--insecure-skip-tls-verify=%s", config.KubeInsecure))
} else {
if config.KubeCertAuth != "" {
args = append(args, fmt.Sprintf("--certificate-authority=%s", config.KubeCertAuth))
if config.KubeClientCert == "" {
args = append(args, fmt.Sprintf("--client-certificate=%s", config.KubeClientCert))
}
if config.KubeClientKey == "" {
args = append(args, fmt.Sprintf("--client-key=%s", config.KubeClientKey))
}
}
}
if config.KubeToken != "" {
args = append(args, fmt.Sprintf("--token=%s", config.KubeToken))
} else {
if config.KubeUsername != "" {
args = append(args, fmt.Sprintf("--username=%s", config.KubeUsername))
}
if config.KubePassword != "" {
args = append(args, fmt.Sprintf("--password=%s", config.KubePassword))
}
}
}
return &KubernetesKubectl{config.KubePath, args}
}
func (k *KubernetesKubectl) Get(name string, resourceType string) (string, error) {
// Specify output as json rather than human readable for easier machine parsing
args := []string{"get",
"-o",
"json",
resourceType,
name}
return k.execute(args, "")
}
func (k *KubernetesKubectl) Create(resource string) (string, error) {
args := []string{"create"}
return k.execute(args, resource)
}
func (k *KubernetesKubectl) Delete(name string, resourceType string) (string, error) {
args := []string{"delete",
resourceType,
name}
return k.execute(args, "")
}
func (k *KubernetesKubectl) execute(args []string, input string) (string, error) {
if len(input) > 0 {
args = append(args, "-f", "-")
}
// Tack on the common arguments to the end of the command line
args = append(args, k.Arguments...)
cmd := exec.Command(k.KubePath, args...)
cmd.Stdin = bytes.NewBuffer([]byte(input))
// Combine stdout and stderr into a single dynamically resized buffer
combined := &bytes.Buffer{}
cmd.Stdout = combined
cmd.Stderr = combined
if err := cmd.Start(); err != nil {
log.Printf("cannot start kubectl %#v", err)
return combined.String(), err
}
if err := cmd.Wait(); err != nil {
log.Printf("kubectl failed: %#v", err)
return combined.String(), err
}
return combined.String(), nil
}
Loading…
Cancel
Save