Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvement in the kubeconfigwriter #2366

Merged
merged 1 commit into from
Apr 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 22 additions & 7 deletions cmd/kubeconfigwriter/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ import (
)

var (
clusterConfig = flag.String("clusterConfig", "", "json string with the configuration of a cluster based on values from a cluster resource. Only required for external clusters.")
clusterConfig = flag.String("clusterConfig", "", "json string with the configuration of a cluster based on values from a cluster resource. Only required for external clusters.")
destinationDir = flag.String("destinationDir", "", "destination directory where generated kubeconfig file will be stored.")
divyansh42 marked this conversation as resolved.
Show resolved Hide resolved
)

func main() {
Expand All @@ -48,10 +49,10 @@ func main() {
if err != nil {
logger.Fatalf("Error reading cluster config: %v", err)
}
createKubeconfigFile(&cr, logger)
createKubeconfigFile(&cr, logger, destinationDir)
}

func createKubeconfigFile(resource *cluster.Resource, logger *zap.SugaredLogger) {
func createKubeconfigFile(resource *cluster.Resource, logger *zap.SugaredLogger, destinationDir *string) {
cluster := &clientcmdapi.Cluster{
Server: resource.URL,
InsecureSkipTLSVerify: resource.Insecure,
Expand All @@ -72,14 +73,18 @@ func createKubeconfigFile(resource *cluster.Resource, logger *zap.SugaredLogger)
//only one authentication technique per user is allowed in a kubeconfig, so clear out the password if a token is provided
user := resource.Username
pass := resource.Password
clientKeyData := resource.ClientKeyData
clientCertificateData := resource.ClientCertificateData
if resource.Token != "" {
user = ""
pass = ""
}
auth := &clientcmdapi.AuthInfo{
Token: resource.Token,
Username: user,
Password: pass,
Token: resource.Token,
Username: user,
Password: pass,
ClientKeyData: clientKeyData,
ClientCertificateData: clientCertificateData,
}
context := &clientcmdapi.Context{
Cluster: resource.Name,
Expand All @@ -95,7 +100,17 @@ func createKubeconfigFile(resource *cluster.Resource, logger *zap.SugaredLogger)
c.APIVersion = "v1"
c.Kind = "Config"

destinationFile := fmt.Sprintf("/workspace/%s/kubeconfig", resource.Name)
// kubeconfig file location
var destinationFile string

// If the destination Directory is provided, kubeconfig will be written to the given directory.
// otherwise it will use default location i.e. "/workspace/<cluster-name>/
if *destinationDir != "" {
destinationFile = fmt.Sprintf("%s/kubeconfig", *destinationDir)
} else {
destinationFile = fmt.Sprintf("/workspace/%s/kubeconfig", resource.Name)
}

if err := clientcmd.WriteToFile(*c, destinationFile); err != nil {
logger.Fatalf("Error writing kubeconfig to file: %v", err)
}
Expand Down
7 changes: 7 additions & 0 deletions docs/resources.md
Original file line number Diff line number Diff line change
Expand Up @@ -648,11 +648,18 @@ The Cluster resource has the following parameters:
certificate.
- `cadata` (required): holds PEM-encoded bytes (typically read from a root
certificates bundle).
- `clientKeyData`: contains PEM-encoded data from a client key file
for TLS
- `clientCertificateData`: contains PEM-encoded data from a client cert file for TLS


Note: Since only one authentication technique is allowed per user, either a
`token` or a `password` should be provided, if both are provided, the `password`
will be ignored.

`clientKeyData` and `clientCertificateData` are only required if `token` or
`password` is not provided for authentication to cluster.

The following example shows the syntax and structure of a `cluster` resource:

```yaml
Expand Down
2 changes: 2 additions & 0 deletions docs/variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ Each variable is accessible via `resources.inputs.<resource name>.<variable name
| `token` | Bearer token. |
| `insecure` | Whether TLS connection to server should be verified: `"true"` or `"false"`. |
| `cadata` | Stringified PEM-encoded bytes typically read from a root certificates bundle. |
| `clientKeyData` | Stringified PEM-encoded bytes from a client key file for TLS. |
| `clientCertificateData` | Stringified PEM-encoded bytes from a client cert file for TLS. |

#### CloudEvent PipelineResource

Expand Down
36 changes: 26 additions & 10 deletions pkg/apis/resource/v1alpha1/cluster/cluster_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ type Resource struct {
// CAData holds PEM-encoded bytes (typically read from a root certificates bundle).
// CAData takes precedence over CAFile
CAData []byte `json:"cadata"`
// ClientKeyData contains PEM-encoded data from a client key file for TLS.
ClientKeyData []byte `json:"clientKeyData"`
// ClientCertificateData contains PEM-encoded data from a client cert file for TLS.
ClientCertificateData []byte `json:"clientCertificateData"`
//Secrets holds a struct to indicate a field name and corresponding secret name to populate it
Secrets []resource.SecretParam `json:"secrets"`

Expand Down Expand Up @@ -88,6 +92,16 @@ func NewResource(kubeconfigWriterImage string, r *resource.PipelineResource) (*R
sDec, _ := b64.StdEncoding.DecodeString(param.Value)
clusterResource.CAData = sDec
}
case strings.EqualFold(param.Name, "ClientKeyData"):
if param.Value != "" {
sDec, _ := b64.StdEncoding.DecodeString(param.Value)
clusterResource.ClientKeyData = sDec
}
case strings.EqualFold(param.Name, "ClientCertificateData"):
if param.Value != "" {
sDec, _ := b64.StdEncoding.DecodeString(param.Value)
clusterResource.ClientCertificateData = sDec
}
}
}
clusterResource.Secrets = r.Spec.SecretParams
Expand Down Expand Up @@ -123,16 +137,18 @@ func (s *Resource) GetURL() string {
// Replacements is used for template replacement on a ClusterResource inside of a Taskrun.
func (s *Resource) Replacements() map[string]string {
return map[string]string{
"name": s.Name,
"type": s.Type,
"url": s.URL,
"revision": s.Revision,
"username": s.Username,
"password": s.Password,
"namespace": s.Namespace,
"token": s.Token,
"insecure": strconv.FormatBool(s.Insecure),
"cadata": string(s.CAData),
"name": s.Name,
"type": s.Type,
"url": s.URL,
"revision": s.Revision,
"username": s.Username,
"password": s.Password,
"namespace": s.Namespace,
"token": s.Token,
"insecure": strconv.FormatBool(s.Insecure),
"cadata": string(s.CAData),
"clientKeyData": string(s.ClientKeyData),
divyansh42 marked this conversation as resolved.
Show resolved Hide resolved
"clientCertificateData": string(s.ClientCertificateData),
}
}

Expand Down
22 changes: 21 additions & 1 deletion pkg/apis/resource/v1alpha1/cluster/cluster_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,26 @@ func TestNewClusterResource(t *testing.T) {
Password: "pass",
KubeconfigWriterImage: "override-with-kubeconfig-writer:latest",
},
}, {
desc: "resource with clientKeyData and clientCertificateData instead of token or password",
resource: tb.PipelineResource("test-cluster-resource", "default", tb.PipelineResourceSpec(
resourcev1alpha1.PipelineResourceTypeCluster,
tb.PipelineResourceSpecParam("url", "http://10.10.10.10"),
tb.PipelineResourceSpecParam("username", "user"),
tb.PipelineResourceSpecParam("cadata", "bXktY2x1c3Rlci1jZXJ0Cg"),
tb.PipelineResourceSpecParam("clientKeyData", "Y2xpZW50LWtleS1kYXRh"),
tb.PipelineResourceSpecParam("clientCertificateData", "Y2xpZW50LWNlcnRpZmljYXRlLWRhdGE="),
)),
want: &cluster.Resource{
Name: "test-cluster-resource",
Type: resourcev1alpha1.PipelineResourceTypeCluster,
URL: "http://10.10.10.10",
Username: "user",
CAData: []byte("my-cluster-cert"),
ClientKeyData: []byte("client-key-data"),
ClientCertificateData: []byte("client-certificate-data"),
KubeconfigWriterImage: "override-with-kubeconfig-writer:latest",
},
}, {
desc: "set insecure flag to true when there is no cert",
resource: tb.PipelineResource("test-cluster-resource", "foo", tb.PipelineResourceSpec(
Expand Down Expand Up @@ -155,7 +175,7 @@ func TestClusterResource_GetInputTaskModifier(t *testing.T) {
Name: "kubeconfig-9l9zj",
Image: "override-with-kubeconfig-writer:latest",
Command: []string{"/ko-app/kubeconfigwriter"},
Args: []string{"-clusterConfig", `{"name":"test-cluster-resource","type":"cluster","url":"http://10.10.10.10","revision":"","username":"","password":"","namespace":"","token":"","Insecure":false,"cadata":null,"secrets":[{"fieldName":"cadata","secretKey":"cadatakey","secretName":"secret1"}]}`},
Args: []string{"-clusterConfig", `{"name":"test-cluster-resource","type":"cluster","url":"http://10.10.10.10","revision":"","username":"","password":"","namespace":"","token":"","Insecure":false,"cadata":null,"clientKeyData":null,"clientCertificateData":null,"secrets":[{"fieldName":"cadata","secretKey":"cadatakey","secretName":"secret1"}]}`},
Env: []corev1.EnvVar{{
Name: "CADATA",
ValueFrom: &corev1.EnvVarSource{
Expand Down
12 changes: 10 additions & 2 deletions pkg/apis/resource/v1alpha1/pipelineresource_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func (rs *PipelineResourceSpec) Validate(ctx context.Context) *apis.FieldError {
return apis.ErrMissingField("spec.type")
}
if rs.Type == PipelineResourceTypeCluster {
var authFound, cadataFound, isInsecure bool
var authFound, cadataFound, clientKeyDataFound, clientCertificateDataFound, isInsecure bool
for _, param := range rs.Params {
switch {
case strings.EqualFold(param.Name, "URL"):
Expand All @@ -54,6 +54,10 @@ func (rs *PipelineResourceSpec) Validate(ctx context.Context) *apis.FieldError {
case strings.EqualFold(param.Name, "CAData"):
authFound = true
cadataFound = true
case strings.EqualFold(param.Name, "ClientKeyData"):
clientKeyDataFound = true
case strings.EqualFold(param.Name, "ClientCertificateData"):
clientCertificateDataFound = true
case strings.EqualFold(param.Name, "Token"):
authFound = true
case strings.EqualFold(param.Name, "insecure"):
Expand All @@ -71,10 +75,14 @@ func (rs *PipelineResourceSpec) Validate(ctx context.Context) *apis.FieldError {
cadataFound = true
}
}
// if both clientKeyData and clientCertificateData found
if clientCertificateDataFound && clientKeyDataFound {
authFound = true
}

// One auth method must be supplied
if !(authFound) {
return apis.ErrMissingField("username or CAData or token param")
return apis.ErrMissingField("username or CAData or token param or clientKeyData or ClientCertificateData")
}
if !cadataFound && !isInsecure {
return apis.ErrMissingField("CAData param")
Expand Down
10 changes: 9 additions & 1 deletion pkg/apis/resource/v1alpha1/pipelineresource_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ func TestResourceValidation_Invalid(t *testing.T) {
Name: "username", Value: "admin",
}, {
Name: "token", Value: "my-token",
}, {
Name: "clientKeyData", Value: "Y2xpZW50LWtleS1kYXRh",
}, {
Name: "clientCertificateData", Value: "Y2xpZW50LWNlcnRpZmljYXRlLWRhdGE=",
}},
},
},
Expand All @@ -63,7 +67,7 @@ func TestResourceValidation_Invalid(t *testing.T) {
}},
},
},
want: apis.ErrMissingField("username or CAData or token param"),
want: apis.ErrMissingField("username or CAData or token param or clientKeyData or ClientCertificateData"),
}, {
name: "cluster with missing cadata",
res: &v1alpha1.PipelineResource{
Expand Down Expand Up @@ -171,6 +175,10 @@ func TestClusterResourceValidation_Valid(t *testing.T) {
Name: "username", Value: "admin",
}, {
Name: "token", Value: "my-token",
}, {
Name: "clientKeyData", Value: "Y2xpZW50LWtleS1kYXRh",
}, {
Name: "clientCertificateData", Value: "Y2xpZW50LWNlcnRpZmljYXRlLWRhdGE=",
}},
},
},
Expand Down
12 changes: 10 additions & 2 deletions pkg/reconciler/taskrun/resources/input_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,14 @@ func setUp() {
Name: "CAdata",
// echo "my-ca-cert" | base64
Value: "bXktY2EtY2VydAo=",
}, {
Name: "clientKeyData",
// echo "my-ca-cert" | base64
Value: "Y2xpZW50LWtleS1kYXRh",
}, {
Name: "clientCertificateData",
// echo "my-ca-cert" | base64
Value: "Y2xpZW50LWNlcnRpZmljYXRlLWRhdGE=",
}},
},
}, {
Expand Down Expand Up @@ -834,7 +842,7 @@ gsutil cp gs://fake-bucket/rules.zip /workspace/gcs-dir
Image: "override-with-kubeconfig-writer:latest",
Command: []string{"/ko-app/kubeconfigwriter"},
Args: []string{
"-clusterConfig", `{"name":"cluster3","type":"cluster","url":"http://10.10.10.10","revision":"","username":"","password":"","namespace":"namespace1","token":"","Insecure":false,"cadata":"bXktY2EtY2VydAo=","secrets":null}`,
"-clusterConfig", `{"name":"cluster3","type":"cluster","url":"http://10.10.10.10","revision":"","username":"","password":"","namespace":"namespace1","token":"","Insecure":false,"cadata":"bXktY2EtY2VydAo=","clientKeyData":"Y2xpZW50LWtleS1kYXRh","clientCertificateData":"Y2xpZW50LWNlcnRpZmljYXRlLWRhdGE=","secrets":null}`,
},
}}},
Resources: &v1beta1.TaskResources{
Expand Down Expand Up @@ -884,7 +892,7 @@ gsutil cp gs://fake-bucket/rules.zip /workspace/gcs-dir
Image: "override-with-kubeconfig-writer:latest",
Command: []string{"/ko-app/kubeconfigwriter"},
Args: []string{
"-clusterConfig", `{"name":"cluster2","type":"cluster","url":"http://10.10.10.10","revision":"","username":"","password":"","namespace":"","token":"","Insecure":false,"cadata":null,"secrets":[{"fieldName":"cadata","secretKey":"cadatakey","secretName":"secret1"}]}`,
"-clusterConfig", `{"name":"cluster2","type":"cluster","url":"http://10.10.10.10","revision":"","username":"","password":"","namespace":"","token":"","Insecure":false,"cadata":null,"clientKeyData":null,"clientCertificateData":null,"secrets":[{"fieldName":"cadata","secretKey":"cadatakey","secretName":"secret1"}]}`,
},
Env: []corev1.EnvVar{{
ValueFrom: &corev1.EnvVarSource{
Expand Down