Skip to content

Commit

Permalink
Add unit test to virt-api/api.go
Browse files Browse the repository at this point in the history
Add a coverage of 70% of it and also little refacotring on api.go to
mock the call to golang filesystem calls.
  • Loading branch information
qinqon committed Jan 10, 2019
1 parent bdf1159 commit 01fd4da
Show file tree
Hide file tree
Showing 7 changed files with 838 additions and 175 deletions.
4 changes: 3 additions & 1 deletion hack/coverage.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@
set -e

path=${1:-./pkg/...}
profile=.coverprofile

go test -cover -v -coverprofile=.coverprofile $(go list ${path})
go test -cover -v -coverprofile=$profile $(go list ${path})
go tool cover -html=$profile -o coverage.html
176 changes: 100 additions & 76 deletions pkg/virt-api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,12 @@ type virtAPIApp struct {
keyBytes []byte
clientCABytes []byte
requestHeaderClientCABytes []byte
certFile string
keyFile string
clientCAFile string
signingCertFile string
namespace string
tlsConfig *tls.Config
}

var _ service.Service = &virtAPIApp{}
Expand Down Expand Up @@ -452,25 +457,15 @@ func (app *virtAPIApp) createWebhook() error {
return nil
}

func (app *virtAPIApp) createValidatingWebhook() error {
registerWebhook := false
func (app *virtAPIApp) validatingWebhooks() []admissionregistrationv1beta1.Webhook {

vmiPathCreate := vmiCreateValidatePath
vmiPathUpdate := vmiUpdateValidatePath
vmPath := vmValidatePath
vmirsPath := vmirsValidatePath
vmipresetPath := vmipresetValidatePath
migrationCreatePath := migrationCreateValidatePath
migrationUpdatePath := migrationUpdateValidatePath

webhookRegistration, err := app.virtCli.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Get(virtWebhookValidator, metav1.GetOptions{})
if err != nil {
if k8serrors.IsNotFound(err) {
registerWebhook = true
} else {
return err
}
}

failurePolicy := admissionregistrationv1beta1.Fail

webHooks := []admissionregistrationv1beta1.Webhook{
Expand Down Expand Up @@ -633,6 +628,22 @@ func (app *virtAPIApp) createValidatingWebhook() error {
},
}

return webHooks
}

func (app *virtAPIApp) createValidatingWebhook() error {
registerWebhook := false
webhookRegistration, err := app.virtCli.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Get(virtWebhookValidator, metav1.GetOptions{})
if err != nil {
if k8serrors.IsNotFound(err) {
fmt.Println(err)
registerWebhook = true
} else {
return err
}
}
webHooks := app.validatingWebhooks()

if registerWebhook {
_, err := app.virtCli.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Create(&admissionregistrationv1beta1.ValidatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{
Expand All @@ -647,7 +658,6 @@ func (app *virtAPIApp) createValidatingWebhook() error {
return err
}
} else {

for _, webhook := range webhookRegistration.Webhooks {
if webhook.ClientConfig.Service != nil && webhook.ClientConfig.Service.Namespace != app.namespace {
return fmt.Errorf("ValidatingAdmissionWebhook [%s] is already registered using services endpoints in a different namespace. Existing webhook registration must be deleted before virt-api can proceed.", virtWebhookValidator)
Expand Down Expand Up @@ -688,23 +698,8 @@ func (app *virtAPIApp) createValidatingWebhook() error {
return nil
}

func (app *virtAPIApp) createMutatingWebhook() error {
namespace, err := util.GetNamespace()
if err != nil {
return err
}
registerWebhook := false
func (app *virtAPIApp) mutatingWebhooks() []admissionregistrationv1beta1.Webhook {
vmiPath := vmiMutatePath

webhookRegistration, err := app.virtCli.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Get(virtWebhookMutator, metav1.GetOptions{})
if err != nil {
if k8serrors.IsNotFound(err) {
registerWebhook = true
} else {
return err
}
}

webHooks := []admissionregistrationv1beta1.Webhook{
{
Name: "virtualmachineinstances-mutator.kubevirt.io",
Expand All @@ -720,14 +715,29 @@ func (app *virtAPIApp) createMutatingWebhook() error {
}},
ClientConfig: admissionregistrationv1beta1.WebhookClientConfig{
Service: &admissionregistrationv1beta1.ServiceReference{
Namespace: namespace,
Namespace: app.namespace,
Name: virtApiServiceName,
Path: &vmiPath,
},
CABundle: app.signingCertBytes,
},
},
}
return webHooks
}

func (app *virtAPIApp) createMutatingWebhook() error {
registerWebhook := false

webhookRegistration, err := app.virtCli.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Get(virtWebhookMutator, metav1.GetOptions{})
if err != nil {
if k8serrors.IsNotFound(err) {
registerWebhook = true
} else {
return err
}
}
webHooks := app.mutatingWebhooks()

if registerWebhook {
_, err := app.virtCli.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Create(&admissionregistrationv1beta1.MutatingWebhookConfiguration{
Expand All @@ -745,7 +755,7 @@ func (app *virtAPIApp) createMutatingWebhook() error {
} else {

for _, webhook := range webhookRegistration.Webhooks {
if webhook.ClientConfig.Service != nil && webhook.ClientConfig.Service.Namespace != namespace {
if webhook.ClientConfig.Service != nil && webhook.ClientConfig.Service.Namespace != app.namespace {
return fmt.Errorf("MutatingAdmissionWebhook [%s] is already registered using services endpoints in a different namespace. Existing webhook registration must be deleted before virt-api can proceed.", virtWebhookMutator)
}
}
Expand All @@ -765,41 +775,21 @@ func (app *virtAPIApp) createMutatingWebhook() error {
return nil
}

func (app *virtAPIApp) createSubresourceApiservice() error {
namespace, err := util.GetNamespace()
if err != nil {
return err
}
config, err := kubecli.GetConfig()
if err != nil {
return err
}
aggregatorClient := aggregatorclient.NewForConfigOrDie(config)
func (app *virtAPIApp) subresourceApiservice() *apiregistrationv1beta1.APIService {

subresourceAggregatedApiName := v1.SubresourceGroupVersion.Version + "." + v1.SubresourceGroupName

registerApiService := false

apiService, err := aggregatorClient.ApiregistrationV1beta1().APIServices().Get(subresourceAggregatedApiName, metav1.GetOptions{})
if err != nil {
if k8serrors.IsNotFound(err) {
registerApiService = true
} else {
return err
}
}

newApiService := &apiregistrationv1beta1.APIService{
return &apiregistrationv1beta1.APIService{
ObjectMeta: metav1.ObjectMeta{
Name: subresourceAggregatedApiName,
Namespace: namespace,
Namespace: app.namespace,
Labels: map[string]string{
v1.AppLabel: "virt-api-aggregator",
},
},
Spec: apiregistrationv1beta1.APIServiceSpec{
Service: &apiregistrationv1beta1.ServiceReference{
Namespace: namespace,
Namespace: app.namespace,
Name: virtApiServiceName,
},
Group: v1.SubresourceGroupName,
Expand All @@ -810,18 +800,41 @@ func (app *virtAPIApp) createSubresourceApiservice() error {
},
}

}

func (app *virtAPIApp) createSubresourceApiservice() error {
config, err := kubecli.GetConfig()
if err != nil {
return err
}

subresourceApiservice := app.subresourceApiservice()

aggregatorClient := aggregatorclient.NewForConfigOrDie(config)

registerApiService := false

apiService, err := aggregatorClient.ApiregistrationV1beta1().APIServices().Get(subresourceApiservice.Name, metav1.GetOptions{})
if err != nil {
if k8serrors.IsNotFound(err) {
registerApiService = true
} else {
return err
}
}

if registerApiService {
_, err = aggregatorClient.ApiregistrationV1beta1().APIServices().Create(newApiService)
_, err = aggregatorClient.ApiregistrationV1beta1().APIServices().Create(app.subresourceApiservice())
if err != nil {
return err
}
} else {
if apiService.Spec.Service != nil && apiService.Spec.Service.Namespace != namespace {
return fmt.Errorf("apiservice [%s] is already registered in a different namespace. Existing apiservice registration must be deleted before virt-api can proceed.", subresourceAggregatedApiName)
if apiService.Spec.Service != nil && apiService.Spec.Service.Namespace != app.namespace {
return fmt.Errorf("apiservice [%s] is already registered in a different namespace. Existing apiservice registration must be deleted before virt-api can proceed.", subresourceApiservice.Name)
}

// Always update spec to latest.
apiService.Spec = newApiService.Spec
apiService.Spec = app.subresourceApiservice().Spec
_, err := aggregatorClient.ApiregistrationV1beta1().APIServices().Update(apiService)
if err != nil {
return err
Expand All @@ -830,23 +843,21 @@ func (app *virtAPIApp) createSubresourceApiservice() error {
return nil
}

func (app *virtAPIApp) startTLS() error {

errors := make(chan error)
func (app *virtAPIApp) setupTLS(fs Filesystem) error {

keyFile := filepath.Join(app.certsDirectory, "/key.pem")
certFile := filepath.Join(app.certsDirectory, "/cert.pem")
signingCertFile := filepath.Join(app.certsDirectory, "/signingCert.pem")
clientCAFile := filepath.Join(app.certsDirectory, "/clientCA.crt")
app.keyFile = filepath.Join(app.certsDirectory, "/key.pem")
app.certFile = filepath.Join(app.certsDirectory, "/cert.pem")
app.signingCertFile = filepath.Join(app.certsDirectory, "/signingCert.pem")
app.clientCAFile = filepath.Join(app.certsDirectory, "/clientCA.crt")

// Write the certs to disk
err := ioutil.WriteFile(clientCAFile, app.clientCABytes, 0600)
err := fs.WriteFile(app.clientCAFile, app.clientCABytes, 0600)
if err != nil {
return err
}

if len(app.requestHeaderClientCABytes) != 0 {
f, err := os.OpenFile(clientCAFile, os.O_APPEND|os.O_WRONLY, 0600)
f, err := fs.OpenFile(app.clientCAFile, os.O_APPEND|os.O_WRONLY, 0600)
if err != nil {
return err
}
Expand All @@ -858,27 +869,27 @@ func (app *virtAPIApp) startTLS() error {
}
}

err = ioutil.WriteFile(keyFile, app.keyBytes, 0600)
err = fs.WriteFile(app.keyFile, app.keyBytes, 0600)
if err != nil {
return err
}
err = ioutil.WriteFile(certFile, app.certBytes, 0600)
err = fs.WriteFile(app.certFile, app.certBytes, 0600)
if err != nil {
return err
}
err = ioutil.WriteFile(signingCertFile, app.signingCertBytes, 0600)
err = fs.WriteFile(app.signingCertFile, app.signingCertBytes, 0600)
if err != nil {
return err
}

// create the client CA pool.
// This ensures we're talking to the k8s api server
pool, err := cert.NewPool(clientCAFile)
pool, err := cert.NewPool(app.clientCAFile)
if err != nil {
return err
}

tlsConfig := &tls.Config{
app.tlsConfig = &tls.Config{
ClientCAs: pool,
// A VerifyClientCertIfGiven request means we're not guaranteed
// a client has been authenticated unless they provide a peer
Expand All @@ -897,16 +908,29 @@ func (app *virtAPIApp) startTLS() error {
// and our aggregated endpoint never becomes available.
ClientAuth: tls.VerifyClientCertIfGiven,
}
tlsConfig.BuildNameToCertificate()
app.tlsConfig.BuildNameToCertificate()
return nil
}

func (app *virtAPIApp) startTLS() error {

errors := make(chan error)

err := app.setupTLS(IOUtil{})
if err != nil {
return err
}

// start TLS server
go func() {
http.Handle("/metrics", promhttp.Handler())

server := &http.Server{
Addr: fmt.Sprintf("%s:%d", app.BindAddress, app.Port),
TLSConfig: tlsConfig,
TLSConfig: app.tlsConfig,
}

errors <- server.ListenAndServeTLS(certFile, keyFile)
errors <- server.ListenAndServeTLS(app.certFile, app.keyFile)
}()

// wait for server to exit
Expand Down
Loading

0 comments on commit 01fd4da

Please sign in to comment.