Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: add pouch plugin #1278

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apis/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import (
"syscall"

"github.com/alibaba/pouch/apis/plugins"
"github.com/alibaba/pouch/client"
"github.com/alibaba/pouch/daemon/config"
"github.com/alibaba/pouch/daemon/mgr"
"github.com/alibaba/pouch/pkg/httputils"

"github.com/sirupsen/logrus"
)
Expand Down Expand Up @@ -47,7 +47,7 @@ func (s *Server) Start() (err error) {

var tlsConfig *tls.Config
if s.Config.TLS.Key != "" && s.Config.TLS.Cert != "" {
tlsConfig, err = client.GenTLSConfig(s.Config.TLS.Key, s.Config.TLS.Cert, s.Config.TLS.CA)
tlsConfig, err = httputils.GenTLSConfig(s.Config.TLS.Key, s.Config.TLS.Cert, s.Config.TLS.CA)
if err != nil {
return err
}
Expand Down
82 changes: 5 additions & 77 deletions client/client.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
package client

import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net"
"net/http"
"net/url"
"os"
"strings"
"time"

"github.com/alibaba/pouch/pkg/httputils"
)

var (
Expand Down Expand Up @@ -47,14 +45,14 @@ func NewAPIClient(host string, tls TLSConfig) (CommonAPIClient, error) {
host = defaultHost
}

newURL, basePath, addr, err := parseHost(host)
newURL, basePath, addr, err := httputils.ParseHost(host)
if err != nil {
return nil, fmt.Errorf("failed to parse host %s: %v", host, err)
}

tlsConfig := generateTLSConfig(host, tls)

httpCli := newHTTPClient(newURL, tlsConfig)
httpCli := httputils.NewHTTPClient(newURL, tlsConfig, defaultTimeout)

basePath = generateBaseURL(newURL, tls)

Expand All @@ -72,57 +70,11 @@ func NewAPIClient(host string, tls TLSConfig) (CommonAPIClient, error) {
}, nil
}

// parseHost inputs a host address string, and output three type:
// url.URL, basePath and an error
func parseHost(host string) (*url.URL, string, string, error) {
u, err := url.Parse(host)
if err != nil {
return nil, "", "", err
}

var basePath string
switch u.Scheme {
case "unix":
basePath = "http://d"
case "tcp":
basePath = "http://" + u.Host
case "http":
basePath = host
default:
return nil, "", "", fmt.Errorf("not support url scheme %v", u.Scheme)
}

return u, basePath, strings.TrimPrefix(host, u.Scheme+"://"), nil
}

func newHTTPClient(u *url.URL, tlsConfig *tls.Config) *http.Client {
tr := &http.Transport{
TLSClientConfig: tlsConfig,
}

switch u.Scheme {
case "unix":
unixDial := func(ctx context.Context, network, addr string) (net.Conn, error) {
return net.DialTimeout("unix", u.Path, time.Duration(defaultTimeout))
}
tr.DialContext = unixDial
default:
dial := func(ctx context.Context, network, addr string) (net.Conn, error) {
return net.DialTimeout(network, addr, time.Duration(defaultTimeout))
}
tr.DialContext = dial
}

return &http.Client{
Transport: tr,
}
}

// generateTLSConfig configures TLS for API Client.
func generateTLSConfig(host string, tls TLSConfig) *tls.Config {
// init tls config
if tls.Key != "" && tls.Cert != "" && !strings.HasPrefix(host, "unix://") {
tlsCfg, err := GenTLSConfig(tls.Key, tls.Cert, tls.CA)
tlsCfg, err := httputils.GenTLSConfig(tls.Key, tls.Cert, tls.CA)
if err != nil {
fmt.Fprintf(os.Stderr, "fail to parse tls config %v", err)
os.Exit(1)
Expand Down Expand Up @@ -174,27 +126,3 @@ func (client *APIClient) GetAPIPath(path string, query url.Values) string {
func (client *APIClient) UpdateClientVersion(v string) {
client.version = v
}

// GenTLSConfig returns a tls config object according to inputting parameters.
func GenTLSConfig(key, cert, ca string) (*tls.Config, error) {
tlsConfig := &tls.Config{}
tlsCert, err := tls.LoadX509KeyPair(cert, key)
if err != nil {
return nil, fmt.Errorf("failed to read X509 key pair (cert: %q, key: %q): %v", cert, key, err)
}
tlsConfig.Certificates = []tls.Certificate{tlsCert}
if ca == "" {
return tlsConfig, nil
}

cp := x509.NewCertPool()
pem, err := ioutil.ReadFile(ca)
if err != nil {
return nil, fmt.Errorf("failed to read CA certificate %q: %v", ca, err)
}
if !cp.AppendCertsFromPEM(pem) {
return nil, fmt.Errorf("failed to append certificates from PEM file: %q", ca)
}
tlsConfig.ClientCAs = cp
return tlsConfig, nil
}
60 changes: 0 additions & 60 deletions client/client_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package client

import (
"crypto/tls"
"fmt"
"net/url"
"reflect"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -35,36 +33,6 @@ func TestNewAPIClient(t *testing.T) {
}
}

func TestParseHost(t *testing.T) {
assert := assert.New(t)
type parsed struct {
host string
expectError bool
expectBasePath string
expectAddr string
}

parseds := []parsed{
{host: testHost, expectError: false, expectBasePath: "http://d", expectAddr: "/var/run/pouchd.sock"},
{host: "tcp://localhost:1234", expectError: false, expectBasePath: "http://localhost:1234", expectAddr: "localhost:1234"},
{host: "http://localhost:5678", expectError: false, expectBasePath: "http://localhost:5678", expectAddr: "localhost:5678"},
{host: "foo:bar", expectError: true, expectBasePath: "", expectAddr: ""},
{host: "", expectError: true, expectBasePath: "", expectAddr: ""},
}

for _, p := range parseds {
_, basePath, addr, err := parseHost(p.host)
if p.expectError {
assert.Error(err, fmt.Sprintf("test data %v", p.host))
} else {
assert.NoError(err, fmt.Sprintf("test data %v", p.host))
}

assert.Equal(basePath, p.expectBasePath)
assert.Equal(addr, p.expectAddr)
}
}

func Test_generateBaseURL(t *testing.T) {
type args struct {
u *url.URL
Expand All @@ -85,31 +53,3 @@ func Test_generateBaseURL(t *testing.T) {
})
}
}

func TestGenTLSConfig(t *testing.T) {
type args struct {
key string
cert string
ca string
}
tests := []struct {
name string
args args
want *tls.Config
wantErr bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := GenTLSConfig(tt.args.key, tt.args.cert, tt.args.ca)
if (err != nil) != tt.wantErr {
t.Errorf("GenTLSConfig() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("GenTLSConfig() = %v, want %v", got, tt.want)
}
})
}
}
87 changes: 87 additions & 0 deletions pkg/httputils/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package httputils

import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net"
"net/http"
"net/url"
"strings"
"time"
)

// ParseHost inputs a host address string, and output four type:
// url.URL, basePath, address without scheme and an error.
func ParseHost(host string) (*url.URL, string, string, error) {
u, err := url.Parse(host)
if err != nil {
return nil, "", "", err
}

var basePath string
switch u.Scheme {
case "unix":
basePath = "http://d"
case "tcp":
basePath = "http://" + u.Host
case "http":
basePath = host
case "https":
basePath = host
default:
return nil, "", "", fmt.Errorf("not support url scheme %v", u.Scheme)
}

return u, basePath, strings.TrimPrefix(host, u.Scheme+"://"), nil
}

// GenTLSConfig returns a tls config object according to inputting parameters.
func GenTLSConfig(key, cert, ca string) (*tls.Config, error) {
tlsConfig := &tls.Config{}
tlsCert, err := tls.LoadX509KeyPair(cert, key)
if err != nil {
return nil, fmt.Errorf("failed to read X509 key pair (cert: %q, key: %q): %v", cert, key, err)
}
tlsConfig.Certificates = []tls.Certificate{tlsCert}
if ca == "" {
return tlsConfig, nil
}

cp := x509.NewCertPool()
pem, err := ioutil.ReadFile(ca)
if err != nil {
return nil, fmt.Errorf("failed to read CA certificate %q: %v", ca, err)
}
if !cp.AppendCertsFromPEM(pem) {
return nil, fmt.Errorf("failed to append certificates from PEM file: %q", ca)
}
tlsConfig.ClientCAs = cp
return tlsConfig, nil
}

// NewHTTPClient creates a http client using url and tlsconfig
func NewHTTPClient(u *url.URL, tlsConfig *tls.Config, dialTimeout time.Duration) *http.Client {
tr := &http.Transport{
TLSClientConfig: tlsConfig,
}

switch u.Scheme {
case "unix":
unixDial := func(ctx context.Context, network, addr string) (net.Conn, error) {
return net.DialTimeout("unix", u.Path, dialTimeout)
}
tr.DialContext = unixDial
default:
dial := func(ctx context.Context, network, addr string) (net.Conn, error) {
return net.DialTimeout(network, addr, dialTimeout)
}
tr.DialContext = dial
}

return &http.Client{
Transport: tr,
}
}
72 changes: 72 additions & 0 deletions pkg/httputils/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package httputils

import (
"crypto/tls"
"fmt"
"reflect"
"testing"

"github.com/stretchr/testify/assert"
)

var (
testHost = "unix:///var/run/pouchd.sock"
)

func TestParseHost(t *testing.T) {
assert := assert.New(t)
type parsed struct {
host string
expectError bool
expectBasePath string
expectAddr string
}

parseds := []parsed{
{host: testHost, expectError: false, expectBasePath: "http://d", expectAddr: "/var/run/pouchd.sock"},
{host: "tcp://localhost:1234", expectError: false, expectBasePath: "http://localhost:1234", expectAddr: "localhost:1234"},
{host: "http://localhost:5678", expectError: false, expectBasePath: "http://localhost:5678", expectAddr: "localhost:5678"},
{host: "foo:bar", expectError: true, expectBasePath: "", expectAddr: ""},
{host: "", expectError: true, expectBasePath: "", expectAddr: ""},
}

for _, p := range parseds {
_, basePath, addr, err := ParseHost(p.host)
if p.expectError {
assert.Error(err, fmt.Sprintf("test data %v", p.host))
} else {
assert.NoError(err, fmt.Sprintf("test data %v", p.host))
}

assert.Equal(basePath, p.expectBasePath)
assert.Equal(addr, p.expectAddr)
}
}

func TestGenTLSConfig(t *testing.T) {
type args struct {
key string
cert string
ca string
}
tests := []struct {
name string
args args
want *tls.Config
wantErr bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := GenTLSConfig(tt.args.key, tt.args.cert, tt.args.ca)
if (err != nil) != tt.wantErr {
t.Errorf("GenTLSConfig() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("GenTLSConfig() = %v, want %v", got, tt.want)
}
})
}
}
Loading