Skip to content

Commit

Permalink
Add test based on goexpect to ensure that nodeNetwork is configured
Browse files Browse the repository at this point in the history
Signed-off-by: Roman Mohr <rmohr@redhat.com>
  • Loading branch information
rmohr committed Oct 10, 2017
1 parent ef2ae63 commit 199bfb3
Show file tree
Hide file tree
Showing 6 changed files with 234 additions and 71 deletions.
20 changes: 17 additions & 3 deletions glide.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions glide.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,14 @@ import:
version: ~1.4.0
- package: github.com/onsi/gomega
version: ~1.2.0
- package: github.com/google/goexpect
- package: github.com/google/goterm
subpackages:
- term
- package: google.golang.org/grpc
version: ^1.6.0
subpackages:
- codes
testImport:
- package: github.com/elazarl/goproxy
version: 07b16b6e30fcac0ad8c0435548e743bcf2ca7e92
Expand Down
8 changes: 8 additions & 0 deletions pkg/kubecli/kubecli.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,11 @@ func GetKubevirtClientFromFlags(master string, kubeconfig string) (KubevirtClien
func GetKubevirtClient() (KubevirtClient, error) {
return GetKubevirtClientFromFlags(master, kubeconfig)
}

func GetKubevirtClientConfig() (*rest.Config, error) {
config, err := clientcmd.BuildConfigFromFlags(master, kubeconfig)
if err != nil {
return nil, err
}
return config, nil
}
156 changes: 88 additions & 68 deletions pkg/virtctl/console/console.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,106 +79,126 @@ func (c *Console) Run(flags *flag.FlagSet) int {
return 1
}

// Create a round tripper with all necessary kubernetes security details
wrappedRoundTripper, err := roundTripperFromConfig(config)
err = ConnectToConsole(config, namespace, vm, device, TerminalWebsocketCallback)
if err != nil {
log.Println(err)
return 1
}
return 0
}

func ConnectToConsole(config *rest.Config, namespace string, name string, console string, callback RoundTripCallback) error {

// Create a round tripper with all necessary kubernetes security details
wrappedRoundTripper, err := RoundTripperFromConfig(config, callback)
if err != nil {
return err
}

// Create the basic console request
req, err := requestFromConfig(config, vm, namespace, device)
req, err := RequestFromConfig(config, name, namespace, console)
if err != nil {
log.Println(err)
return 1
return err
}

// Do the call and process the websocket connection with the callback
_, err = wrappedRoundTripper.RoundTrip(req)

if err != nil {
log.Println(err)
return 1
return err
}
return 0
return nil
}

func WebsocketCallback(ws *websocket.Conn, resp *http.Response, err error) error {
func NewWebsocketCallback(in io.ReadCloser, out io.WriteCloser, stopChan chan struct{}) RoundTripCallback {

if err != nil {
if resp != nil && resp.StatusCode != http.StatusOK {
buf := new(bytes.Buffer)
buf.ReadFrom(resp.Body)
return fmt.Errorf("Can't connect to console (%d): %s\n", resp.StatusCode, buf.String())
return func(ws *websocket.Conn, resp *http.Response, err error) error {

if err != nil {
if resp != nil && resp.StatusCode != http.StatusOK {
buf := new(bytes.Buffer)
buf.ReadFrom(resp.Body)
return fmt.Errorf("Can't connect to console (%d): %s\n", resp.StatusCode, buf.String())
}
return fmt.Errorf("Can't connect to console: %s\n", err.Error())
}
return fmt.Errorf("Can't connect to console: %s\n", err.Error())
}

interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
writeStop := make(chan struct{})
readStop := make(chan struct{})

go func() {
defer close(readStop)
for {
_, message, err := ws.ReadMessage()
if err != nil {
out.Write(message)
return
}
out.Write(message)
}
}()

buf := make([]byte, 1024, 1024)
go func() {
defer close(writeStop)
for {
n, err := in.Read(buf)
if err != nil && err != io.EOF {
log.Println(err)
return
}

if buf[0] == 29 {
return
}

err = ws.WriteMessage(websocket.TextMessage, buf[0:n])
if err != nil && err != io.EOF {
log.Println(err)
return
}
}
}()

select {
case <-stopChan:
case <-readStop:
case <-writeStop:
}

writeStop := make(chan struct{})
readStop := make(chan struct{})
err = ws.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
if err != nil {
return fmt.Errorf("Error on close announcement: %s", err.Error())
}
select {
case <-readStop:
case <-time.After(time.Second):
}
return nil
}
}

func TerminalWebsocketCallback(ws *websocket.Conn, resp *http.Response, err error) error {
state, err := terminal.MakeRaw(int(os.Stdin.Fd()))
if err != nil {
return fmt.Errorf("Make raw terminal failed: %s", err)
}
defer terminal.Restore(int(os.Stdin.Fd()), state)
fmt.Fprint(os.Stderr, "Escape sequence is ^]")

go func() {
defer close(readStop)
for {
_, message, err := ws.ReadMessage()
if err != nil {
os.Stdout.Write(message)
return
}
os.Stdout.Write(message)
}
}()
stopChan := make(chan struct{}, 1)

buf := make([]byte, 1024, 1024)
go func() {
defer close(writeStop)
for {
n, err := os.Stdin.Read(buf)
if err != nil && err != io.EOF {
log.Println(err)
return
}

if buf[0] == 29 {
return
}

err = ws.WriteMessage(websocket.TextMessage, buf[0:n])
if err != nil && err != io.EOF {
log.Println(err)
return
}
}
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
<-interrupt
close(stopChan)
}()

select {
case <-interrupt:
case <-readStop:
case <-writeStop:
}

err = ws.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
if err != nil {
return fmt.Errorf("Error on close announcement: %s", err.Error())
}
select {
case <-readStop:
case <-time.After(time.Second):
}
return nil
return NewWebsocketCallback(os.Stdin, os.Stdout, stopChan)(ws, resp, err)
}

func requestFromConfig(config *rest.Config, vm string, namespace string, device string) (*http.Request, error) {
func RequestFromConfig(config *rest.Config, vm string, namespace string, device string) (*http.Request, error) {

u, err := url.Parse(config.Host)
if err != nil {
Expand Down Expand Up @@ -206,7 +226,7 @@ func requestFromConfig(config *rest.Config, vm string, namespace string, device
return req, nil
}

func roundTripperFromConfig(config *rest.Config) (http.RoundTripper, error) {
func RoundTripperFromConfig(config *rest.Config, callback RoundTripCallback) (http.RoundTripper, error) {

// Configure TLS
tlsConfig, err := rest.TLSConfigFor(config)
Expand All @@ -222,7 +242,7 @@ func roundTripperFromConfig(config *rest.Config) (http.RoundTripper, error) {

// Create a roundtripper which will pass in the final underlying websocket connection to a callback
rt := &WebsocketRoundTripper{
Do: WebsocketCallback,
Do: callback,
Dialer: dialer,
}

Expand Down
30 changes: 30 additions & 0 deletions tests/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,14 @@ import (

"k8s.io/apimachinery/pkg/api/resource"

"io"

"github.com/google/goexpect"
"k8s.io/client-go/rest"

"kubevirt.io/kubevirt/pkg/api/v1"
"kubevirt.io/kubevirt/pkg/kubecli"
"kubevirt.io/kubevirt/pkg/virtctl/console"
)

type EventType string
Expand Down Expand Up @@ -760,3 +766,27 @@ func NewRandomReplicaSetFromVM(vm *v1.VirtualMachine, replicas int32) *v1.Virtua
}
return rs
}

func NewConsoleExpecter(config *rest.Config, vm *v1.VirtualMachine, consoleName string, timeout time.Duration, opts ...expect.Option) (expect.Expecter, <-chan error, error) {
vmReader, vmWriter := io.Pipe()
expecterReader, expecterWriter := io.Pipe()
resCh := make(chan error)
stopChan := make(chan struct{})
go func() {
err := console.ConnectToConsole(config, vm.ObjectMeta.Namespace, vm.ObjectMeta.Name, consoleName, console.NewWebsocketCallback(vmReader, expecterWriter, stopChan))
resCh <- err
}()

return expect.SpawnGeneric(&expect.GenOptions{
In: vmWriter,
Out: expecterReader,
Wait: func() error {
return <-resCh
},
Close: func() error {
close(stopChan)
return nil
},
Check: func() bool { return true },
}, timeout, opts...)
}
Loading

0 comments on commit 199bfb3

Please sign in to comment.