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

[WIP] Add unit tests for creating secrets #158

Open
wants to merge 2 commits into
base: private/ojas/create-secrets
Choose a base branch
from
Open
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
129 changes: 23 additions & 106 deletions cmd/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ import (

corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
certutil "k8s.io/client-go/util/cert"

"github.com/platform9/cctl/common"
"github.com/platform9/cctl/pkg/util/clusterapi"
"github.com/platform9/cctl/pkg/util/secret"
"github.com/platform9/cctl/semverutil"

spconstants "github.com/platform9/ssh-provider/constants"
Expand All @@ -43,7 +43,6 @@ var clusterCmdCreate = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {

vip := cmd.Flag("vip").Value.String()

// Verify that both routerID and vip are not defaults if one is specified
if (routerID == common.RouterID) != (len(vip) == 0) {
log.Fatalf("Must specify both routerID and vip, or leave both empty for non-HA cluster.")
Expand Down Expand Up @@ -85,12 +84,29 @@ var clusterCmdCreate = &cobra.Command{
}
}
setClusterConfigDefaults(clusterConfig)
newAPIServerCASecret := createCASecret(common.DefaultAPIServerCASecretName, apiServerCACertFile, apiServerCAKeyFile)
newEtcdCASecret := createCASecret(common.DefaultEtcdCASecretName, etcdCACertFile, etcdCAKeyFile)
newFrontProxyCASecret := createCASecret(common.DefaultFrontProxyCASecretName, frontProxyCACertFile, frontProxyCAKeyFile)

newServiceAccountKeySecret := createServiceAccountKeySecret(saPrivateKeyFile, saPublicKeyFile)
newBootstrapTokenSecret := createBootstrapTokenSecret(common.DefaultBootstrapTokenSecretName)
newAPIServerCASecret, err := secret.CreateCASecret(common.DefaultAPIServerCASecretName, apiServerCACertFile, apiServerCAKeyFile)
if err != nil {
log.Fatalf("Unable to generate API Server CA cert pair: %v", err)
}
newEtcdCASecret, err := secret.CreateCASecret(common.DefaultEtcdCASecretName, etcdCACertFile, etcdCAKeyFile)
if err != nil {
log.Fatalf("Unable to generate etcd CA cert pair: %v", err)
}
newFrontProxyCASecret, err := secret.CreateCASecret(common.DefaultFrontProxyCASecretName, frontProxyCACertFile, frontProxyCAKeyFile)
if err != nil {
log.Fatalf("Unable to generate front proxy CA cert pair: %v", err)
}

newServiceAccountKeySecret, err := secret.CreateSAKeySecret(common.DefaultServiceAccountKeySecretName, saPrivateKeyFile, saPublicKeyFile)
if err != nil {
log.Fatalf("Unable to generate service account key pair: %v", err)
}
newBootstrapTokenSecret, err := secret.CreateBootstrapTokenSecret(common.DefaultBootstrapTokenSecretName)
if err != nil {
log.Fatalf("Unable to generate bootstrap token secret: %v", err)
}

newCluster, err := createCluster(common.DefaultClusterName, podsCIDR, servicesCIDR, vip, routerID, clusterConfig)
if err != nil {
log.Fatalf("Unable to create cluster: %v", err)
Expand Down Expand Up @@ -267,105 +283,6 @@ func createCluster(clusterName, podsCIDR, servicesCIDR, vip string, routerID int
return &newCluster, nil
}

func createServiceAccountKeySecret(saPrivateKeyFile, saPublicKeyFile string) *corev1.Secret {
sakSecret := corev1.Secret{
TypeMeta: metav1.TypeMeta{
Kind: "Secret",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "serviceaccount-key",
Namespace: common.DefaultNamespace,
CreationTimestamp: metav1.Now(),
},
Data: make(map[string][]byte),
}

var privateKeyBytes []byte
var publicKeyBytes []byte
if len(saPrivateKeyFile) != 0 && len(saPublicKeyFile) != 0 {
var err error
privateKeyBytes, err = ioutil.ReadFile(saPrivateKeyFile)
if err != nil {
log.Fatalf("Unable to read service account private key %q: %v", saPrivateKeyFile, err)
}
publicKeyBytes, err = ioutil.ReadFile(saPublicKeyFile)
if err != nil {
log.Fatalf("Unable to read service account public key %q: %v", saPublicKeyFile, err)
}
} else {
key, err := certutil.NewPrivateKey()
if err != nil {
log.Fatalf("Unable to create a service account private key: %v", err)
}
privateKeyBytes = certutil.EncodePrivateKeyPEM(key)
publicKeyBytes, err = certutil.EncodePublicKeyPEM(&key.PublicKey)
if err != nil {
log.Fatalf("Unable to encode service account public key to PEM format: %v", err)
}
}

sakSecret.Data["privatekey"] = privateKeyBytes
sakSecret.Data["publickey"] = publicKeyBytes

return &sakSecret
}

func createCASecret(secretName, certFilename, keyFilename string) *corev1.Secret {
caSecret := corev1.Secret{
TypeMeta: metav1.TypeMeta{
Kind: "Secret",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Namespace: common.DefaultNamespace,
CreationTimestamp: metav1.Now(),
},
Data: make(map[string][]byte),
}

var certBytes []byte
var keyBytes []byte
if len(certFilename) != 0 && len(keyFilename) != 0 {
var err error
certBytes, err = ioutil.ReadFile(certFilename)
if err != nil {
log.Fatalf("Unable to read CA cert %q: %v", certFilename, err)
}
keyBytes, err = ioutil.ReadFile(keyFilename)
if err != nil {
log.Fatalf("Unable to read CA key %q: %v", keyFilename, err)
}
} else {
cert, key, err := common.NewCertificateAuthority()
if err != nil {
log.Fatalf("Unable to create CA: %v", err)
}
certBytes = certutil.EncodeCertPEM(cert)
keyBytes = certutil.EncodePrivateKeyPEM(key)
}
caSecret.Data["tls.crt"] = certBytes
caSecret.Data["tls.key"] = keyBytes
return &caSecret
}

func createBootstrapTokenSecret(name string) *corev1.Secret {
btSecret := corev1.Secret{
TypeMeta: metav1.TypeMeta{
Kind: "Secret",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: common.DefaultNamespace,
CreationTimestamp: metav1.Now(),
},
Data: make(map[string][]byte),
}
return &btSecret
}

var clusterCmdDelete = &cobra.Command{
Use: "cluster",
Short: "Deletes a node from a cluster",
Expand Down
71 changes: 71 additions & 0 deletions cmd/secret.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package cmd

import (
"fmt"

"github.com/platform9/cctl/common"
log "github.com/platform9/cctl/pkg/logrus"
"github.com/platform9/cctl/pkg/util/secret"
"github.com/spf13/cobra"
)

var secretCmdCreate = &cobra.Command{
Use: "secrets",
Short: "Create default secrets",
Run: func(cmd *cobra.Command, args []string) {
err := createSecretDefaults()
if err != nil {
log.Fatalf("Unable to create secrets: %v", err)
}
log.Println("Secrets created successfully.")
},
}

func createSecretDefaults() error {
newAPIServerCASecret, err := secret.CreateCASecretDefault(common.DefaultAPIServerCASecretName)
if err != nil {
return fmt.Errorf("unable to generate API server CA secret: %v", err)
}
newEtcdCASecret, err := secret.CreateCASecretDefault(common.DefaultEtcdCASecretName)
if err != nil {
return fmt.Errorf("unable to generate etcd CA secret: %v", err)
}
newFrontProxyCASecret, err := secret.CreateCASecretDefault(common.DefaultFrontProxyCASecretName)
if err != nil {
return fmt.Errorf("unable to generate front proxy CA secret: %v", err)
}

newServiceAccountKeySecret, err := secret.CreateSAKeySecretDefault(common.DefaultServiceAccountKeySecretName)
if err != nil {
return fmt.Errorf("unable to generate service account CA secret: %v", err)
}
newBootstrapTokenSecret, err := secret.CreateBootstrapTokenSecret(common.DefaultBootstrapTokenSecretName)
if err != nil {
return fmt.Errorf("unable to generate bootstrap token CA secret: %v", err)
}

if _, err := state.KubeClient.CoreV1().Secrets(common.DefaultNamespace).Create(newAPIServerCASecret); err != nil {
return fmt.Errorf("unable to create API server CA secret: %v", err)
}
if _, err := state.KubeClient.CoreV1().Secrets(common.DefaultNamespace).Create(newEtcdCASecret); err != nil {
return fmt.Errorf("unable to create etcd CA secret: %v", err)
}
if _, err := state.KubeClient.CoreV1().Secrets(common.DefaultNamespace).Create(newFrontProxyCASecret); err != nil {
return fmt.Errorf("unable to create front proxy CA secret: %v", err)
}
if _, err := state.KubeClient.CoreV1().Secrets(common.DefaultNamespace).Create(newServiceAccountKeySecret); err != nil {
return fmt.Errorf("unable to create service account secret: %v", err)
}
if _, err := state.KubeClient.CoreV1().Secrets(common.DefaultNamespace).Create(newBootstrapTokenSecret); err != nil {
return fmt.Errorf("unable to create bootstrap token secret: %v", err)
}
if err := state.PullFromAPIs(); err != nil {
return fmt.Errorf("unable to sync on-disk state: %v", err)
}

return nil
}

func init() {
createCmd.AddCommand(secretCmdCreate)
}
122 changes: 122 additions & 0 deletions pkg/util/secret/secret_util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package secret

import (
"fmt"
"io/ioutil"

"github.com/platform9/cctl/common"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
certutil "k8s.io/client-go/util/cert"
)

func CreateCASecretDefault(secretName string) (*corev1.Secret, error) {
return CreateCASecret(secretName, "", "")
}

func CreateSAKeySecretDefault(secretName string) (*corev1.Secret, error) {
return CreateSAKeySecret(secretName, "", "")
}

func CreateCASecret(secretName, certFilename, keyFilename string) (*corev1.Secret, error) {
caSecret := createSecret(secretName)

var certBytes []byte
var keyBytes []byte
if len(certFilename) != 0 && len(keyFilename) != 0 {
var err error
certBytes, err = ioutil.ReadFile(certFilename)
if err != nil {
return nil, fmt.Errorf("unable to read CA cert %q: %v", certFilename, err)
}
keyBytes, err = ioutil.ReadFile(keyFilename)
if err != nil {
return nil, fmt.Errorf("unable to read CA key %q: %v", keyFilename, err)
}
} else {
var err error
certBytes, keyBytes, err = generateCertPair()
if err != nil {
return nil, fmt.Errorf("unable to generate cert pair: %v", err)
}

}
caSecret.Data["tls.crt"] = certBytes
caSecret.Data["tls.key"] = keyBytes
return caSecret, nil
}

func CreateSAKeySecret(secretName, saPrivateKeyFile, saPublicKeyFile string) (*corev1.Secret, error) {
sakSecret := createSecret(secretName)

var privateKeyBytes []byte
var publicKeyBytes []byte
if len(saPrivateKeyFile) != 0 && len(saPublicKeyFile) != 0 {
var err error
privateKeyBytes, err = ioutil.ReadFile(saPrivateKeyFile)
if err != nil {
return nil, fmt.Errorf("unable to read service account private key %q: %v", saPrivateKeyFile, err)
}
publicKeyBytes, err = ioutil.ReadFile(saPublicKeyFile)
if err != nil {
return nil, fmt.Errorf("unable to read service account public key %q: %v", saPublicKeyFile, err)
}
} else {
var err error
privateKeyBytes, publicKeyBytes, err = generateKeyPair()
if err != nil {
return nil, fmt.Errorf("unable to generate key pair: %v", err)
}
}

sakSecret.Data["privatekey"] = privateKeyBytes
sakSecret.Data["publickey"] = publicKeyBytes

return sakSecret, nil
}

func CreateBootstrapTokenSecret(secretName string) (*corev1.Secret, error) {
btSecret := createSecret(secretName)
return btSecret, nil
}

func generateCertPair() ([]byte, []byte, error) {
var certBytes, keyBytes []byte
cert, key, err := common.NewCertificateAuthority()
if err != nil {
return nil, nil, fmt.Errorf("unable to create CA: %v", err)
}
certBytes = certutil.EncodeCertPEM(cert)
keyBytes = certutil.EncodePrivateKeyPEM(key)
return certBytes, keyBytes, nil
}

func generateKeyPair() ([]byte, []byte, error) {
var privateKeyBytes, publicKeyBytes []byte
key, err := certutil.NewPrivateKey()
if err != nil {
return nil, nil, fmt.Errorf("unable to create a service account private key: %v", err)
}
privateKeyBytes = certutil.EncodePrivateKeyPEM(key)
publicKeyBytes, err = certutil.EncodePublicKeyPEM(&key.PublicKey)
if err != nil {
return nil, nil, fmt.Errorf("unable to encode service account public key to PEM format: %v", err)
}
return privateKeyBytes, publicKeyBytes, nil
}

func createSecret(name string) *corev1.Secret {
btSecret := corev1.Secret{
TypeMeta: metav1.TypeMeta{
Kind: "Secret",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: common.DefaultNamespace,
CreationTimestamp: metav1.Now(),
},
Data: make(map[string][]byte),
}
return &btSecret
}
Loading