diff --git a/pkg/nfd-master/nfd-master.go b/pkg/nfd-master/nfd-master.go index c0721d258..95e0bc8dc 100644 --- a/pkg/nfd-master/nfd-master.go +++ b/pkg/nfd-master/nfd-master.go @@ -27,6 +27,7 @@ import ( "regexp" "sort" "strings" + "time" "golang.org/x/net/context" "google.golang.org/grpc" @@ -72,16 +73,18 @@ type Args struct { type NfdMaster interface { Run() error Stop() + WaitForReady(time.Duration) bool } type nfdMaster struct { args Args server *grpc.Server + ready chan bool } // Create new NfdMaster server instance. func NewNfdMaster(args Args) (*nfdMaster, error) { - nfd := &nfdMaster{args: args} + nfd := &nfdMaster{args: args, ready: make(chan bool, 1)} // Check TLS related args if args.CertFile != "" || args.KeyFile != "" || args.CaFile != "" { @@ -121,6 +124,9 @@ func (m *nfdMaster) Run() error { if err != nil { return fmt.Errorf("failed to listen: %v", err) } + // Notify that we're ready to accept connections + m.ready <- true + close(m.ready) serverOpts := []grpc.ServerOption{} // Enable mutual TLS authentication if --cert-file, --key-file or --ca-file @@ -159,6 +165,21 @@ func (m *nfdMaster) Stop() { m.server.Stop() } +// Wait until NfdMaster is able able to accept connections. +func (m *nfdMaster) WaitForReady(timeout time.Duration) bool { + select { + case ready, ok := <-m.ready: + // Ready if the flag is true or the channel has been closed + if ready == true || ok == false { + return true + } + case <-time.After(timeout): + return false + } + // We should never end-up here + return false +} + // Advertise NFD master information func updateMasterNode(helper apihelper.APIHelpers) error { cli, err := helper.GetClient() diff --git a/pkg/nfd-worker/nfd-worker_test.go b/pkg/nfd-worker/nfd-worker_test.go index 8b2d1fc58..9ecc4b7f6 100644 --- a/pkg/nfd-worker/nfd-worker_test.go +++ b/pkg/nfd-worker/nfd-worker_test.go @@ -17,12 +17,57 @@ limitations under the License. package nfdworker_test import ( + "fmt" + "os" "testing" + "time" . "github.com/smartystreets/goconvey/convey" + nfdmaster "sigs.k8s.io/node-feature-discovery/pkg/nfd-master" w "sigs.k8s.io/node-feature-discovery/pkg/nfd-worker" + "sigs.k8s.io/node-feature-discovery/test/data" ) +type testContext struct { + master nfdmaster.NfdMaster + errs chan error +} + +func setupTest(args nfdmaster.Args) testContext { + // Fixed port and no-publish, for convenience + args.NoPublish = true + args.Port = 8192 + m, err := nfdmaster.NewNfdMaster(args) + if err != nil { + fmt.Printf("Test setup failed: %v\n", err) + os.Exit(1) + } + ctx := testContext{master: m, errs: make(chan error)} + + // Run nfd-master instance, intended to be used as the server counterpart + go func() { + ctx.errs <- ctx.master.Run() + close(ctx.errs) + }() + ready := ctx.master.WaitForReady(time.Second) + if !ready { + fmt.Println("Test setup failed: timeout while waiting for nfd-master") + os.Exit(1) + } + + return ctx +} + +func teardownTest(ctx testContext) { + ctx.master.Stop() + for e := range ctx.errs { + if e != nil { + fmt.Printf("Error in test context: %v\n", e) + os.Exit(1) + } + } +} + func TestNewNfdWorker(t *testing.T) { Convey("When initializing new NfdWorker instance", t, func() { Convey("When one of --cert-file, --key-file or --ca-file is missing", func() { @@ -37,3 +82,45 @@ func TestNewNfdWorker(t *testing.T) { }) }) } + +func TestRun(t *testing.T) { + ctx := setupTest(nfdmaster.Args{}) + defer teardownTest(ctx) + Convey("When running nfd-worker against nfd-master", t, func() { + Convey("When publishing features from fake source", func() { + worker, _ := w.NewNfdWorker(w.Args{Oneshot: true, Sources: []string{"fake"}, Server: "localhost:8192"}) + err := worker.Run() + Convey("No error should be returned", func() { + So(err, ShouldBeNil) + }) + }) + }) +} + +func TestRunTls(t *testing.T) { + masterArgs := nfdmaster.Args{ + CaFile: data.FilePath("ca.crt"), + CertFile: data.FilePath("nfd-test-master.crt"), + KeyFile: data.FilePath("nfd-test-master.key"), + VerifyNodeName: false, + } + ctx := setupTest(masterArgs) + defer teardownTest(ctx) + Convey("When running nfd-worker against nfd-master with mutual TLS auth enabled", t, func() { + Convey("When publishing features from fake source", func() { + workerArgs := w.Args{ + CaFile: data.FilePath("ca.crt"), + CertFile: data.FilePath("nfd-test-worker.crt"), + KeyFile: data.FilePath("nfd-test-worker.key"), + Oneshot: true, + Sources: []string{"fake"}, + Server: "localhost:8192", + ServerNameOverride: "nfd-test-master"} + worker, _ := w.NewNfdWorker(workerArgs) + err := worker.Run() + Convey("No error should be returned", func() { + So(err, ShouldBeNil) + }) + }) + }) +} diff --git a/test/data/ca.crt b/test/data/ca.crt new file mode 100644 index 000000000..ec474b212 --- /dev/null +++ b/test/data/ca.crt @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIIChzCCAfCgAwIBAgIJAN7lBwV9QUv7MA0GCSqGSIb3DQEBCwUAMFsxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQxFDASBgNVBAMMC25mZC10ZXN0LWNhMB4XDTE5MDQwNTEz +MzAxNVoXDTI5MDQwMjEzMzAxNVowWzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNv +bWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEUMBIG +A1UEAwwLbmZkLXRlc3QtY2EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAPAc +vlYeDH60PziqlNGwRJ6MQLGfAMgGNZnaBu7vFa0Bs4PK35AnzaFPi9F6mGtl19AF +G4CQuZ8SZdopztd3xpB5bKzDkVpwuNiCYTmAhnCNZu3MCVD8oMUgoU1ZRm+gDGF/ +OnIUKG8a681t99FLob6q8oHWAWXxUcAyfdYhypopAgMBAAGjUzBRMB0GA1UdDgQW +BBRy1A5l3zYT5+GY7FmRnVRm/vm6qTAfBgNVHSMEGDAWgBRy1A5l3zYT5+GY7FmR +nVRm/vm6qTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4GBAI3CdZKG +u6/WC6PcNShIz/cIVvdfzDrhwbZwciRb3ovdsEndMboaaFADLSc55tfnU9uFKBsW +JJ/2U4sHqJrZOPOiWvwmKf4mgpriMX/NNNHBkj4GxNV7GspkRNzBtUdRdIi9ZVRz +gemI0b3c7AEURtzKKnzyNzRIq+asmb9Mkz8E +-----END CERTIFICATE----- diff --git a/test/data/data.go b/test/data/data.go new file mode 100644 index 000000000..9d1fd9c0a --- /dev/null +++ b/test/data/data.go @@ -0,0 +1,33 @@ +/* +Copyright 2019 The Kubernetes Authors. + +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 data + +import ( + "path/filepath" + "runtime" +) + +var packagePath string + +func init() { + _, thisFile, _, _ := runtime.Caller(0) + packagePath = filepath.Dir(thisFile) +} + +func FilePath(filePath string) string { + return filepath.Join(packagePath, filePath) +} diff --git a/test/data/nfd-test-master.crt b/test/data/nfd-test-master.crt new file mode 100644 index 000000000..25b427996 --- /dev/null +++ b/test/data/nfd-test-master.crt @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICMTCCAZoCCQCI0sgI4VdhPjANBgkqhkiG9w0BAQsFADBbMQswCQYDVQQGEwJB +VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 +cyBQdHkgTHRkMRQwEgYDVQQDDAtuZmQtdGVzdC1jYTAeFw0xOTA0MDUxMzMxMTRa +Fw0yOTA0MDIxMzMxMTRaMF8xCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0 +YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxGDAWBgNVBAMM +D25mZC10ZXN0LW1hc3RlcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4ZQO +O3uM/BlGhgtlUPxP0eeZEtmcKKXhqLclCkpQXds42EQV2/bfLetV4aSoewRg1yf/ ++fHB50Jivqpnxw2+9kL7H4jog6rpWYVH3iSYvwjshc+Bf/ePKULE2lhve0/gNylr +hRfhiFBr9YHq2Baj6hu9kCJwqL8MisT+xkNttkkCAwEAATANBgkqhkiG9w0BAQsF +AAOBgQCQJEQbdDAZfBL/twzkQ5lbpeCaQ/bSrKLid3iwmsxO4hSQetdnieXGvgI+ +VZB4EbtouIMcQiThBS9rUgZ5mDZwqVJhY/DZ/9LzPzovRG4Ptafay04tmVeqXFiC +MISkbKE+7JwrDxFjnDzfiYoPYCi5hjoVgU0YsYp5e/DW/TP3SA== +-----END CERTIFICATE----- diff --git a/test/data/nfd-test-master.key b/test/data/nfd-test-master.key new file mode 100644 index 000000000..1348716c3 --- /dev/null +++ b/test/data/nfd-test-master.key @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAOGUDjt7jPwZRoYL +ZVD8T9HnmRLZnCil4ai3JQpKUF3bONhEFdv23y3rVeGkqHsEYNcn//nxwedCYr6q +Z8cNvvZC+x+I6IOq6VmFR94kmL8I7IXPgX/3jylCxNpYb3tP4Dcpa4UX4YhQa/WB +6tgWo+obvZAicKi/DIrE/sZDbbZJAgMBAAECgYEAvKHFQQJxA8LTEXZoE8/Zo4qK +m5OzHN6SFDaKV8+K4uFV6KsOqHEJcemwWE8LwEsJ/AFr8YOzhQIjdpMi0vZwrwWE +0wBTkKctYEpuKLCFROkIiDPV1c1m5i5+kjDpvHvhwnufIozgLz7rFcC5ucR5gSMq +Uy1V3sfF9DM0UBBuoX0CQQD3Ogmfgmffi8gCq1zsQcp99u5R3Yj/ApQjQgRz0k6g +IhbY1i3u+fY7pTc/nBO9ezbvy4SHFP0rkfVbINQ25mdnAkEA6ZVZxUmI0r3GnTsZ +Rrya2lYXA9KfhgLFvpx7eAaaejClQyCP+fpoyV3KXPlI9J9bWAazO4k0jbmeNEL9 +F9DWzwJBAIdjfzWdQqlHcWcU1TSE6xGEkwq+GXIdxWZxluKev3QudviUgl8nAFO1 +rMXnAWB5A6Laf19CfUrJCea32b+e+e0CQCdMW0gX8Q8ToqC9WqlN/feR2FlqTDBt +svs4tIUjB0ZbfNJoXhC+knaecvdlcWLGlMWgivMPSGo3umgshQxGtH0CQQDlTjdU +stoxEneM2vIv9doqmXo80zkoMIG4gdsaINnfcbpnh7Is+mEdDGll/eSrekgBDpGw +rk9C6sqYwFFCY3Re +-----END PRIVATE KEY----- diff --git a/test/data/nfd-test-worker.crt b/test/data/nfd-test-worker.crt new file mode 100644 index 000000000..cfbcf4f35 --- /dev/null +++ b/test/data/nfd-test-worker.crt @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICMTCCAZoCCQCI0sgI4VdhPzANBgkqhkiG9w0BAQsFADBbMQswCQYDVQQGEwJB +VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 +cyBQdHkgTHRkMRQwEgYDVQQDDAtuZmQtdGVzdC1jYTAeFw0xOTA0MDUxMzMxMzRa +Fw0yOTA0MDIxMzMxMzRaMF8xCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0 +YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxGDAWBgNVBAMM +D25mZC10ZXN0LXdvcmtlcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA5f4D +NErXtIW6GBJ5at/4HxJlQR0GFkaMErrKmYBmAxbolbBNux0lFTq6itNfZe+A61Gi +VdvBJPBaMWmS9iArQSjyQotTm04TpdiLThRVhG8drbqlAql+x5S6DrjUY61SVZhS +Aaa9IyKQQiKWH5v9oAb7n8r582qLTx8uZfq3ZcsCAwEAATANBgkqhkiG9w0BAQsF +AAOBgQA8WueokD59e0bzeGe1hPwuVXMnKoRHADbqbK/02KagoDBRnYTRRYM7hNDT +XMu++PHD0GkSfYs4XgX6BwdptaqQ0ww6BnX1OG1T+d9Ft/osneVyvkGv7fiGPF6D +5oXDz3xevUcKQzjux4P246bhWCIx/jrt8EVw5CXquS72qfGrXw== +-----END CERTIFICATE----- diff --git a/test/data/nfd-test-worker.key b/test/data/nfd-test-worker.key new file mode 100644 index 000000000..4f3ed3125 --- /dev/null +++ b/test/data/nfd-test-worker.key @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAOX+AzRK17SFuhgS +eWrf+B8SZUEdBhZGjBK6ypmAZgMW6JWwTbsdJRU6uorTX2XvgOtRolXbwSTwWjFp +kvYgK0Eo8kKLU5tOE6XYi04UVYRvHa26pQKpfseUug641GOtUlWYUgGmvSMikEIi +lh+b/aAG+5/K+fNqi08fLmX6t2XLAgMBAAECgYEAqsA7gMdP/iaKUvTkUASYIfl2 +UzFJI6CcvgsP/4bkNcb8RqXuD81Dis9fT1I+sV9vR0YET9onO1V2oNjQ0wpvETjO +bk5veRfqFLOTavl64pAPGLEvOTWHSHQ9rtFZst1FFfehB1Vw69nBs9E40Zo2Y9yv +gkK+RIKUc2oPqMOigQECQQD8k2jxRX1COF+GO+pBTOTAr3pAmd0ahWAQGoqLwteQ +x+ARRZss1nuX0IGEyJ89hD6dHLv/FhhUxGE1R0xdQ31JAkEA6Rw5VYrAphATPCCX +h2gboAbHTOFAzwjnlW1pU6nlZI89kDAD3TF8d+eq906J8y7ji0YE89/G4HEzHqtQ +vMsucwJBAId2VAlauJRkga8PwVKmd+Vz98BgBTqtH9ljMr1EkbK/0EfTKieBHSZO +GLjrlKQ8ogxHlfh4lDIaZPxbMfSvNqkCQDkEfEmeHK0BtZK5bhbisg8cWVdGqXF6 +fhqgnmimX8OO/cHs3KUX25gAhGLlRPzEdUe1orR8AcsYJSbVRHRJRl0CQQC7VBgp +04kaZzLFf61TmqMGVDoG2Wi5HwXYyzAEEEYFW61kwfZ6vuq3AP7NPMfW1F94welg +8LfkI2NBgjyKGiqn +-----END PRIVATE KEY-----