<commit_before>package test

import (
	"io"
	"net"
	"testing"
	"time"

	"github.com/nbusy/devastator"
	"github.com/nbusy/neptulon/jsonrpc"
)

// ClientHelper is a JSON-RPC client wrapper.
// All the functions are wrapped with proper test runner error logging.
type ClientHelper struct {
	client    *jsonrpc.Client
	testing   *testing.T
	cert, key []byte
}

// NewClientHelper creates a new JSON-RPC client helper object.
func NewClientHelper(t *testing.T) *ClientHelper {
	if testing.Short() {
		t.Skip("Skipping integration test in short testing mode")
	}

	return &ClientHelper{testing: t}
}

// DefaultCert attaches default test client certificate to the connection.
func (c *ClientHelper) DefaultCert() *ClientHelper {
	c.cert = certChain.ClientCert
	c.key = certChain.ClientKey
	return c
}

// Cert attaches given PEM encoded client certificate to the connection.
func (c *ClientHelper) Cert(cert, key []byte) *ClientHelper {
	c.cert = cert
	c.key = key
	return c
}

// Dial initiates a connection.
func (c *ClientHelper) Dial() *ClientHelper {
	addr := "127.0.0.1:" + devastator.Conf.App.Port

	// retry connect in case we're operating on a very slow machine
	for i := 0; i <= 5; i++ {
		client, err := jsonrpc.Dial(addr, certChain.IntCACert, c.cert, c.key, false) // no need for debug mode on client conn as we have it on server conn already
		if err != nil {
			if operr, ok := err.(*net.OpError); ok && operr.Op == "dial" && operr.Err.Error() == "connection refused" {
				time.Sleep(time.Millisecond * 50)
				continue
			} else if i == 5 {
				c.testing.Fatalf("Cannot connect to server address %v after 5 retries, with error: %v", addr, err)
			}
			c.testing.Fatalf("Cannot connect to server address %v with error: %v", addr, err)
		}

		if i != 0 {
			c.testing.Logf("WARNING: it took %v retries to connect to the server, which might indicate code issues or slow machine.", i)
		}

		client.SetReadDeadline(10)
		c.client = client
		return c
	}

	return nil
}

// WriteRequest sends a request message through the client connection.
func (c *ClientHelper) WriteRequest(method string, params interface{}) (reqID string) {
	id, err := c.client.WriteRequest(method, params)
	if err != nil {
		c.testing.Fatal("Failed to write request to client connection:", err)
	}
	return id
}

// WriteNotification sends a notification message through the client connection.
func (c *ClientHelper) WriteNotification(method string, params interface{}) {
	if err := c.client.WriteNotification(method, params); err != nil {
		c.testing.Fatal("Failed to write notification to client connection:", err)
	}
}

// ReadMsg reads a JSON-RPC message from a client connection.
func (c *ClientHelper) ReadMsg(resultData interface{}) (req *jsonrpc.Request, res *jsonrpc.Response, not *jsonrpc.Notification) {
	req, res, not, err := c.client.ReadMsg(resultData)
	if err != nil {
		c.testing.Fatal("Failed to read message from client connection:", err)
	}

	return
}

// ReadRes reads a response object from a client connection. If incoming message is not a response, an error is logged.
func (c *ClientHelper) ReadRes(resultData interface{}) *jsonrpc.Response {
	_, res, _, err := c.client.ReadMsg(resultData)
	if err != nil {
		c.testing.Fatal("Failed to read response from client connection:", err)
	}

	return res
}

// VerifyConnClosed verifies that the connection is in closed state.
// Verification is done via reading from the channel and checking that returned error is io.EOF.
func (c *ClientHelper) VerifyConnClosed() bool {
	_, _, _, err := c.client.ReadMsg(nil)
	if err != io.EOF {
		return false
	}

	return true
}

// Close closes a client connection.
func (c *ClientHelper) Close() {
	if err := c.client.Close(); err != nil {
		c.testing.Fatal("Failed to close client connection:", err)
	}
}
<commit_msg>add variadic variations of write req/notification in ClientHelper<commit_after>package test

import (
	"io"
	"net"
	"testing"
	"time"

	"github.com/nbusy/devastator"
	"github.com/nbusy/neptulon/jsonrpc"
)

// ClientHelper is a JSON-RPC client wrapper.
// All the functions are wrapped with proper test runner error logging.
type ClientHelper struct {
	client    *jsonrpc.Client
	testing   *testing.T
	cert, key []byte
}

// NewClientHelper creates a new JSON-RPC client helper object.
func NewClientHelper(t *testing.T) *ClientHelper {
	if testing.Short() {
		t.Skip("Skipping integration test in short testing mode")
	}

	return &ClientHelper{testing: t}
}

// DefaultCert attaches default test client certificate to the connection.
func (c *ClientHelper) DefaultCert() *ClientHelper {
	c.cert = certChain.ClientCert
	c.key = certChain.ClientKey
	return c
}

// Cert attaches given PEM encoded client certificate to the connection.
func (c *ClientHelper) Cert(cert, key []byte) *ClientHelper {
	c.cert = cert
	c.key = key
	return c
}

// Dial initiates a connection.
func (c *ClientHelper) Dial() *ClientHelper {
	addr := "127.0.0.1:" + devastator.Conf.App.Port

	// retry connect in case we're operating on a very slow machine
	for i := 0; i <= 5; i++ {
		client, err := jsonrpc.Dial(addr, certChain.IntCACert, c.cert, c.key, false) // no need for debug mode on client conn as we have it on server conn already
		if err != nil {
			if operr, ok := err.(*net.OpError); ok && operr.Op == "dial" && operr.Err.Error() == "connection refused" {
				time.Sleep(time.Millisecond * 50)
				continue
			} else if i == 5 {
				c.testing.Fatalf("Cannot connect to server address %v after 5 retries, with error: %v", addr, err)
			}
			c.testing.Fatalf("Cannot connect to server address %v with error: %v", addr, err)
		}

		if i != 0 {
			c.testing.Logf("WARNING: it took %v retries to connect to the server, which might indicate code issues or slow machine.", i)
		}

		client.SetReadDeadline(10)
		c.client = client
		return c
	}

	return nil
}

// WriteRequest sends a request message through the client connection.
func (c *ClientHelper) WriteRequest(method string, params interface{}) (reqID string) {
	id, err := c.client.WriteRequest(method, params)
	if err != nil {
		c.testing.Fatal("Failed to write request to client connection:", err)
	}
	return id
}

// WriteRequestArr sends a request message through the client connection. Params object is variadic.
func (c *ClientHelper) WriteRequestArr(method string, params ...interface{}) (reqID string) {
	return c.WriteRequest(method, params)
}

// WriteNotification sends a notification message through the client connection.
func (c *ClientHelper) WriteNotification(method string, params interface{}) {
	if err := c.client.WriteNotification(method, params); err != nil {
		c.testing.Fatal("Failed to write notification to client connection:", err)
	}
}

// WriteNotificationArr sends a notification message through the client connection. Params object is variadic.
func (c *ClientHelper) WriteNotificationArr(method string, params ...interface{}) {
	c.WriteNotification(method, params)
}

// ReadMsg reads a JSON-RPC message from a client connection.
func (c *ClientHelper) ReadMsg(resultData interface{}) (req *jsonrpc.Request, res *jsonrpc.Response, not *jsonrpc.Notification) {
	req, res, not, err := c.client.ReadMsg(resultData)
	if err != nil {
		c.testing.Fatal("Failed to read message from client connection:", err)
	}

	return
}

// ReadRes reads a response object from a client connection. If incoming message is not a response, an error is logged.
func (c *ClientHelper) ReadRes(resultData interface{}) *jsonrpc.Response {
	_, res, _, err := c.client.ReadMsg(resultData)
	if err != nil {
		c.testing.Fatal("Failed to read response from client connection:", err)
	}

	return res
}

// VerifyConnClosed verifies that the connection is in closed state.
// Verification is done via reading from the channel and checking that returned error is io.EOF.
func (c *ClientHelper) VerifyConnClosed() bool {
	_, _, _, err := c.client.ReadMsg(nil)
	if err != io.EOF {
		return false
	}

	return true
}

// Close closes a client connection.
func (c *ClientHelper) Close() {
	if err := c.client.Close(); err != nil {
		c.testing.Fatal("Failed to close client connection:", err)
	}
}
<|endoftext|>


<commit_before>package util

// STACK ATTACK
// From the wild internet(s)

type Stack struct {
	top  *Element
	size int
}

type Element struct {
	value interface{}
	next  *Element
}

// Return the stack's length
func (s *Stack) Len() int {
	return s.size
}

// Push a new element onto the stack
func (s *Stack) Push(value interface{}) {
	s.top = &Element{value, s.top}
	s.size++
}

// Remove the top element from the stack and return it's value
// If the stack is empty, return nil
func (s *Stack) Pop() (value interface{}) {
	if s.size > 0 {
		value, s.top = s.top.value, s.top.next
		s.size--
		return
	}
	return nil
}
<commit_msg>Remove stack<commit_after><|endoftext|>


<commit_before>package util

import (
	"errors"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"time"

	"github.com/google/go-github/github"
)

// Adapted from github.com/apex/apex

var UpdateAvailable *github.RepositoryRelease

func DoUpgrade(version string) error {
	fmt.Printf("current release is v%s\n", version)

	if err := CheckUpdate(version); err != nil || UpdateAvailable == nil {
		return err
	}

	asset := findAsset(UpdateAvailable)
	if asset == nil {
		return errors.New("cannot find binary for your system")
	}

	// create tmp file
	tmpPath := filepath.Join(os.TempDir(), "semaphore-upgrade")
	f, err := os.OpenFile(tmpPath, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0755)
	if err != nil {
		return err
	}

	// download binary
	fmt.Printf("downloading %s\n", *asset.BrowserDownloadURL)
	res, err := http.Get(*asset.BrowserDownloadURL)
	if err != nil {
		return err
	}
	defer res.Body.Close()

	// copy it down
	_, err = io.Copy(f, res.Body)
	if err != nil {
		return err
	}

	// replace it
	cmdPath := FindSemaphore()
	if len(cmdPath) == 0 {
		return errors.New("Cannot find semaphore binary")
	}

	fmt.Printf("replacing %s\n", cmdPath)
	err = os.Rename(tmpPath, cmdPath)
	if err != nil {
		return err
	}

	fmt.Println("visit https://github.com/ansible-semaphore/semaphore/releases for the changelog")
	go func() {
		time.Sleep(time.Second * 3)
		os.Exit(0)
	}()

	return nil
}

func FindSemaphore() string {
	cmdPath, _ := exec.LookPath("semaphore")

	if len(cmdPath) == 0 {
		cmdPath, _ = filepath.Abs(os.Args[0])
	}

	return cmdPath
}

// findAsset returns the binary for this platform.
func findAsset(release *github.RepositoryRelease) *github.ReleaseAsset {
	for _, asset := range release.Assets {
		if *asset.Name == fmt.Sprintf("semaphore_%s_%s", runtime.GOOS, runtime.GOARCH) {
			return &asset
		}
	}

	return nil
}

func CheckUpdate(version string) error {
	// fetch releases
	gh := github.NewClient(nil)
	releases, _, err := gh.Repositories.ListReleases("ansible-semaphore", "semaphore", nil)
	if err != nil {
		return err
	}

	UpdateAvailable = nil
	if (*releases[0].TagName)[1:] != version {
		UpdateAvailable = releases[0]
	}

	return nil
}
<commit_msg>fix api breaking of google/go-github<commit_after>package util

import (
	"errors"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"time"
	"context"

	"github.com/google/go-github/github"
)

// Adapted from github.com/apex/apex

var UpdateAvailable *github.RepositoryRelease

func DoUpgrade(version string) error {
	fmt.Printf("current release is v%s\n", version)

	if err := CheckUpdate(version); err != nil || UpdateAvailable == nil {
		return err
	}

	asset := findAsset(UpdateAvailable)
	if asset == nil {
		return errors.New("cannot find binary for your system")
	}

	// create tmp file
	tmpPath := filepath.Join(os.TempDir(), "semaphore-upgrade")
	f, err := os.OpenFile(tmpPath, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0755)
	if err != nil {
		return err
	}

	// download binary
	fmt.Printf("downloading %s\n", *asset.BrowserDownloadURL)
	res, err := http.Get(*asset.BrowserDownloadURL)
	if err != nil {
		return err
	}
	defer res.Body.Close()

	// copy it down
	_, err = io.Copy(f, res.Body)
	if err != nil {
		return err
	}

	// replace it
	cmdPath := FindSemaphore()
	if len(cmdPath) == 0 {
		return errors.New("Cannot find semaphore binary")
	}

	fmt.Printf("replacing %s\n", cmdPath)
	err = os.Rename(tmpPath, cmdPath)
	if err != nil {
		return err
	}

	fmt.Println("visit https://github.com/ansible-semaphore/semaphore/releases for the changelog")
	go func() {
		time.Sleep(time.Second * 3)
		os.Exit(0)
	}()

	return nil
}

func FindSemaphore() string {
	cmdPath, _ := exec.LookPath("semaphore")

	if len(cmdPath) == 0 {
		cmdPath, _ = filepath.Abs(os.Args[0])
	}

	return cmdPath
}

// findAsset returns the binary for this platform.
func findAsset(release *github.RepositoryRelease) *github.ReleaseAsset {
	for _, asset := range release.Assets {
		if *asset.Name == fmt.Sprintf("semaphore_%s_%s", runtime.GOOS, runtime.GOARCH) {
			return &asset
		}
	}

	return nil
}

func CheckUpdate(version string) error {
	// fetch releases
	gh := github.NewClient(nil)
	releases, _, err := gh.Repositories.ListReleases(context.TODO(), "ansible-semaphore", "semaphore", nil)
	if err != nil {
		return err
	}

	UpdateAvailable = nil
	if (*releases[0].TagName)[1:] != version {
		UpdateAvailable = releases[0]
	}

	return nil
}
<|endoftext|>


<commit_before>package utils

import (
	"context"
	"net"
	"sync"
	"time"
)

var connPool sync.Pool

type netConn net.Conn
type netDialer net.Dialer

// Dialer is wrapped dialer provided by qingstor go sdk.
//
// We provide this dialer wrapper ReadTimeout & WriteTimeout attributes into connection object.
// This timeout is for individual buffer I/O operation like other language (python, perl... etc),
// so don't bother with SetDeadline or stupid nethttp.Client timeout.
type Dialer struct {
	*net.Dialer
	ReadTimeout  time.Duration
	WriteTimeout time.Duration
}

// NewDialer will create a new dialer.
func NewDialer(connTimeout, readTimeout, writeTimeout time.Duration) *Dialer {
	d := &net.Dialer{
		DualStack: false,
		Timeout:   connTimeout,
	}
	return &Dialer{d, readTimeout, writeTimeout}
}

// Dial connects to the address on the named network.
func (d *Dialer) Dial(network, addr string) (net.Conn, error) {
	c, err := d.Dialer.Dial(network, addr)
	if err != nil {
		return nil, err
	}
	conn := NewConn(c)
	conn.readTimeout = d.ReadTimeout
	conn.writeTimeout = d.WriteTimeout
	return conn, nil
}

// DialContext connects to the address on the named network using
// the provided context.
func (d *Dialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
	c, err := d.Dialer.DialContext(ctx, network, addr)
	if err != nil {
		return nil, err
	}
	conn := NewConn(c)
	conn.readTimeout = d.ReadTimeout
	conn.writeTimeout = d.WriteTimeout
	return conn, nil
}

// Conn is a generic stream-oriented network connection.
type Conn struct {
	netConn
	readTimeout  time.Duration
	writeTimeout time.Duration
	timeoutFunc  func() bool
}

// NewConn will create a new conn.
func NewConn(c netConn) *Conn {
	conn, ok := c.(*Conn)
	if ok {
		return conn
	}
	conn, ok = connPool.Get().(*Conn)
	if !ok {
		conn = new(Conn)
	}
	conn.netConn = c
	return conn
}

// SetReadTimeout will set the conn's read timeout.
func (c *Conn) SetReadTimeout(d time.Duration) {
	c.readTimeout = d
}

// SetWriteTimeout will set the conn's write timeout.
func (c *Conn) SetWriteTimeout(d time.Duration) {
	c.writeTimeout = d
}

// Read will read from the conn.
func (c Conn) Read(buf []byte) (n int, err error) {
	if c.readTimeout > 0 {
		c.SetDeadline(time.Now().Add(c.readTimeout))
	}
	n, err = c.netConn.Read(buf)
	if c.readTimeout > 0 {
		c.SetDeadline(time.Time{}) // clear timeout
	}
	return
}

// Write will write into the conn.
func (c Conn) Write(buf []byte) (n int, err error) {
	if c.writeTimeout > 0 {
		c.SetDeadline(time.Now().Add(c.writeTimeout))
	}
	n, err = c.netConn.Write(buf)
	if c.writeTimeout > 0 {
		c.SetDeadline(time.Time{})
	}
	return
}

// Close will close the conn.
func (c Conn) Close() (err error) {
	if c.netConn == nil {
		return nil
	}
	err = c.netConn.Close()
	connPool.Put(c)
	c.netConn = nil
	c.readTimeout = 0
	c.writeTimeout = 0
	return
}

// IsTimeoutError will check whether the err is a timeout error.
func IsTimeoutError(err error) bool {
	e, ok := err.(net.Error)
	if ok {
		return e.Timeout()
	}
	return false
}
<commit_msg>Remove sync.Pool usage from our net.Conn wrapper<commit_after>package utils

import (
	"context"
	"net"
	"time"
)

type netConn net.Conn
type netDialer net.Dialer

// Dialer is wrapped dialer provided by qingstor go sdk.
//
// We provide this dialer wrapper ReadTimeout & WriteTimeout attributes into connection object.
// This timeout is for individual buffer I/O operation like other language (python, perl... etc),
// so don't bother with SetDeadline or stupid nethttp.Client timeout.
type Dialer struct {
	*net.Dialer
	ReadTimeout  time.Duration
	WriteTimeout time.Duration
}

// NewDialer will create a new dialer.
func NewDialer(connTimeout, readTimeout, writeTimeout time.Duration) *Dialer {
	d := &net.Dialer{
		DualStack: false,
		Timeout:   connTimeout,
	}
	return &Dialer{d, readTimeout, writeTimeout}
}

// Dial connects to the address on the named network.
func (d *Dialer) Dial(network, addr string) (net.Conn, error) {
	c, err := d.Dialer.Dial(network, addr)
	if err != nil {
		return nil, err
	}
	conn := NewConn(c)
	conn.readTimeout = d.ReadTimeout
	conn.writeTimeout = d.WriteTimeout
	return conn, nil
}

// DialContext connects to the address on the named network using
// the provided context.
func (d *Dialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
	c, err := d.Dialer.DialContext(ctx, network, addr)
	if err != nil {
		return nil, err
	}
	conn := NewConn(c)
	conn.readTimeout = d.ReadTimeout
	conn.writeTimeout = d.WriteTimeout
	return conn, nil
}

// Conn is a generic stream-oriented network connection.
type Conn struct {
	netConn
	readTimeout  time.Duration
	writeTimeout time.Duration
	timeoutFunc  func() bool
}

// NewConn will create a new conn.
func NewConn(c netConn) *Conn {
	conn, ok := c.(*Conn)
	if ok {
		return conn
	}
	conn = new(Conn)
	conn.netConn = c
	return conn
}

// SetReadTimeout will set the conn's read timeout.
func (c *Conn) SetReadTimeout(d time.Duration) {
	c.readTimeout = d
}

// SetWriteTimeout will set the conn's write timeout.
func (c *Conn) SetWriteTimeout(d time.Duration) {
	c.writeTimeout = d
}

// Read will read from the conn.
func (c Conn) Read(buf []byte) (n int, err error) {
	if c.readTimeout > 0 {
		c.SetDeadline(time.Now().Add(c.readTimeout))
	}
	n, err = c.netConn.Read(buf)
	if c.readTimeout > 0 {
		c.SetDeadline(time.Time{}) // clear timeout
	}
	return
}

// Write will write into the conn.
func (c Conn) Write(buf []byte) (n int, err error) {
	if c.writeTimeout > 0 {
		c.SetDeadline(time.Now().Add(c.writeTimeout))
	}
	n, err = c.netConn.Write(buf)
	if c.writeTimeout > 0 {
		c.SetDeadline(time.Time{})
	}
	return
}

// Close will close the conn.
func (c Conn) Close() (err error) {
	if c.netConn == nil {
		return nil
	}
	err = c.netConn.Close()
	c.netConn = nil
	c.readTimeout = 0
	c.writeTimeout = 0
	return
}

// IsTimeoutError will check whether the err is a timeout error.
func IsTimeoutError(err error) bool {
	e, ok := err.(net.Error)
	if ok {
		return e.Timeout()
	}
	return false
}
<|endoftext|>


<commit_before>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build windows

// Package terminal provides support functions for dealing with terminals, as
// commonly found on UNIX systems.
//
// Putting a terminal into raw mode is the most common requirement:
//
// 	oldState, err := terminal.MakeRaw(0)
// 	if err != nil {
// 	        panic(err)
// 	}
// 	defer terminal.Restore(0, oldState)
package terminal

import (
	"os"

	"golang.org/x/sys/windows"
)

type State struct {
	mode uint32
}

// IsTerminal returns whether the given file descriptor is a terminal.
func IsTerminal(fd int) bool {
	var st uint32
	err := windows.GetConsoleMode(windows.Handle(fd), &st)
	return err == nil
}

// MakeRaw put the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
func MakeRaw(fd int) (*State, error) {
	var st uint32
	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
		return nil, err
	}
	raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
	if err := windows.SetConsoleMode(windows.Handle(fd), raw); err != nil {
		return nil, err
	}
	return &State{st}, nil
}

// GetState returns the current state of a terminal which may be useful to
// restore the terminal after a signal.
func GetState(fd int) (*State, error) {
	var st uint32
	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
		return nil, err
	}
	return &State{st}, nil
}

// Restore restores the terminal connected to the given file descriptor to a
// previous state.
func Restore(fd int, state *State) error {
	return windows.SetConsoleMode(windows.Handle(fd), state.mode)
}

// GetSize returns the visible dimensions of the given terminal.
//
// These dimensions don't include any scrollback buffer height.
func GetSize(fd int) (width, height int, err error) {
	var info windows.ConsoleScreenBufferInfo
	if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil {
		return 0, 0, err
	}
	return int(info.Window.Right - info.Window.Left + 1), int(info.Window.Bottom - info.Window.Top + 1), nil
}

// ReadPassword reads a line of input from a terminal without local echo.  This
// is commonly used for inputting passwords and other sensitive data. The slice
// returned does not include the \n.
func ReadPassword(fd int) ([]byte, error) {
	var st uint32
	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
		return nil, err
	}
	old := st

	st &^= (windows.ENABLE_ECHO_INPUT)
	st |= (windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
	if err := windows.SetConsoleMode(windows.Handle(fd), st); err != nil {
		return nil, err
	}

	defer windows.SetConsoleMode(windows.Handle(fd), old)

	var h windows.Handle
	p, _ := windows.GetCurrentProcess()
	if err := windows.DuplicateHandle(p, windows.Handle(fd), p, &h, 0, false, windows.DUPLICATE_SAME_ACCESS); err != nil {
		return nil, err
	}

	f := os.NewFile(uintptr(h), "stdin")
	defer f.Close()
	return readPasswordLine(f)
}
<commit_msg>ssh/terminal: account for win32 api changes<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build windows

// Package terminal provides support functions for dealing with terminals, as
// commonly found on UNIX systems.
//
// Putting a terminal into raw mode is the most common requirement:
//
// 	oldState, err := terminal.MakeRaw(0)
// 	if err != nil {
// 	        panic(err)
// 	}
// 	defer terminal.Restore(0, oldState)
package terminal

import (
	"os"

	"golang.org/x/sys/windows"
)

type State struct {
	mode uint32
}

// IsTerminal returns whether the given file descriptor is a terminal.
func IsTerminal(fd int) bool {
	var st uint32
	err := windows.GetConsoleMode(windows.Handle(fd), &st)
	return err == nil
}

// MakeRaw put the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
func MakeRaw(fd int) (*State, error) {
	var st uint32
	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
		return nil, err
	}
	raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
	if err := windows.SetConsoleMode(windows.Handle(fd), raw); err != nil {
		return nil, err
	}
	return &State{st}, nil
}

// GetState returns the current state of a terminal which may be useful to
// restore the terminal after a signal.
func GetState(fd int) (*State, error) {
	var st uint32
	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
		return nil, err
	}
	return &State{st}, nil
}

// Restore restores the terminal connected to the given file descriptor to a
// previous state.
func Restore(fd int, state *State) error {
	return windows.SetConsoleMode(windows.Handle(fd), state.mode)
}

// GetSize returns the visible dimensions of the given terminal.
//
// These dimensions don't include any scrollback buffer height.
func GetSize(fd int) (width, height int, err error) {
	var info windows.ConsoleScreenBufferInfo
	if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil {
		return 0, 0, err
	}
	return int(info.Window.Right - info.Window.Left + 1), int(info.Window.Bottom - info.Window.Top + 1), nil
}

// ReadPassword reads a line of input from a terminal without local echo.  This
// is commonly used for inputting passwords and other sensitive data. The slice
// returned does not include the \n.
func ReadPassword(fd int) ([]byte, error) {
	var st uint32
	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
		return nil, err
	}
	old := st

	st &^= (windows.ENABLE_ECHO_INPUT)
	st |= (windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
	if err := windows.SetConsoleMode(windows.Handle(fd), st); err != nil {
		return nil, err
	}

	defer windows.SetConsoleMode(windows.Handle(fd), old)

	var h windows.Handle
	if err := windows.DuplicateHandle(windows.GetCurrentProcess(), windows.Handle(fd), windows.GetCurrentProcess(), &h, 0, false, windows.DUPLICATE_SAME_ACCESS); err != nil {
		return nil, err
	}

	f := os.NewFile(uintptr(h), "stdin")
	defer f.Close()
	return readPasswordLine(f)
}
<|endoftext|>


<commit_before>// The MIT License (MIT)

// Copyright (c) 2014 William Miller

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package fedops_runtime

import (
	// Standard
	"bytes"
	"crypto/tls"
	"encoding/gob"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"regexp"
	"time"
	// 3rd Party
	"golang.org/x/crypto/bcrypt"
	// FedOps
	"github.com/wmiller848/Fedops/lib/encryption"
	"github.com/wmiller848/Fedops/lib/engine/container"
	"github.com/wmiller848/Fedops/lib/engine/network"
)

const (
	KeyFileName    string = ".fedops-key"
	ConfigFileName string = "Fedops-Runtime"
)

type FedopsEventHandle func(event *FedopsEvent)

type FedopsEvent struct {
	ID         string
	Handle     FedopsEventHandle
	Persistant bool
	Time       time.Time
}

//
//
type RuntimeError struct {
	msg string
}

func (err RuntimeError) Error() string {
	return err.msg
}

//
// This config is stored encrypted on disk
type ClusterConfig struct {
	ClusterID  string
	MemberID   string
	Created    string
	Modified   string
	Cert       fedops_encryption.Cert
	Containers map[string]fedops_container.Container
}

type Runtime struct {
	Cipherkey      []byte
	Version        string
	PowerDirectory string
	Config         ClusterConfig
	Routes         []fedops_network.FedopsRoute
	Events         []FedopsEvent
}

func (r *Runtime) Configure(pwd string) error {
	if !r.HasKeyFile(pwd) {
		return RuntimeError{msg: "No key file located in " + pwd}
	}

	if !r.HasConfigFile(pwd) {
		return RuntimeError{msg: "No config file located in " + pwd}
	}

	cipherkey, err := r.GetKeyFile(pwd)
	if err != nil {
		//  We couldn't find the key file :(
		return err
	}

	config, err := r.Load(cipherkey, pwd)
	if err != nil {
		return err
	}

	r.Cipherkey = fedops_encryption.Encode(cipherkey)
	r.Config = config
	r.Version = "0.0.1"
	r.PowerDirectory = pwd

	if r.Config.Containers == nil {
		r.Config.Containers = make(map[string]fedops_container.Container)
	}

	return nil
}

func (r *Runtime) HasKeyFile(pwd string) bool {
	_, err := os.Stat(pwd + "/" + KeyFileName)
	if err != nil {
		return false
	}
	return true
}

func (r *Runtime) GetKeyFile(pwd string) ([]byte, error) {
	return ioutil.ReadFile(pwd + "/" + KeyFileName)
}

func (r *Runtime) HasConfigFile(pwd string) bool {
	_, err := os.Stat(pwd + "/" + ConfigFileName)
	if err != nil {
		return false
	}
	return true
}

func (r *Runtime) GetConfigFile(pwd string) ([]byte, error) {
	return ioutil.ReadFile(pwd + "/" + ConfigFileName)
}

//
//
func (r *Runtime) Error() *RuntimeError {
	return &RuntimeError{}
}

func (r *Runtime) Info() {
	fmt.Println("[WARNING] Fedops encrypts all information you provide to it...")
	fmt.Println("[WARNING] Fedops data is UNRECOVERABLE without knowning the encryption key")
}

func (r *Runtime) Load(cipherkey []byte, pwd string) (ClusterConfig, error) {
	var config ClusterConfig
	cdata, err := r.GetConfigFile(pwd)
	if err != nil {
		//  We couldn't find the config file :(
		return config, err
	}

	// We found the config, now unencrypt it, base64 decode it, and then marshal from json
	decrypted, err := fedops_encryption.Decrypt(cipherkey, cdata)
	if err != nil {
		return config, err
	}
	decoder := json.NewDecoder(bytes.NewBuffer(decrypted))
	err = decoder.Decode(&config)
	if err != nil {
		return config, err
	}

	return config, nil
}

func (r *Runtime) Unload() bool {
	now := time.Now()
	r.Config.Modified = now.UTC().String()

	pwd := r.PowerDirectory
	disjson, err := json.Marshal(r.Config)
	if err != nil {
		fmt.Println(err.Error())
		return false
	}
	cipherkey, err := fedops_encryption.Decode(r.Cipherkey)
	if err != nil {
		fmt.Println(err.Error())
		return false
	}

	encrypted, err := fedops_encryption.Encrypt(cipherkey, disjson)
	if err != nil {
		fmt.Println(err.Error())
		return false
	}

	err = ioutil.WriteFile(pwd+"/"+ConfigFileName, encrypted, 0666)
	if err != nil {
		fmt.Println(err.Error())
		return false
	}
	return true
}

func (r *Runtime) UnloadToMemory() []byte {
	now := time.Now()
	r.Config.Modified = now.UTC().String()

	disjson, err := json.Marshal(r.Config)
	if err != nil {
		fmt.Println(err.Error())
		return nil
	}
	cipherkey, err := fedops_encryption.Decode(r.Cipherkey)
	if err != nil {
		fmt.Println(err.Error())
		return nil
	}

	encrypted, err := fedops_encryption.Encrypt(cipherkey, disjson)
	if err != nil {
		fmt.Println(err.Error())
		return nil
	}

	return encrypted
}

func (r *Runtime) AddRoute(method uint, route string, handle fedops_network.HandleRoute) error {
	rgx, err := regexp.Compile(route)
	if err != nil {
		return err
	}
	fedRoute := fedops_network.FedopsRoute{
		Method: method,
		Route:  rgx,
		Handle: handle,
	}
	r.Routes = append(r.Routes, fedRoute)
	return nil
}

// Handles incoming requests.
func (r *Runtime) HandleConnection(conn net.Conn) {
	fmt.Println("HandleConnection")
	// Make a buffer to hold incoming data.
	// buf := make([]byte, 1024)
	// Read the incoming connection into the buffer.
	// reqLen, err := conn.Read(buf)
	// if err != nil {
	//   fmt.Println("Error reading:", err.Error())
	//   return
	// }
	// if reqLen > 0 {
	//   fmt.Println(buf)
	// }
	// Send a response back to person contacting us.
	// conn.Write([]byte("Message received"))
	defer conn.Close()
	dec := gob.NewDecoder(conn)
	var req fedops_network.FedopsRequest
	err := dec.Decode(&req)
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	res := fedops_network.FedopsResponse{
		Success: true,
	}

	err = bcrypt.CompareHashAndPassword(req.Authorization, []byte(r.Config.ClusterID))
	if err != nil {
		fmt.Println("Authorization not accepted", err.Error())
		return
	} else {
		fmt.Println("Authorization accepted")
		fmt.Println("Method", req.Method)
		fmt.Println("Route", string(req.Route))
		fmt.Println("Data", string(req.Data))

		for i := range r.Routes {
			if r.Routes[i].Method == req.Method && r.Routes[i].Route.Match(req.Route) {
				err = r.Routes[i].Handle(&req, &res)
				if err != nil {
					res.Success = false
					res.Error = []byte(err.Error())
					fmt.Println(err.Error())
				}
				break
			}
		}
	}

	enc := gob.NewEncoder(conn)
	err = enc.Encode(res)
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	persisted := r.Unload()
	if persisted != true {
		fmt.Println("Error saving to disk")
	}
	// conn.Write([]byte("ok"))
}

func (r *Runtime) Listen(status chan error) {
	fmt.Println("Listen")
	fed_cert := r.Config.Cert
	// cert, err := tls.LoadX509KeyPair("./cert.pem", "./key.pem")
	cert, err := tls.X509KeyPair(fed_cert.CertificatePem, fed_cert.PrivatePem)
	if err != nil {
		status <- err
		return
	}

	config := tls.Config{
		Certificates:             []tls.Certificate{cert},
		PreferServerCipherSuites: true,
		SessionTicketsDisabled:   true,
		CipherSuites: []uint16{
			tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
		},
		CurvePreferences: []tls.CurveID{tls.CurveP521},
		MinVersion:       tls.VersionTLS12,
		MaxVersion:       tls.VersionTLS12,
	}
	listener, err := tls.Listen("tcp", ":13371", &config)
	if err != nil {
		status <- err
		return
	}

	fmt.Println("Starting Listener Loop")
	for {
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println(err.Error())
			break
		}
		fmt.Println(conn.RemoteAddr(), "Connected")
		go r.HandleConnection(conn)
	}
}

func (r *Runtime) StartEventEngine(status chan error) {
	fmt.Println("StartEventEngine Loop")
	for {
		var event FedopsEvent
		l := len(r.Events)
		if l > 0 {
			fmt.Println("Events -", l)
			event, r.Events = r.Events[l-1], r.Events[:l-1]
			ftime := event.Time.Add(2 * time.Second)
			n := time.Now()
			if n.After(ftime) {
				event.Time = n
				fmt.Println("Running Event Handle")
				go event.Handle(&event)
				if event.Persistant {
					events := r.Events
					r.Events = make([]FedopsEvent, l)
					r.Events = append(r.Events, event)
					r.Events = append(r.Events, events[:l-1]...)
				}
			}
		}
		time.Sleep(500 * time.Millisecond)
	}
}
<commit_msg>some debug logs<commit_after>// The MIT License (MIT)

// Copyright (c) 2014 William Miller

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package fedops_runtime

import (
	// Standard
	"bytes"
	"crypto/tls"
	"encoding/gob"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"regexp"
	"time"
	// 3rd Party
	"golang.org/x/crypto/bcrypt"
	// FedOps
	"github.com/wmiller848/Fedops/lib/encryption"
	"github.com/wmiller848/Fedops/lib/engine/container"
	"github.com/wmiller848/Fedops/lib/engine/network"
)

const (
	KeyFileName    string = ".fedops-key"
	ConfigFileName string = "Fedops-Runtime"
)

type FedopsEventHandle func(event *FedopsEvent)

type FedopsEvent struct {
	ID         string
	Handle     FedopsEventHandle
	Persistant bool
	Time       time.Time
}

//
//
type RuntimeError struct {
	msg string
}

func (err RuntimeError) Error() string {
	return err.msg
}

//
// This config is stored encrypted on disk
type ClusterConfig struct {
	ClusterID  string
	MemberID   string
	Created    string
	Modified   string
	Cert       fedops_encryption.Cert
	Containers map[string]fedops_container.Container
}

type Runtime struct {
	Cipherkey      []byte
	Version        string
	PowerDirectory string
	Config         ClusterConfig
	Routes         []fedops_network.FedopsRoute
	Events         []FedopsEvent
}

func (r *Runtime) Configure(pwd string) error {
	if !r.HasKeyFile(pwd) {
		return RuntimeError{msg: "No key file located in " + pwd}
	}

	if !r.HasConfigFile(pwd) {
		return RuntimeError{msg: "No config file located in " + pwd}
	}

	cipherkey, err := r.GetKeyFile(pwd)
	if err != nil {
		//  We couldn't find the key file :(
		return err
	}

	config, err := r.Load(cipherkey, pwd)
	if err != nil {
		return err
	}

	r.Cipherkey = fedops_encryption.Encode(cipherkey)
	r.Config = config
	r.Version = "0.0.1"
	r.PowerDirectory = pwd

	if r.Config.Containers == nil {
		r.Config.Containers = make(map[string]fedops_container.Container)
	}

	return nil
}

func (r *Runtime) HasKeyFile(pwd string) bool {
	_, err := os.Stat(pwd + "/" + KeyFileName)
	if err != nil {
		return false
	}
	return true
}

func (r *Runtime) GetKeyFile(pwd string) ([]byte, error) {
	return ioutil.ReadFile(pwd + "/" + KeyFileName)
}

func (r *Runtime) HasConfigFile(pwd string) bool {
	_, err := os.Stat(pwd + "/" + ConfigFileName)
	if err != nil {
		return false
	}
	return true
}

func (r *Runtime) GetConfigFile(pwd string) ([]byte, error) {
	return ioutil.ReadFile(pwd + "/" + ConfigFileName)
}

//
//
func (r *Runtime) Error() *RuntimeError {
	return &RuntimeError{}
}

func (r *Runtime) Info() {
	fmt.Println("[WARNING] Fedops encrypts all information you provide to it...")
	fmt.Println("[WARNING] Fedops data is UNRECOVERABLE without knowning the encryption key")
}

func (r *Runtime) Load(cipherkey []byte, pwd string) (ClusterConfig, error) {
	var config ClusterConfig
	cdata, err := r.GetConfigFile(pwd)
	if err != nil {
		//  We couldn't find the config file :(
		return config, err
	}

	// We found the config, now unencrypt it, base64 decode it, and then marshal from json
	decrypted, err := fedops_encryption.Decrypt(cipherkey, cdata)
	if err != nil {
		return config, err
	}
	decoder := json.NewDecoder(bytes.NewBuffer(decrypted))
	err = decoder.Decode(&config)
	if err != nil {
		return config, err
	}

	return config, nil
}

func (r *Runtime) Unload() bool {
	now := time.Now()
	r.Config.Modified = now.UTC().String()

	pwd := r.PowerDirectory
	disjson, err := json.Marshal(r.Config)
	if err != nil {
		fmt.Println(err.Error())
		return false
	}
	cipherkey, err := fedops_encryption.Decode(r.Cipherkey)
	if err != nil {
		fmt.Println(err.Error())
		return false
	}

	encrypted, err := fedops_encryption.Encrypt(cipherkey, disjson)
	if err != nil {
		fmt.Println(err.Error())
		return false
	}

	err = ioutil.WriteFile(pwd+"/"+ConfigFileName, encrypted, 0666)
	if err != nil {
		fmt.Println(err.Error())
		return false
	}
	return true
}

func (r *Runtime) UnloadToMemory() []byte {
	now := time.Now()
	r.Config.Modified = now.UTC().String()

	disjson, err := json.Marshal(r.Config)
	if err != nil {
		fmt.Println(err.Error())
		return nil
	}
	cipherkey, err := fedops_encryption.Decode(r.Cipherkey)
	if err != nil {
		fmt.Println(err.Error())
		return nil
	}

	encrypted, err := fedops_encryption.Encrypt(cipherkey, disjson)
	if err != nil {
		fmt.Println(err.Error())
		return nil
	}

	return encrypted
}

func (r *Runtime) AddRoute(method uint, route string, handle fedops_network.HandleRoute) error {
	rgx, err := regexp.Compile(route)
	if err != nil {
		return err
	}
	fedRoute := fedops_network.FedopsRoute{
		Method: method,
		Route:  rgx,
		Handle: handle,
	}
	r.Routes = append(r.Routes, fedRoute)
	return nil
}

// Handles incoming requests.
func (r *Runtime) HandleConnection(conn net.Conn) {
	fmt.Println("HandleConnection")
	// Make a buffer to hold incoming data.
	// buf := make([]byte, 1024)
	// Read the incoming connection into the buffer.
	// reqLen, err := conn.Read(buf)
	// if err != nil {
	//   fmt.Println("Error reading:", err.Error())
	//   return
	// }
	// if reqLen > 0 {
	//   fmt.Println(buf)
	// }
	// Send a response back to person contacting us.
	// conn.Write([]byte("Message received"))
	defer conn.Close()
	dec := gob.NewDecoder(conn)
	var req fedops_network.FedopsRequest
	err := dec.Decode(&req)
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	res := fedops_network.FedopsResponse{
		Success: true,
	}

	err = bcrypt.CompareHashAndPassword(req.Authorization, []byte(r.Config.ClusterID))
	if err != nil {
		fmt.Println("Authorization not accepted", err.Error())
		return
	} else {
		fmt.Println("Authorization accepted")
		fmt.Println("Method", req.Method)
		fmt.Println("Route", string(req.Route))
		fmt.Println("Data", string(req.Data))

		for i := range r.Routes {
			if r.Routes[i].Method == req.Method && r.Routes[i].Route.Match(req.Route) {
				err = r.Routes[i].Handle(&req, &res)
				if err != nil {
					res.Success = false
					res.Error = []byte(err.Error())
					fmt.Println(err.Error())
				}
				break
			}
		}
	}

	enc := gob.NewEncoder(conn)
	err = enc.Encode(res)
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	persisted := r.Unload()
	if persisted != true {
		fmt.Println("Error saving to disk")
	}
	// conn.Write([]byte("ok"))
}

func (r *Runtime) Listen(status chan error) {
	fmt.Println("Listen")
	fed_cert := r.Config.Cert
	// cert, err := tls.LoadX509KeyPair("./cert.pem", "./key.pem")
	cert, err := tls.X509KeyPair(fed_cert.CertificatePem, fed_cert.PrivatePem)
	if err != nil {
		status <- err
		return
	}

	config := tls.Config{
		Certificates:             []tls.Certificate{cert},
		PreferServerCipherSuites: true,
		SessionTicketsDisabled:   true,
		CipherSuites: []uint16{
			tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
		},
		CurvePreferences: []tls.CurveID{tls.CurveP521},
		MinVersion:       tls.VersionTLS12,
		MaxVersion:       tls.VersionTLS12,
	}
	listener, err := tls.Listen("tcp", ":13371", &config)
	if err != nil {
		status <- err
		return
	}

	fmt.Println("Starting Listener Loop")
	for {
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println(err.Error())
			break
		}
		fmt.Println(conn.RemoteAddr(), "Connected")
		go r.HandleConnection(conn)
	}
}

func (r *Runtime) StartEventEngine(status chan error) {
	fmt.Println("StartEventEngine Loop")
	for {
		var event FedopsEvent
		l := len(r.Events)
		if l > 0 {
			fmt.Println("Events -", l)
			event = r.Events[l-1]
			ftime := event.Time.Add(2 * time.Second)
			n := time.Now()
			if n.After(ftime) {
				r.Events = r.Events[:l-1]
				event.Time = n
				fmt.Println("Running Event Handle")
				go event.Handle(&event)
				if event.Persistant {
					events := r.Events
					r.Events = make([]FedopsEvent, l)
					r.Events = append(r.Events, event)
					r.Events = append(r.Events, events[:l-1]...)
				}
			}
		}
		time.Sleep(500 * time.Millisecond)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"net/http"
	"strings"
	"testing"
	"time"

	"github.com/gophercloud/gophercloud"
	"github.com/gophercloud/gophercloud/openstack"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
	"github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	v1 "k8s.io/api/core/v1"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"

	"github.com/sapcc/kubernikus/pkg/api/client/operations"
	"github.com/sapcc/kubernikus/pkg/api/models"
	"github.com/sapcc/kubernikus/test/e2e/framework"
)

const (
	// Incremental Increasing TImeout
	StateRunningTimeout                = 5 * time.Minute  // Time from cluster ready to nodes being created
	RegisteredTimeout                  = 15 * time.Minute // Time from node created to registered
	StateSchedulableTimeout            = 1 * time.Minute  // Time from registered to schedulable
	StateHealthyTimeout                = 1 * time.Minute
	ConditionRouteBrokenTimeout        = 1 * time.Minute
	ConditionNetworkUnavailableTimeout = 1 * time.Minute
	ConditionReadyTimeout              = 1 * time.Minute
)

type NodeTests struct {
	Kubernetes        *framework.Kubernetes
	Kubernikus        *framework.Kubernikus
	OpenStack         *framework.OpenStack
	ExpectedNodeCount int
	KlusterName       string
}

func (k *NodeTests) Run(t *testing.T) {
	_ = t.Run("Created", k.StateRunning) &&
		t.Run("Registered", k.Registered) &&
		t.Run("LatestStableContainerLinux", k.LatestStableContainerLinux) &&
		t.Run("Schedulable", k.StateSchedulable) &&
		t.Run("NetworkUnavailable", k.ConditionNetworkUnavailable) &&
		t.Run("Healthy", k.StateHealthy) &&
		t.Run("Ready", k.ConditionReady) &&
		t.Run("Labeled", k.Labeled) &&
		t.Run("Sufficient", k.Sufficient) &&
		t.Run("SameBuildingBlock", k.SameBuildingBlock)
}

func (k *NodeTests) StateRunning(t *testing.T) {
	count, err := k.checkState(t, func(pool models.NodePoolInfo) int64 { return pool.Running }, StateRunningTimeout)
	assert.NoError(t, err)
	assert.Equal(t, k.ExpectedNodeCount, count)
}

func (k *NodeTests) StateSchedulable(t *testing.T) {
	count, err := k.checkState(t, func(pool models.NodePoolInfo) int64 { return pool.Schedulable }, StateSchedulableTimeout)
	assert.NoError(t, err)
	assert.Equal(t, k.ExpectedNodeCount, count)
}

func (k *NodeTests) StateHealthy(t *testing.T) {
	count, err := k.checkState(t, func(pool models.NodePoolInfo) int64 { return pool.Healthy }, StateHealthyTimeout)
	assert.NoError(t, err)
	assert.Equal(t, k.ExpectedNodeCount, count)
}

func (k *NodeTests) ConditionNetworkUnavailable(t *testing.T) {
	count, err := k.checkCondition(t, v1.NodeNetworkUnavailable, v1.ConditionFalse, ConditionNetworkUnavailableTimeout)
	assert.NoError(t, err)
	assert.Equal(t, k.ExpectedNodeCount, count)
}

func (k *NodeTests) ConditionReady(t *testing.T) {
	count, err := k.checkCondition(t, v1.NodeReady, v1.ConditionTrue, ConditionReadyTimeout)
	assert.NoError(t, err)
	assert.Equal(t, k.ExpectedNodeCount, count)
}

func (k *NodeTests) Labeled(t *testing.T) {
	nodeList, err := k.Kubernetes.ClientSet.CoreV1().Nodes().List(meta_v1.ListOptions{})
	require.NoError(t, err, "There must be no error while listing the kluster's nodes")

	for _, node := range nodeList.Items {
		assert.Contains(t, node.Labels, "ccloud.sap.com/nodepool", "node %s is missing the ccloud.sap.com/nodepool label", node.Name)
	}

}

func (k *NodeTests) Registered(t *testing.T) {
	count := 0
	err := wait.PollImmediate(framework.Poll, RegisteredTimeout,
		func() (bool, error) {
			nodes, err := k.Kubernetes.ClientSet.CoreV1().Nodes().List(meta_v1.ListOptions{})
			if err != nil {
				return false, fmt.Errorf("Failed to list nodes: %v", err)
			}
			count = len(nodes.Items)

			return count >= k.ExpectedNodeCount, nil
		})

	assert.NoError(t, err)
	assert.Equal(t, k.ExpectedNodeCount, count)
}

func (k NodeTests) LatestStableContainerLinux(t *testing.T) {
	nodes, err := k.Kubernetes.ClientSet.CoreV1().Nodes().List(meta_v1.ListOptions{})
	if !assert.NoError(t, err) {
		return
	}
	resp, err := http.Get("https://stable.release.core-os.net/amd64-usr/current/version.txt")
	if !assert.NoError(t, err) {
		return
	}
	if !assert.Equal(t, 200, resp.StatusCode) {
		return
	}

	version := ""
	var date time.Time
	scanner := bufio.NewScanner(resp.Body)
	for scanner.Scan() {
		keyval := strings.Split(scanner.Text(), "=")

		if len(keyval) == 2 && keyval[0] == "COREOS_VERSION" {
			version = keyval[1]
			if !assert.NotEmpty(t, version, "Failed to detect latest stable Container Linux version") {
				return
			}
		}

		if len(keyval) == 2 && keyval[0] == "COREOS_BUILD_ID" {
			date, err = time.Parse("2006-01-02", keyval[1][1:11])
			if !assert.NoError(t, err) {
				return
			}
			if !assert.NotEmpty(t, date, "Could not get release date") {
				return
			}
			// check if release is at least 72 old, otherwise image might not be up-to-date
			if time.Since(date).Hours() < 72 {
				return
			}
		}
	}

	for _, node := range nodes.Items {
		assert.Contains(t, node.Status.NodeInfo.OSImage, version, "Node %s is not on latest version", node.Name)
	}
}

func (k *NodeTests) Sufficient(t *testing.T) {
	nodeList, err := k.Kubernetes.ClientSet.CoreV1().Nodes().List(meta_v1.ListOptions{})
	require.NoError(t, err, "There must be no error while listing the kluster's nodes")
	require.Equal(t, len(nodeList.Items), SmokeTestNodeCount, "There must be exactly %d nodes", SmokeTestNodeCount)
}

type poolCount func(models.NodePoolInfo) int64

func (k *NodeTests) checkState(t *testing.T, fn poolCount, timeout time.Duration) (int, error) {
	count := 0
	err := wait.PollImmediate(framework.Poll, StateRunningTimeout,
		func() (done bool, err error) {
			cluster, err := k.Kubernikus.Client.Operations.ShowCluster(
				operations.NewShowClusterParams().WithName(k.KlusterName),
				k.Kubernikus.AuthInfo,
			)
			if err != nil {
				return false, err
			}

			count = int(fn(cluster.Payload.Status.NodePools[0]))
			return count >= k.ExpectedNodeCount, nil
		})

	return count, err
}

func (k *NodeTests) checkCondition(t *testing.T, conditionType v1.NodeConditionType, expectedStatus v1.ConditionStatus, timeout time.Duration) (int, error) {
	count := 0
	err := wait.PollImmediate(framework.Poll, timeout,
		func() (bool, error) {
			nodes, err := k.Kubernetes.ClientSet.CoreV1().Nodes().List(meta_v1.ListOptions{})
			if err != nil {
				return false, fmt.Errorf("Failed to list nodes: %v", err)
			}

			count = 0
			for _, node := range nodes.Items {
				for _, condition := range node.Status.Conditions {
					if condition.Type == conditionType {
						if condition.Status == expectedStatus {
							count++
						}
						break
					}
				}
			}

			return count >= k.ExpectedNodeCount, nil
		})

	return count, err
}

func (k *NodeTests) SameBuildingBlock(t *testing.T) {
	if k.ExpectedNodeCount < 2 {
		return
	}

	computeClient, err := openstack.NewComputeV2(k.OpenStack.Provider, gophercloud.EndpointOpts{})
	require.NoError(t, err, "There should be no error creating compute client")

	project, err := tokens.Get(k.OpenStack.Identity, k.OpenStack.Provider.Token()).ExtractProject()
	require.NoError(t, err, "There should be no error while extracting the project")

	serversListOpts := servers.ListOpts{
		Name:     k.KlusterName,
		TenantID: project.ID,
		Status:   "ACTIVE",
	}

	allPages, err := servers.List(computeClient, serversListOpts).AllPages()
	require.NoError(t, err, "There should be no error while listing all servers")

	var s []struct {
		BuildingBlock string `json:"OS-EXT-SRV-ATTR:host"`
		ID            string `json:"id"`
	}
	err = servers.ExtractServersInto(allPages, &s)
	require.NoError(t, err, "There should be no error extracting server info")

	require.Len(t, s, k.ExpectedNodeCount, "Expected to find %d building blocks", k.ExpectedNodeCount)

	bb := ""
	for _, bbs := range s {
		require.NotEmpty(t, bbs.BuildingBlock, "Node building block should not be empty")
		if bb == "" {
			bb = bbs.BuildingBlock
		} else {
			require.Equal(t, bb, bbs.BuildingBlock, "Node %s is on building block %s which is different from node %s which is running on %s", bbs.ID, bbs.BuildingBlock, s[0].ID, s[0].BuildingBlock)
		}
	}
}
<commit_msg>remove same building block test now that we don't do softaffinity anymore<commit_after>package main

import (
	"bufio"
	"fmt"
	"net/http"
	"strings"
	"testing"
	"time"

	"github.com/gophercloud/gophercloud"
	"github.com/gophercloud/gophercloud/openstack"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
	"github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	v1 "k8s.io/api/core/v1"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"

	"github.com/sapcc/kubernikus/pkg/api/client/operations"
	"github.com/sapcc/kubernikus/pkg/api/models"
	"github.com/sapcc/kubernikus/test/e2e/framework"
)

const (
	// Incremental Increasing TImeout
	StateRunningTimeout                = 5 * time.Minute  // Time from cluster ready to nodes being created
	RegisteredTimeout                  = 15 * time.Minute // Time from node created to registered
	StateSchedulableTimeout            = 1 * time.Minute  // Time from registered to schedulable
	StateHealthyTimeout                = 1 * time.Minute
	ConditionRouteBrokenTimeout        = 1 * time.Minute
	ConditionNetworkUnavailableTimeout = 1 * time.Minute
	ConditionReadyTimeout              = 1 * time.Minute
)

type NodeTests struct {
	Kubernetes        *framework.Kubernetes
	Kubernikus        *framework.Kubernikus
	OpenStack         *framework.OpenStack
	ExpectedNodeCount int
	KlusterName       string
}

func (k *NodeTests) Run(t *testing.T) {
	_ = t.Run("Created", k.StateRunning) &&
		t.Run("Registered", k.Registered) &&
		t.Run("LatestStableContainerLinux", k.LatestStableContainerLinux) &&
		t.Run("Schedulable", k.StateSchedulable) &&
		t.Run("NetworkUnavailable", k.ConditionNetworkUnavailable) &&
		t.Run("Healthy", k.StateHealthy) &&
		t.Run("Ready", k.ConditionReady) &&
		t.Run("Labeled", k.Labeled) &&
		t.Run("Sufficient", k.Sufficient)
	//t.Run("SameBuildingBlock", k.SameBuildingBlock)
}

func (k *NodeTests) StateRunning(t *testing.T) {
	count, err := k.checkState(t, func(pool models.NodePoolInfo) int64 { return pool.Running }, StateRunningTimeout)
	assert.NoError(t, err)
	assert.Equal(t, k.ExpectedNodeCount, count)
}

func (k *NodeTests) StateSchedulable(t *testing.T) {
	count, err := k.checkState(t, func(pool models.NodePoolInfo) int64 { return pool.Schedulable }, StateSchedulableTimeout)
	assert.NoError(t, err)
	assert.Equal(t, k.ExpectedNodeCount, count)
}

func (k *NodeTests) StateHealthy(t *testing.T) {
	count, err := k.checkState(t, func(pool models.NodePoolInfo) int64 { return pool.Healthy }, StateHealthyTimeout)
	assert.NoError(t, err)
	assert.Equal(t, k.ExpectedNodeCount, count)
}

func (k *NodeTests) ConditionNetworkUnavailable(t *testing.T) {
	count, err := k.checkCondition(t, v1.NodeNetworkUnavailable, v1.ConditionFalse, ConditionNetworkUnavailableTimeout)
	assert.NoError(t, err)
	assert.Equal(t, k.ExpectedNodeCount, count)
}

func (k *NodeTests) ConditionReady(t *testing.T) {
	count, err := k.checkCondition(t, v1.NodeReady, v1.ConditionTrue, ConditionReadyTimeout)
	assert.NoError(t, err)
	assert.Equal(t, k.ExpectedNodeCount, count)
}

func (k *NodeTests) Labeled(t *testing.T) {
	nodeList, err := k.Kubernetes.ClientSet.CoreV1().Nodes().List(meta_v1.ListOptions{})
	require.NoError(t, err, "There must be no error while listing the kluster's nodes")

	for _, node := range nodeList.Items {
		assert.Contains(t, node.Labels, "ccloud.sap.com/nodepool", "node %s is missing the ccloud.sap.com/nodepool label", node.Name)
	}

}

func (k *NodeTests) Registered(t *testing.T) {
	count := 0
	err := wait.PollImmediate(framework.Poll, RegisteredTimeout,
		func() (bool, error) {
			nodes, err := k.Kubernetes.ClientSet.CoreV1().Nodes().List(meta_v1.ListOptions{})
			if err != nil {
				return false, fmt.Errorf("Failed to list nodes: %v", err)
			}
			count = len(nodes.Items)

			return count >= k.ExpectedNodeCount, nil
		})

	assert.NoError(t, err)
	assert.Equal(t, k.ExpectedNodeCount, count)
}

func (k NodeTests) LatestStableContainerLinux(t *testing.T) {
	nodes, err := k.Kubernetes.ClientSet.CoreV1().Nodes().List(meta_v1.ListOptions{})
	if !assert.NoError(t, err) {
		return
	}
	resp, err := http.Get("https://stable.release.core-os.net/amd64-usr/current/version.txt")
	if !assert.NoError(t, err) {
		return
	}
	if !assert.Equal(t, 200, resp.StatusCode) {
		return
	}

	version := ""
	var date time.Time
	scanner := bufio.NewScanner(resp.Body)
	for scanner.Scan() {
		keyval := strings.Split(scanner.Text(), "=")

		if len(keyval) == 2 && keyval[0] == "COREOS_VERSION" {
			version = keyval[1]
			if !assert.NotEmpty(t, version, "Failed to detect latest stable Container Linux version") {
				return
			}
		}

		if len(keyval) == 2 && keyval[0] == "COREOS_BUILD_ID" {
			date, err = time.Parse("2006-01-02", keyval[1][1:11])
			if !assert.NoError(t, err) {
				return
			}
			if !assert.NotEmpty(t, date, "Could not get release date") {
				return
			}
			// check if release is at least 72 old, otherwise image might not be up-to-date
			if time.Since(date).Hours() < 72 {
				return
			}
		}
	}

	for _, node := range nodes.Items {
		assert.Contains(t, node.Status.NodeInfo.OSImage, version, "Node %s is not on latest version", node.Name)
	}
}

func (k *NodeTests) Sufficient(t *testing.T) {
	nodeList, err := k.Kubernetes.ClientSet.CoreV1().Nodes().List(meta_v1.ListOptions{})
	require.NoError(t, err, "There must be no error while listing the kluster's nodes")
	require.Equal(t, len(nodeList.Items), SmokeTestNodeCount, "There must be exactly %d nodes", SmokeTestNodeCount)
}

type poolCount func(models.NodePoolInfo) int64

func (k *NodeTests) checkState(t *testing.T, fn poolCount, timeout time.Duration) (int, error) {
	count := 0
	err := wait.PollImmediate(framework.Poll, StateRunningTimeout,
		func() (done bool, err error) {
			cluster, err := k.Kubernikus.Client.Operations.ShowCluster(
				operations.NewShowClusterParams().WithName(k.KlusterName),
				k.Kubernikus.AuthInfo,
			)
			if err != nil {
				return false, err
			}

			count = int(fn(cluster.Payload.Status.NodePools[0]))
			return count >= k.ExpectedNodeCount, nil
		})

	return count, err
}

func (k *NodeTests) checkCondition(t *testing.T, conditionType v1.NodeConditionType, expectedStatus v1.ConditionStatus, timeout time.Duration) (int, error) {
	count := 0
	err := wait.PollImmediate(framework.Poll, timeout,
		func() (bool, error) {
			nodes, err := k.Kubernetes.ClientSet.CoreV1().Nodes().List(meta_v1.ListOptions{})
			if err != nil {
				return false, fmt.Errorf("Failed to list nodes: %v", err)
			}

			count = 0
			for _, node := range nodes.Items {
				for _, condition := range node.Status.Conditions {
					if condition.Type == conditionType {
						if condition.Status == expectedStatus {
							count++
						}
						break
					}
				}
			}

			return count >= k.ExpectedNodeCount, nil
		})

	return count, err
}

func (k *NodeTests) SameBuildingBlock(t *testing.T) {
	if k.ExpectedNodeCount < 2 {
		return
	}

	computeClient, err := openstack.NewComputeV2(k.OpenStack.Provider, gophercloud.EndpointOpts{})
	require.NoError(t, err, "There should be no error creating compute client")

	project, err := tokens.Get(k.OpenStack.Identity, k.OpenStack.Provider.Token()).ExtractProject()
	require.NoError(t, err, "There should be no error while extracting the project")

	serversListOpts := servers.ListOpts{
		Name:     k.KlusterName,
		TenantID: project.ID,
		Status:   "ACTIVE",
	}

	allPages, err := servers.List(computeClient, serversListOpts).AllPages()
	require.NoError(t, err, "There should be no error while listing all servers")

	var s []struct {
		BuildingBlock string `json:"OS-EXT-SRV-ATTR:host"`
		ID            string `json:"id"`
	}
	err = servers.ExtractServersInto(allPages, &s)
	require.NoError(t, err, "There should be no error extracting server info")

	require.Len(t, s, k.ExpectedNodeCount, "Expected to find %d building blocks", k.ExpectedNodeCount)

	bb := ""
	for _, bbs := range s {
		require.NotEmpty(t, bbs.BuildingBlock, "Node building block should not be empty")
		if bb == "" {
			bb = bbs.BuildingBlock
		} else {
			require.Equal(t, bb, bbs.BuildingBlock, "Node %s is on building block %s which is different from node %s which is running on %s", bbs.ID, bbs.BuildingBlock, s[0].ID, s[0].BuildingBlock)
		}
	}
}
<|endoftext|>


<commit_before>package token

type TokenID int

const (
	TokenERR TokenID = iota
	TokenEOF
	TokenInteger
	TokenComment
)

type Token struct {
	ID    TokenID
	Value string
}

func NewToken(id TokenID, value string) *Token {
	return &Token{ID: id, Value: value}
}
<commit_msg>token: add string equivalents of tokens for debugging<commit_after>package token

import "fmt"

type TokenID int

const (
	TokenERR TokenID = iota
	TokenEOF
	TokenInteger
	TokenComment
	TokenNewLine
)

var IDtoString = map[TokenID]string{
	TokenERR:     "Error",
	TokenEOF:     "EOF",
	TokenInteger: "Integer",
	TokenComment: "Comment",
	TokenNewLine: "NewLine",
}

type Token struct {
	ID    TokenID
	Value string
}

func NewToken(id TokenID, value string) *Token {
	return &Token{ID: id, Value: value}
}

func (t *Token) String() string {
	return fmt.Sprintf("%s %s", IDtoString[t.ID], t.Value)
}
<|endoftext|>


<commit_before>//+build !local_tests

package myirmaserver

import (
	"testing"
	"time"

	"github.com/privacybydesign/irmago/internal/test"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestPostgresDBUserManagement(t *testing.T) {
	SetupDatabase(t)
	defer TeardownDatabase(t)

	db, err := newPostgresDB(test.PostgresTestUrl)
	require.NoError(t, err)

	pdb := db.(*postgresDB)
	_, err = pdb.db.Exec("INSERT INTO irma.users (id, username, last_seen, language, coredata, pin_counter, pin_block_date) VALUES (15, 'testuser', 0, '', '', 0,0)")
	require.NoError(t, err)
	_, err = pdb.db.Exec("INSERT INTO irma.email_verification_tokens (token, email, expiry, user_id) VALUES ('testtoken', 'test@test.com', $1, 15)", time.Now().Unix())
	require.NoError(t, err)

	id, err := db.userIDByUsername("testuser")
	assert.NoError(t, err)
	assert.Equal(t, int64(15), id)

	user, err := db.user(id)
	assert.NoError(t, err)
	assert.Equal(t, []userEmail(nil), user.Emails)

	id, err = db.verifyEmailToken("testtoken")
	assert.NoError(t, err)
	assert.Equal(t, int64(15), id)

	user, err = db.user(id)
	assert.NoError(t, err)
	assert.Equal(t, []userEmail{{Email: "test@test.com", DeleteInProgress: false}}, user.Emails)

	_, err = db.verifyEmailToken("testtoken")
	assert.Error(t, err)

	_, err = db.userIDByUsername("DNE")
	assert.Error(t, err)

	err = db.setSeen(15)
	assert.NoError(t, err)

	err = db.setSeen(123456)
	assert.Error(t, err)

	err = db.scheduleUserRemoval(15, 0)
	assert.NoError(t, err)

	user, err = db.user(15)
	require.NoError(t, err)
	require.True(t, user.DeleteInProgress)

	err = db.scheduleUserRemoval(15, 0)
	assert.Error(t, err)
}

func TestPostgresDBLoginToken(t *testing.T) {
	SetupDatabase(t)
	defer TeardownDatabase(t)

	db, err := newPostgresDB(test.PostgresTestUrl)
	require.NoError(t, err)

	pdb := db.(*postgresDB)
	_, err = pdb.db.Exec("INSERT INTO irma.users (id, username, last_seen, language, coredata, pin_counter, pin_block_date) VALUES (15, 'testuser', 0, '', '', 0,0)")
	require.NoError(t, err)
	_, err = pdb.db.Exec("INSERT INTO irma.users (id, username, last_seen, language, coredata, pin_counter, pin_block_date) VALUES (17, 'noemail', 0, '', '', 0,0)")
	require.NoError(t, err)
	_, err = pdb.db.Exec("INSERT INTO irma.emails (user_id, email) VALUES (15, 'test@test.com')")
	require.NoError(t, err)

	err = db.addLoginToken("test2@test.com", "test2token")
	assert.Error(t, err)

	err = db.addLoginToken("test@test.com", "testtoken")
	require.NoError(t, err)

	cand, err := db.loginUserCandidates("testtoken")
	assert.NoError(t, err)
	assert.Equal(t, []loginCandidate{{Username: "testuser", LastActive: 0}}, cand)

	currenttime := time.Now().Unix()
	require.NoError(t, db.setSeen(int64(15)))
	cand, err = db.loginUserCandidates("testtoken")
	assert.NoError(t, err)
	assert.Equal(t, []loginCandidate{{Username: "testuser", LastActive: currenttime}}, cand)

	_, err = db.loginUserCandidates("DNE")
	assert.Error(t, err)

	_, err = db.verifyLoginToken("testtoken", "DNE")
	assert.Error(t, err)

	_, err = db.verifyLoginToken("testtoken", "noemail")
	assert.Error(t, err)

	id, err := db.verifyLoginToken("testtoken", "testuser")
	assert.NoError(t, err)
	assert.Equal(t, int64(15), id)

	_, err = db.verifyLoginToken("testtoken", "testuser")
	assert.Error(t, err)

	assert.NoError(t, db.addEmail(17, "test@test.com"))
	assert.NoError(t, db.addLoginToken("test@test.com", "testtoken"))
	cand, err = db.loginUserCandidates("testtoken")
	assert.NoError(t, err)
	assert.Equal(t, []loginCandidate{
		{Username: "testuser", LastActive: currenttime},
		{Username: "noemail", LastActive: 0},
	}, cand)
}

var (
	str15    = "15"
	strEmpty = ""
)

func TestPostgresDBUserInfo(t *testing.T) {
	SetupDatabase(t)
	defer TeardownDatabase(t)

	db, err := newPostgresDB(test.PostgresTestUrl)
	require.NoError(t, err)

	pdb := db.(*postgresDB)
	_, err = pdb.db.Exec("INSERT INTO irma.users (id, username, last_seen, language, coredata, pin_counter, pin_block_date) VALUES (15, 'testuser', 15, '', '', 0,0)")
	require.NoError(t, err)
	_, err = pdb.db.Exec("INSERT INTO irma.users (id, username, last_seen, language, coredata, pin_counter, pin_block_date) VALUES (17, 'noemail', 20, '', '', 0,0)")
	require.NoError(t, err)
	_, err = pdb.db.Exec("INSERT INTO irma.emails (user_id, email) VALUES (15, 'test@test.com')")
	require.NoError(t, err)
	_, err = pdb.db.Exec(
		`INSERT INTO irma.log_entry_records (time, event, param, user_id)
		 VALUES (110, 'test', '', 15), (120, 'test2', '15', 15), (130, 'test3', NULL, 15)`)
	require.NoError(t, err)

	info, err := db.user(15)
	assert.NoError(t, err)
	assert.Equal(t, "testuser", info.Username)
	assert.Equal(t, []userEmail{{Email: "test@test.com", DeleteInProgress: false}}, info.Emails)

	info, err = db.user(17)
	assert.NoError(t, err)
	assert.Equal(t, "noemail", info.Username)
	assert.Equal(t, []userEmail(nil), info.Emails)

	_, err = db.user(1231)
	assert.Error(t, err)

	entries, err := db.logs(15, 0, 3)
	assert.NoError(t, err)
	assert.Equal(t, []logEntry{
		{
			Timestamp: 130,
			Event:     "test3",
			Param:     nil,
		},
		{
			Timestamp: 120,
			Event:     "test2",
			Param:     &str15,
		},
		{
			Timestamp: 110,
			Event:     "test",
			Param:     &strEmpty,
		},
	}, entries)

	entries, err = db.logs(15, 0, 1)
	assert.NoError(t, err)
	assert.Equal(t, 1, len(entries))

	entries, err = db.logs(15, 1, 15)
	assert.NoError(t, err)
	assert.Equal(t, 2, len(entries))

	entries, err = db.logs(15, 100, 20)
	assert.NoError(t, err)
	assert.Equal(t, 0, len(entries))

	entries, err = db.logs(20, 100, 20)
	assert.NoError(t, err)
	assert.Equal(t, 0, len(entries))

	err = db.addEmail(17, "test@test.com")
	assert.NoError(t, err)

	info, err = db.user(17)
	assert.NoError(t, err)
	assert.Equal(t, []userEmail{{Email: "test@test.com", DeleteInProgress: false}}, info.Emails)

	err = db.addEmail(20, "bla@bla.com")
	assert.Error(t, err)

	err = db.scheduleEmailRemoval(17, "test@test.com", 0)
	assert.NoError(t, err)

	info, err = db.user(17)
	assert.NoError(t, err)
	assert.Equal(t, []userEmail{{Email: "test@test.com", DeleteInProgress: true}}, info.Emails)

	// Need sleep here to ensure time has passed since delete
	time.Sleep(1 * time.Second)

	info, err = db.user(17)
	assert.NoError(t, err)
	assert.Equal(t, 0, len(info.Emails))

	err = db.scheduleEmailRemoval(17, "bla@bla.com", 0)
	assert.Error(t, err)

	err = db.scheduleEmailRemoval(20, "bl@bla.com", 0)
	assert.Error(t, err)
}

func SetupDatabase(t *testing.T) {
	test.RunScriptOnDB(t, "../keyshareserver/cleanup.sql", true)
	test.RunScriptOnDB(t, "../keyshareserver/schema.sql", false)
}

func TeardownDatabase(t *testing.T) {
	test.RunScriptOnDB(t, "../keyshareserver/cleanup.sql", false)
}
<commit_msg>test: test entire user object in myirmaserver postgres DB test<commit_after>//+build !local_tests

package myirmaserver

import (
	"testing"
	"time"

	"github.com/privacybydesign/irmago/internal/test"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestPostgresDBUserManagement(t *testing.T) {
	SetupDatabase(t)
	defer TeardownDatabase(t)

	db, err := newPostgresDB(test.PostgresTestUrl)
	require.NoError(t, err)

	pdb := db.(*postgresDB)
	_, err = pdb.db.Exec("INSERT INTO irma.users (id, username, last_seen, language, coredata, pin_counter, pin_block_date) VALUES (15, 'testuser', 0, '', '', 0,0)")
	require.NoError(t, err)
	_, err = pdb.db.Exec("INSERT INTO irma.email_verification_tokens (token, email, expiry, user_id) VALUES ('testtoken', 'test@test.com', $1, 15)", time.Now().Unix())
	require.NoError(t, err)

	id, err := db.userIDByUsername("testuser")
	assert.NoError(t, err)
	assert.Equal(t, int64(15), id)

	user, err := db.user(id)
	assert.NoError(t, err)
	assert.Equal(t, []userEmail(nil), user.Emails)

	id, err = db.verifyEmailToken("testtoken")
	assert.NoError(t, err)
	assert.Equal(t, int64(15), id)

	user, err = db.user(id)
	assert.NoError(t, err)
	assert.Equal(t, []userEmail{{Email: "test@test.com", DeleteInProgress: false}}, user.Emails)

	_, err = db.verifyEmailToken("testtoken")
	assert.Error(t, err)

	_, err = db.userIDByUsername("DNE")
	assert.Error(t, err)

	err = db.setSeen(15)
	assert.NoError(t, err)

	err = db.setSeen(123456)
	assert.Error(t, err)

	err = db.scheduleUserRemoval(15, 0)
	assert.NoError(t, err)

	user, err = db.user(15)
	require.NoError(t, err)
	require.True(t, user.DeleteInProgress)

	err = db.scheduleUserRemoval(15, 0)
	assert.Error(t, err)
}

func TestPostgresDBLoginToken(t *testing.T) {
	SetupDatabase(t)
	defer TeardownDatabase(t)

	db, err := newPostgresDB(test.PostgresTestUrl)
	require.NoError(t, err)

	pdb := db.(*postgresDB)
	_, err = pdb.db.Exec("INSERT INTO irma.users (id, username, last_seen, language, coredata, pin_counter, pin_block_date) VALUES (15, 'testuser', 0, '', '', 0,0)")
	require.NoError(t, err)
	_, err = pdb.db.Exec("INSERT INTO irma.users (id, username, last_seen, language, coredata, pin_counter, pin_block_date) VALUES (17, 'noemail', 0, '', '', 0,0)")
	require.NoError(t, err)
	_, err = pdb.db.Exec("INSERT INTO irma.emails (user_id, email) VALUES (15, 'test@test.com')")
	require.NoError(t, err)

	err = db.addLoginToken("test2@test.com", "test2token")
	assert.Error(t, err)

	err = db.addLoginToken("test@test.com", "testtoken")
	require.NoError(t, err)

	cand, err := db.loginUserCandidates("testtoken")
	assert.NoError(t, err)
	assert.Equal(t, []loginCandidate{{Username: "testuser", LastActive: 0}}, cand)

	currenttime := time.Now().Unix()
	require.NoError(t, db.setSeen(int64(15)))
	cand, err = db.loginUserCandidates("testtoken")
	assert.NoError(t, err)
	assert.Equal(t, []loginCandidate{{Username: "testuser", LastActive: currenttime}}, cand)

	_, err = db.loginUserCandidates("DNE")
	assert.Error(t, err)

	_, err = db.verifyLoginToken("testtoken", "DNE")
	assert.Error(t, err)

	_, err = db.verifyLoginToken("testtoken", "noemail")
	assert.Error(t, err)

	id, err := db.verifyLoginToken("testtoken", "testuser")
	assert.NoError(t, err)
	assert.Equal(t, int64(15), id)

	_, err = db.verifyLoginToken("testtoken", "testuser")
	assert.Error(t, err)

	assert.NoError(t, db.addEmail(17, "test@test.com"))
	assert.NoError(t, db.addLoginToken("test@test.com", "testtoken"))
	cand, err = db.loginUserCandidates("testtoken")
	assert.NoError(t, err)
	assert.Equal(t, []loginCandidate{
		{Username: "testuser", LastActive: currenttime},
		{Username: "noemail", LastActive: 0},
	}, cand)
}

var (
	str15    = "15"
	strEmpty = ""
)

func TestPostgresDBUserInfo(t *testing.T) {
	SetupDatabase(t)
	defer TeardownDatabase(t)

	db, err := newPostgresDB(test.PostgresTestUrl)
	require.NoError(t, err)

	pdb := db.(*postgresDB)
	_, err = pdb.db.Exec("INSERT INTO irma.users (id, username, last_seen, language, coredata, pin_counter, pin_block_date) VALUES (15, 'testuser', 15, '', '', 0,0)")
	require.NoError(t, err)
	_, err = pdb.db.Exec("INSERT INTO irma.users (id, username, last_seen, language, coredata, pin_counter, pin_block_date) VALUES (17, 'noemail', 20, '', '', 0,0)")
	require.NoError(t, err)
	_, err = pdb.db.Exec("INSERT INTO irma.emails (user_id, email) VALUES (15, 'test@test.com')")
	require.NoError(t, err)
	_, err = pdb.db.Exec(
		`INSERT INTO irma.log_entry_records (time, event, param, user_id)
		 VALUES (110, 'test', '', 15), (120, 'test2', '15', 15), (130, 'test3', NULL, 15)`)
	require.NoError(t, err)

	info, err := db.user(15)
	assert.NoError(t, err)
	assert.Equal(t, user{
		Username:         "testuser",
		Emails:           []userEmail{{Email: "test@test.com", DeleteInProgress: false}},
		language:         "",
		DeleteInProgress: false,
	}, info)

	info, err = db.user(17)
	assert.NoError(t, err)
	assert.Equal(t, "noemail", info.Username)
	assert.Equal(t, []userEmail(nil), info.Emails)

	_, err = db.user(1231)
	assert.Error(t, err)

	entries, err := db.logs(15, 0, 3)
	assert.NoError(t, err)
	assert.Equal(t, []logEntry{
		{
			Timestamp: 130,
			Event:     "test3",
			Param:     nil,
		},
		{
			Timestamp: 120,
			Event:     "test2",
			Param:     &str15,
		},
		{
			Timestamp: 110,
			Event:     "test",
			Param:     &strEmpty,
		},
	}, entries)

	entries, err = db.logs(15, 0, 1)
	assert.NoError(t, err)
	assert.Equal(t, 1, len(entries))

	entries, err = db.logs(15, 1, 15)
	assert.NoError(t, err)
	assert.Equal(t, 2, len(entries))

	entries, err = db.logs(15, 100, 20)
	assert.NoError(t, err)
	assert.Equal(t, 0, len(entries))

	entries, err = db.logs(20, 100, 20)
	assert.NoError(t, err)
	assert.Equal(t, 0, len(entries))

	err = db.addEmail(17, "test@test.com")
	assert.NoError(t, err)

	info, err = db.user(17)
	assert.NoError(t, err)
	assert.Equal(t, []userEmail{{Email: "test@test.com", DeleteInProgress: false}}, info.Emails)

	err = db.addEmail(20, "bla@bla.com")
	assert.Error(t, err)

	err = db.scheduleEmailRemoval(17, "test@test.com", 0)
	assert.NoError(t, err)

	info, err = db.user(17)
	assert.NoError(t, err)
	assert.Equal(t, []userEmail{{Email: "test@test.com", DeleteInProgress: true}}, info.Emails)

	// Need sleep here to ensure time has passed since delete
	time.Sleep(1 * time.Second)

	info, err = db.user(17)
	assert.NoError(t, err)
	assert.Equal(t, 0, len(info.Emails))

	err = db.scheduleEmailRemoval(17, "bla@bla.com", 0)
	assert.Error(t, err)

	err = db.scheduleEmailRemoval(20, "bl@bla.com", 0)
	assert.Error(t, err)
}

func SetupDatabase(t *testing.T) {
	test.RunScriptOnDB(t, "../keyshareserver/cleanup.sql", true)
	test.RunScriptOnDB(t, "../keyshareserver/schema.sql", false)
}

func TeardownDatabase(t *testing.T) {
	test.RunScriptOnDB(t, "../keyshareserver/cleanup.sql", false)
}
<|endoftext|>


<commit_before>package integration_test

import (
	"encoding/pem"
	"io"
	"io/ioutil"
	"log"
	"os/exec"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
	"github.com/onsi/gomega/ghttp"

	"github.com/concourse/concourse/fly/rc"
)

var _ = Describe("login -k Command", func() {
	var loginATCServer *ghttp.Server

	Describe("login", func() {
		var (
			flyCmd *exec.Cmd
			stdin  io.WriteCloser
		)
		BeforeEach(func() {
			l := log.New(GinkgoWriter, "TLSServer", 0)
			loginATCServer = ghttp.NewUnstartedServer()
			loginATCServer.HTTPTestServer.Config.ErrorLog = l
			loginATCServer.HTTPTestServer.StartTLS()
		})

		AfterEach(func() {
			loginATCServer.Close()
		})

		Context("to new target with invalid SSL with -k", func() {
			BeforeEach(func() {
				loginATCServer.AppendHandlers(
					infoHandler(),
					tokenHandler(),
					userInfoHandler(),
				)

				flyCmd = exec.Command(flyPath, "-t", "some-target", "login", "-c", loginATCServer.URL(), "-k", "-u", "some_user", "-p", "some_pass")

				var err error
				stdin, err = flyCmd.StdinPipe()
				Expect(err).NotTo(HaveOccurred())
			})

			It("succeeds", func() {
				sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter)
				Expect(err).NotTo(HaveOccurred())

				Consistently(sess.Out.Contents).ShouldNot(ContainSubstring("some_pass"))

				Eventually(sess.Out).Should(gbytes.Say("target saved"))

				err = stdin.Close()
				Expect(err).NotTo(HaveOccurred())

				<-sess.Exited
				Expect(sess.ExitCode()).To(Equal(0))
			})

			Context("login to existing target", func() {
				var otherCmd *exec.Cmd
				BeforeEach(func() {
					loginATCServer.AppendHandlers(
						infoHandler(),
						tokenHandler(),
						userInfoHandler(),
					)

					sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter)
					Expect(err).NotTo(HaveOccurred())

					Eventually(sess.Out).Should(gbytes.Say("target saved"))

					<-sess.Exited
					Expect(sess.ExitCode()).To(Equal(0))
				})

				Context("with -k", func() {
					BeforeEach(func() {
						otherCmd = exec.Command(flyPath, "-t", "some-target", "login", "-k", "-u", "some_user", "-p", "some_pass")
					})

					It("succeeds", func() {
						sess, err := gexec.Start(otherCmd, GinkgoWriter, GinkgoWriter)
						Expect(err).NotTo(HaveOccurred())

						Eventually(sess.Out).Should(gbytes.Say("target saved"))

						<-sess.Exited
						Expect(sess.ExitCode()).To(Equal(0))
					})
				})

				Context("without -k", func() {
					BeforeEach(func() {
						otherCmd = exec.Command(flyPath, "-t", "some-target", "login", "-u", "some_user", "-p", "some_pass")
					})

					It("errors", func() {
						sess, err := gexec.Start(otherCmd, GinkgoWriter, GinkgoWriter)
						Expect(err).NotTo(HaveOccurred())

						<-sess.Exited
						Expect(sess.ExitCode()).To(Equal(1))
						Eventually(sess.Err).Should(gbytes.Say("x509: certificate signed by unknown authority"))
					})
				})
			})
		})

		Context("to new target with invalid SSL without -k", func() {
			Context("without --ca-cert", func() {
				BeforeEach(func() {
					flyCmd = exec.Command(flyPath, "-t", "some-target", "login", "-c", loginATCServer.URL(), "-u", "some_user", "-p", "some_pass")

					var err error
					stdin, err = flyCmd.StdinPipe()
					Expect(err).NotTo(HaveOccurred())
				})

				It("errors", func() {
					sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter)
					Expect(err).NotTo(HaveOccurred())

					err = stdin.Close()
					Expect(err).NotTo(HaveOccurred())

					<-sess.Exited
					Expect(sess.ExitCode()).To(Equal(1))
					Eventually(sess.Err).Should(gbytes.Say("x509: certificate signed by unknown authority"))
				})
			})

			Context("with --ca-cert", func() {
				var (
					sslCert string
				)

				BeforeEach(func() {
					sslCert = string(pem.EncodeToMemory(&pem.Block{
						Type:  "CERTIFICATE",
						Bytes: loginATCServer.HTTPTestServer.TLS.Certificates[0].Certificate[0],
					}))

					caCertFile, err := ioutil.TempFile("", "ca_cert.pem")
					Expect(err).NotTo(HaveOccurred())

					_, err = caCertFile.WriteString(sslCert)
					Expect(err).NotTo(HaveOccurred())

					flyCmd = exec.Command(flyPath, "-t", "some-target", "login", "-c", loginATCServer.URL(), "--ca-cert", caCertFile.Name(), "-u", "some_user", "-p", "some_pass")

					loginATCServer.AppendHandlers(
						infoHandler(),
						tokenHandler(),
						userInfoHandler(),
					)
				})

				It("succeeds", func() {
					sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter)
					Expect(err).NotTo(HaveOccurred())

					Consistently(sess.Out.Contents).ShouldNot(ContainSubstring("some_pass"))

					Eventually(sess.Out).Should(gbytes.Say("target saved"))

					<-sess.Exited
					Expect(sess.ExitCode()).To(Equal(0))

					By("saving the CA cert to the .flyrc", func() {
						returnedTarget, err := rc.LoadTarget("some-target", false)
						Expect(err).NotTo(HaveOccurred())
						Expect(returnedTarget.CACert()).To(Equal(sslCert))
					})
				})
			})
		})

		Context("to existing target with invalid SSL certificate", func() {
			Context("when 'insecure' is not set", func() {
				BeforeEach(func() {
					createFlyRc(rc.Targets{
						"some-target": {
							API:      loginATCServer.URL(),
							TeamName: "main",
							CACert:   "some-ca-cert",
							Token:    &rc.TargetToken{Type: "Bearer", Value: validAccessToken(date(2020, 1, 1))},
						},
					})
				})

				Context("with -k", func() {
					BeforeEach(func() {
						loginATCServer.AppendHandlers(
							infoHandler(),
							tokenHandler(),
							userInfoHandler(),
						)

						flyCmd = exec.Command(flyPath, "-t", "some-target", "login", "-k", "-u", "some_user", "-p", "some_pass")
					})

					It("succeeds", func() {
						sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter)
						Expect(err).NotTo(HaveOccurred())

						Consistently(sess.Out.Contents).ShouldNot(ContainSubstring("some_pass"))

						Eventually(sess.Out).Should(gbytes.Say("target saved"))

						<-sess.Exited
						Expect(sess.ExitCode()).To(Equal(0))

						By("saving the CA cert to the .flyrc", func() {
							returnedTarget, err := rc.LoadTarget("some-target", false)
							Expect(err).NotTo(HaveOccurred())
							Expect(returnedTarget.CACert()).To(Equal(""))
						})
					})
				})

				Context("without -k", func() {
					BeforeEach(func() {
						flyCmd = exec.Command(flyPath, "-t", "some-target", "login")
					})

					It("errors", func() {
						sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter)
						Expect(err).NotTo(HaveOccurred())

						<-sess.Exited
						Expect(sess.ExitCode()).To(Equal(1))
						Eventually(sess.Err).Should(gbytes.Say("CA Cert not valid"))
					})
				})
			})
		})
	})
})
<commit_msg>allow go 1.18 darwin error message<commit_after>package integration_test

import (
	"encoding/pem"
	"io"
	"io/ioutil"
	"log"
	"os/exec"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
	"github.com/onsi/gomega/ghttp"

	"github.com/concourse/concourse/fly/rc"
)

var _ = Describe("login -k Command", func() {
	var loginATCServer *ghttp.Server

	Describe("login", func() {
		var (
			flyCmd *exec.Cmd
			stdin  io.WriteCloser
		)
		BeforeEach(func() {
			l := log.New(GinkgoWriter, "TLSServer", 0)
			loginATCServer = ghttp.NewUnstartedServer()
			loginATCServer.HTTPTestServer.Config.ErrorLog = l
			loginATCServer.HTTPTestServer.StartTLS()
		})

		AfterEach(func() {
			loginATCServer.Close()
		})

		Context("to new target with invalid SSL with -k", func() {
			BeforeEach(func() {
				loginATCServer.AppendHandlers(
					infoHandler(),
					tokenHandler(),
					userInfoHandler(),
				)

				flyCmd = exec.Command(flyPath, "-t", "some-target", "login", "-c", loginATCServer.URL(), "-k", "-u", "some_user", "-p", "some_pass")

				var err error
				stdin, err = flyCmd.StdinPipe()
				Expect(err).NotTo(HaveOccurred())
			})

			It("succeeds", func() {
				sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter)
				Expect(err).NotTo(HaveOccurred())

				Consistently(sess.Out.Contents).ShouldNot(ContainSubstring("some_pass"))

				Eventually(sess.Out).Should(gbytes.Say("target saved"))

				err = stdin.Close()
				Expect(err).NotTo(HaveOccurred())

				<-sess.Exited
				Expect(sess.ExitCode()).To(Equal(0))
			})

			Context("login to existing target", func() {
				var otherCmd *exec.Cmd
				BeforeEach(func() {
					loginATCServer.AppendHandlers(
						infoHandler(),
						tokenHandler(),
						userInfoHandler(),
					)

					sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter)
					Expect(err).NotTo(HaveOccurred())

					Eventually(sess.Out).Should(gbytes.Say("target saved"))

					<-sess.Exited
					Expect(sess.ExitCode()).To(Equal(0))
				})

				Context("with -k", func() {
					BeforeEach(func() {
						otherCmd = exec.Command(flyPath, "-t", "some-target", "login", "-k", "-u", "some_user", "-p", "some_pass")
					})

					It("succeeds", func() {
						sess, err := gexec.Start(otherCmd, GinkgoWriter, GinkgoWriter)
						Expect(err).NotTo(HaveOccurred())

						Eventually(sess.Out).Should(gbytes.Say("target saved"))

						<-sess.Exited
						Expect(sess.ExitCode()).To(Equal(0))
					})
				})

				Context("without -k", func() {
					BeforeEach(func() {
						otherCmd = exec.Command(flyPath, "-t", "some-target", "login", "-u", "some_user", "-p", "some_pass")
					})

					It("errors", func() {
						sess, err := gexec.Start(otherCmd, GinkgoWriter, GinkgoWriter)
						Expect(err).NotTo(HaveOccurred())

						<-sess.Exited
						Expect(sess.ExitCode()).To(Equal(1))
						Eventually(sess.Err).Should(gbytes.Say("x509: certificate signed by unknown authority|certificate is not trusted"))
					})
				})
			})
		})

		Context("to new target with invalid SSL without -k", func() {
			Context("without --ca-cert", func() {
				BeforeEach(func() {
					flyCmd = exec.Command(flyPath, "-t", "some-target", "login", "-c", loginATCServer.URL(), "-u", "some_user", "-p", "some_pass")

					var err error
					stdin, err = flyCmd.StdinPipe()
					Expect(err).NotTo(HaveOccurred())
				})

				It("errors", func() {
					sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter)
					Expect(err).NotTo(HaveOccurred())

					err = stdin.Close()
					Expect(err).NotTo(HaveOccurred())

					<-sess.Exited
					Expect(sess.ExitCode()).To(Equal(1))
					Eventually(sess.Err).Should(gbytes.Say("x509: certificate signed by unknown authority|certificate is not trusted"))
				})
			})

			Context("with --ca-cert", func() {
				var (
					sslCert string
				)

				BeforeEach(func() {
					sslCert = string(pem.EncodeToMemory(&pem.Block{
						Type:  "CERTIFICATE",
						Bytes: loginATCServer.HTTPTestServer.TLS.Certificates[0].Certificate[0],
					}))

					caCertFile, err := ioutil.TempFile("", "ca_cert.pem")
					Expect(err).NotTo(HaveOccurred())

					_, err = caCertFile.WriteString(sslCert)
					Expect(err).NotTo(HaveOccurred())

					flyCmd = exec.Command(flyPath, "-t", "some-target", "login", "-c", loginATCServer.URL(), "--ca-cert", caCertFile.Name(), "-u", "some_user", "-p", "some_pass")

					loginATCServer.AppendHandlers(
						infoHandler(),
						tokenHandler(),
						userInfoHandler(),
					)
				})

				It("succeeds", func() {
					sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter)
					Expect(err).NotTo(HaveOccurred())

					Consistently(sess.Out.Contents).ShouldNot(ContainSubstring("some_pass"))

					Eventually(sess.Out).Should(gbytes.Say("target saved"))

					<-sess.Exited
					Expect(sess.ExitCode()).To(Equal(0))

					By("saving the CA cert to the .flyrc", func() {
						returnedTarget, err := rc.LoadTarget("some-target", false)
						Expect(err).NotTo(HaveOccurred())
						Expect(returnedTarget.CACert()).To(Equal(sslCert))
					})
				})
			})
		})

		Context("to existing target with invalid SSL certificate", func() {
			Context("when 'insecure' is not set", func() {
				BeforeEach(func() {
					createFlyRc(rc.Targets{
						"some-target": {
							API:      loginATCServer.URL(),
							TeamName: "main",
							CACert:   "some-ca-cert",
							Token:    &rc.TargetToken{Type: "Bearer", Value: validAccessToken(date(2020, 1, 1))},
						},
					})
				})

				Context("with -k", func() {
					BeforeEach(func() {
						loginATCServer.AppendHandlers(
							infoHandler(),
							tokenHandler(),
							userInfoHandler(),
						)

						flyCmd = exec.Command(flyPath, "-t", "some-target", "login", "-k", "-u", "some_user", "-p", "some_pass")
					})

					It("succeeds", func() {
						sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter)
						Expect(err).NotTo(HaveOccurred())

						Consistently(sess.Out.Contents).ShouldNot(ContainSubstring("some_pass"))

						Eventually(sess.Out).Should(gbytes.Say("target saved"))

						<-sess.Exited
						Expect(sess.ExitCode()).To(Equal(0))

						By("saving the CA cert to the .flyrc", func() {
							returnedTarget, err := rc.LoadTarget("some-target", false)
							Expect(err).NotTo(HaveOccurred())
							Expect(returnedTarget.CACert()).To(Equal(""))
						})
					})
				})

				Context("without -k", func() {
					BeforeEach(func() {
						flyCmd = exec.Command(flyPath, "-t", "some-target", "login")
					})

					It("errors", func() {
						sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter)
						Expect(err).NotTo(HaveOccurred())

						<-sess.Exited
						Expect(sess.ExitCode()).To(Equal(1))
						Eventually(sess.Err).Should(gbytes.Say("CA Cert not valid"))
					})
				})
			})
		})
	})
})
<|endoftext|>