package docker

import (
	"crypto/tls"
	"crypto/x509"
	"io/ioutil"
	"strings"
	"testing"
	"time"

	"github.com/docker/docker/api/client"
	"github.com/docker/libtrust"
)

const (
	errBadCertificate = "remote error: bad certificate"
	errCaUnknown      = "x509: certificate signed by unknown authority"
)

func getTlsConfig(certFile, keyFile string, t *testing.T) *tls.Config {
	certPool := x509.NewCertPool()
	file, err := ioutil.ReadFile("fixtures/https/ca.pem")
	if err != nil {
		t.Fatal(err)
	}
	certPool.AppendCertsFromPEM(file)

	cert, err := tls.LoadX509KeyPair("fixtures/https/"+certFile, "fixtures/https/"+keyFile)
	if err != nil {
		t.Fatalf("Couldn't load X509 key pair: %s", err)
	}
	tlsConfig := &tls.Config{
		RootCAs:      certPool,
		Certificates: []tls.Certificate{cert},
	}
	return tlsConfig
}

// TestHttpsInfo connects via two-way authenticated HTTPS to the info endpoint
func TestHttpsInfo(t *testing.T) {
	key, err := libtrust.GenerateECP256PrivateKey()
	if err != nil {
		t.Fatal(err)
	}
	cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, key, testDaemonProto,
		testDaemonHttpsAddr, getTlsConfig("client-cert.pem", "client-key.pem", t))

	setTimeout(t, "Reading command output time out", 10*time.Second, func() {
		if err := cli.CmdInfo(); err != nil {
			t.Fatal(err)
		}
	})
}

// TestHttpsInfoRogueCert connects via two-way authenticated HTTPS to the info endpoint
// by using a rogue client certificate and checks that it fails with the expected error.
func TestHttpsInfoRogueCert(t *testing.T) {
	key, err := libtrust.GenerateECP256PrivateKey()
	if err != nil {
		t.Fatal(err)
	}
	cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, key, testDaemonProto,
		testDaemonHttpsAddr, getTlsConfig("client-rogue-cert.pem", "client-rogue-key.pem", t))

	setTimeout(t, "Reading command output time out", 10*time.Second, func() {
		err := cli.CmdInfo()
		if err == nil {
			t.Fatal("Expected error but got nil")
		}
		if !strings.Contains(err.Error(), errBadCertificate) {
			t.Fatalf("Expected error: %s, got instead: %s", errBadCertificate, err)
		}
	})
}

// TestHttpsInfoRogueServerCert connects via two-way authenticated HTTPS to the info endpoint
// which provides a rogue server certificate and checks that it fails with the expected error
func TestHttpsInfoRogueServerCert(t *testing.T) {
	key, err := libtrust.GenerateECP256PrivateKey()
	if err != nil {
		t.Fatal(err)
	}
	cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, key, testDaemonProto,
		testDaemonRogueHttpsAddr, getTlsConfig("client-cert.pem", "client-key.pem", t))

	setTimeout(t, "Reading command output time out", 10*time.Second, func() {
		err := cli.CmdInfo()
		if err == nil {
			t.Fatal("Expected error but got nil")
		}

		if !strings.Contains(err.Error(), errCaUnknown) {
			t.Fatalf("Expected error: %s, got instead: %s", errBadCertificate, err)
		}

	})
}
