<commit_before>package rpc

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"strconv"
	"time"

	"github.com/wandoulabs/codis/pkg/utils/atomic2"
	"github.com/wandoulabs/codis/pkg/utils/errors"
	"github.com/wandoulabs/codis/pkg/utils/log"
	"github.com/wandoulabs/codis/pkg/utils/trace"
)

const (
	MethodGet = "GET"
	MethodPut = "PUT"
)

var client *http.Client

func init() {
	var dials atomic2.Int64
	tr := &http.Transport{}
	tr.Dial = func(network, addr string) (net.Conn, error) {
		c, err := net.DialTimeout(network, addr, time.Second*30)
		if err == nil {
			log.Debugf("rpc: dial new connection to [%d] %s - %s",
				dials.Incr()-1, network, addr)
		}
		return c, err
	}
	client = &http.Client{
		Transport: tr,
		Timeout:   time.Minute,
	}
}

type rpcError struct {
	Cause string
	Stack trace.Stack
}

func responseBodyAsBytes(rsp *http.Response) ([]byte, error) {
	b, err := ioutil.ReadAll(rsp.Body)
	if err != nil {
		return nil, errors.Trace(err)
	}
	return b, nil
}

func responseBodyAsError(rsp *http.Response) (error, error) {
	b, err := responseBodyAsBytes(rsp)
	if err != nil {
		return nil, err
	}
	if len(b) == 0 {
		return nil, errors.Errorf("[Remote Error] Unknown Error")
	}
	e := &rpcError{}
	if err := json.Unmarshal(b, e); err != nil {
		return nil, errors.Trace(err)
	}
	return &errors.TracedError{
		Cause: errors.New("[Remote Error] " + e.Cause),
		Stack: e.Stack,
	}, nil
}

func apiRequestJson(method string, url string, args, reply interface{}) error {
	var body []byte
	if args != nil {
		b, err := json.MarshalIndent(args, "", "    ")
		if err != nil {
			return errors.Trace(err)
		}
		body = b
	}

	req, err := http.NewRequest(method, url, bytes.NewReader(body))
	if err != nil {
		return errors.Trace(err)
	}
	if body != nil {
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("Content-Length", strconv.Itoa(len(body)))
	}

	rsp, err := client.Do(req)
	if err != nil {
		return errors.Trace(err)
	}
	defer func() {
		io.Copy(ioutil.Discard, rsp.Body)
		rsp.Body.Close()
	}()

	switch rsp.StatusCode {
	case 200:
		b, err := responseBodyAsBytes(rsp)
		if err != nil {
			return err
		}
		if reply == nil {
			return nil
		}
		if err := json.Unmarshal(b, reply); err != nil {
			return errors.Trace(err)
		} else {
			return nil
		}
	case 1500:
		e, err := responseBodyAsError(rsp)
		if err != nil {
			return err
		} else {
			return e
		}
	default:
		return errors.Errorf("[%d] %s", rsp.StatusCode, http.StatusText(rsp.StatusCode))
	}
}

func ApiGetJson(url string, reply interface{}) error {
	return apiRequestJson(MethodGet, url, nil, reply)
}

func ApiPutJson(url string, args, reply interface{}) error {
	return apiRequestJson(MethodPut, url, args, reply)
}

func ApiResponseError(err error) (int, string) {
	if err == nil {
		return 1500, ""
	}
	e := &rpcError{
		Cause: err.Error(),
		Stack: errors.Stack(err),
	}
	b, err := json.MarshalIndent(e, "", "    ")
	if err != nil {
		return 1500, ""
	} else {
		return 1500, string(b)
	}
}

func ApiResponseJson(v interface{}) (int, string) {
	b, err := json.MarshalIndent(v, "", "    ")
	if err != nil {
		return ApiResponseError(errors.Trace(err))
	} else {
		return 200, string(b)
	}
}

func EncodeURL(addr string, format string, args ...interface{}) string {
	return "http://" + addr + fmt.Sprintf(format, args...)
}
<commit_msg>Update, close idle http clients<commit_after>package rpc

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"strconv"
	"time"

	"github.com/wandoulabs/codis/pkg/utils/atomic2"
	"github.com/wandoulabs/codis/pkg/utils/errors"
	"github.com/wandoulabs/codis/pkg/utils/log"
	"github.com/wandoulabs/codis/pkg/utils/trace"
)

const (
	MethodGet = "GET"
	MethodPut = "PUT"
)

var client *http.Client

func init() {
	var dials atomic2.Int64
	tr := &http.Transport{}
	tr.Dial = func(network, addr string) (net.Conn, error) {
		c, err := net.DialTimeout(network, addr, time.Second*30)
		if err == nil {
			log.Debugf("rpc: dial new connection to [%d] %s - %s",
				dials.Incr()-1, network, addr)
		}
		return c, err
	}
	client = &http.Client{
		Transport: tr,
		Timeout:   time.Minute,
	}
	go func() {
		for {
			time.Sleep(time.Minute)
			tr.CloseIdleConnections()
		}
	}()
}

type rpcError struct {
	Cause string
	Stack trace.Stack
}

func responseBodyAsBytes(rsp *http.Response) ([]byte, error) {
	b, err := ioutil.ReadAll(rsp.Body)
	if err != nil {
		return nil, errors.Trace(err)
	}
	return b, nil
}

func responseBodyAsError(rsp *http.Response) (error, error) {
	b, err := responseBodyAsBytes(rsp)
	if err != nil {
		return nil, err
	}
	if len(b) == 0 {
		return nil, errors.Errorf("[Remote Error] Unknown Error")
	}
	e := &rpcError{}
	if err := json.Unmarshal(b, e); err != nil {
		return nil, errors.Trace(err)
	}
	return &errors.TracedError{
		Cause: errors.New("[Remote Error] " + e.Cause),
		Stack: e.Stack,
	}, nil
}

func apiRequestJson(method string, url string, args, reply interface{}) error {
	var body []byte
	if args != nil {
		b, err := json.MarshalIndent(args, "", "    ")
		if err != nil {
			return errors.Trace(err)
		}
		body = b
	}

	req, err := http.NewRequest(method, url, bytes.NewReader(body))
	if err != nil {
		return errors.Trace(err)
	}
	if body != nil {
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("Content-Length", strconv.Itoa(len(body)))
	}

	rsp, err := client.Do(req)
	if err != nil {
		return errors.Trace(err)
	}
	defer func() {
		io.Copy(ioutil.Discard, rsp.Body)
		rsp.Body.Close()
	}()

	switch rsp.StatusCode {
	case 200:
		b, err := responseBodyAsBytes(rsp)
		if err != nil {
			return err
		}
		if reply == nil {
			return nil
		}
		if err := json.Unmarshal(b, reply); err != nil {
			return errors.Trace(err)
		} else {
			return nil
		}
	case 1500:
		e, err := responseBodyAsError(rsp)
		if err != nil {
			return err
		} else {
			return e
		}
	default:
		return errors.Errorf("[%d] %s", rsp.StatusCode, http.StatusText(rsp.StatusCode))
	}
}

func ApiGetJson(url string, reply interface{}) error {
	return apiRequestJson(MethodGet, url, nil, reply)
}

func ApiPutJson(url string, args, reply interface{}) error {
	return apiRequestJson(MethodPut, url, args, reply)
}

func ApiResponseError(err error) (int, string) {
	if err == nil {
		return 1500, ""
	}
	e := &rpcError{
		Cause: err.Error(),
		Stack: errors.Stack(err),
	}
	b, err := json.MarshalIndent(e, "", "    ")
	if err != nil {
		return 1500, ""
	} else {
		return 1500, string(b)
	}
}

func ApiResponseJson(v interface{}) (int, string) {
	b, err := json.MarshalIndent(v, "", "    ")
	if err != nil {
		return ApiResponseError(errors.Trace(err))
	} else {
		return 200, string(b)
	}
}

func EncodeURL(addr string, format string, args ...interface{}) string {
	return "http://" + addr + fmt.Sprintf(format, args...)
}
<|endoftext|>


<commit_before>// Copyright 2009 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.

package syscall

import "unsafe"

func Getpagesize() int	{ return 4096 }

func TimespecToNsec(ts Timespec) int64	{ return int64(ts.Sec)*1e9 + int64(ts.Nsec) }

func NsecToTimespec(nsec int64) (ts Timespec) {
	ts.Sec = int32(nsec / 1e9);
	ts.Nsec = int32(nsec % 1e9);
	return;
}

func TimevalToNsec(tv Timeval) int64	{ return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }

func NsecToTimeval(nsec int64) (tv Timeval) {
	nsec += 999;	// round up to microsecond
	tv.Sec = int32(nsec / 1e9);
	tv.Usec = int32(nsec % 1e9 / 1e3);
	return;
}

// 64-bit file system and 32-bit uid calls
// (386 default is 32-bit file system and 16-bit uid).
//sys	Chown(path string, uid int, gid int) (errno int) = SYS_CHOWN32
//sys	Fchown(fd int, uid int, gid int) (errno int) = SYS_FCHOWN32
//sys	Fstat(fd int, stat *Stat_t) (errno int) = SYS_FSTAT64
//sys	Fstatfs(fd int, buf *Statfs_t) (errno int) = SYS_FSTATFS64
//sys	Getegid() (egid int) = SYS_GETEGID32
//sys	Geteuid() (euid int) = SYS_GETEUID32
//sys	Getgid() (gid int) = SYS_GETGID32
//sys	Getuid() (uid int) = SYS_GETUID32
//sys	Ioperm(from int, num int, on int) (errno int)
//sys	Iopl(level int) (errno int)
//sys	Lchown(path string, uid int, gid int) (errno int) = SYS_LCHOWN32
//sys	Lstat(path string, stat *Stat_t) (errno int) = SYS_LSTAT64
//sys	Setfsgid(gid int) (errno int) = SYS_SETFSGID32
//sys	Setfsuid(uid int) (errno int) = SYS_SETFSUID32
//sys	Setgid(gid int) (errno int) = SYS_SETGID32
//sys	Setregid(rgid int, egid int) (errno int) = SYS_SETREGID32
//sys	Setresgid(rgid int, egid int, sgid int) (errno int) = SYS_SETRESGID32
//sys	Setresuid(ruid int, euid int, suid int) (errno int) = SYS_SETRESUID32
//sys	Setreuid(ruid int, euid int) (errno int) = SYS_SETREUID32
//sys	Stat(path string, stat *Stat_t) (errno int) = SYS_STAT64
//sys	Statfs(path string, buf *Statfs_t) (errno int) = SYS_STATFS64
//sys	SyncFileRange(fd int, off int64, n int64, flags int) (errno int)
//sys	getgroups(n int, list *_Gid_t) (nn int, errno int) = SYS_GETGROUPS32
//sys	setgroups(n int, list *_Gid_t) (errno int) = SYS_SETGROUPS32
//sys	Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, errno int) = SYS__NEWSELECT

// Underlying system call writes to newoffset via pointer.
// Implemented in assembly to avoid allocation.
func Seek(fd int, offset int64, whence int) (newoffset int64, errno int)

// On x86 Linux, all the socket calls go through an extra indirection,
// I think because the 5-register system call interface can't handle
// the 6-argument calls like sendto and recvfrom.  Instead the
// arguments to the underlying system call are the number below
// and a pointer to an array of uintptr.  We hide the pointer in the
// socketcall assembly to avoid allocation on every system call.

const (
	// see linux/net.h
	_SOCKET		= 1;
	_BIND		= 2;
	_CONNECT	= 3;
	_LISTEN		= 4;
	_ACCEPT		= 5;
	_GETSOCKNAME	= 6;
	_GETPEERNAME	= 7;
	_SOCKETPAIR	= 8;
	_SEND		= 9;
	_RECV		= 10;
	_SENDTO		= 11;
	_RECVFROM	= 12;
	_SHUTDOWN	= 13;
	_SETSOCKOPT	= 14;
	_GETSOCKOPT	= 15;
	_SENDMSG	= 16;
	_RECVMSG	= 17;
)

func socketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, errno int)

func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, errno int) {
	fd, errno = socketcall(_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), 0, 0, 0);
	return;
}

func getsockname(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) {
	_, errno = socketcall(_GETSOCKNAME, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), 0, 0, 0);
	return;
}

func getpeername(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) {
	_, errno = socketcall(_GETPEERNAME, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), 0, 0, 0);
	return;
}

func bind(s int, addr uintptr, addrlen _Socklen) (errno int) {
	_, errno = socketcall(_BIND, uintptr(s), uintptr(addr), uintptr(addrlen), 0, 0, 0);
	return;
}

func connect(s int, addr uintptr, addrlen _Socklen) (errno int) {
	_, errno = socketcall(_CONNECT, uintptr(s), uintptr(addr), uintptr(addrlen), 0, 0, 0);
	return;
}

func socket(domain int, typ int, proto int) (fd int, errno int) {
	fd, errno = socketcall(_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto), 0, 0, 0);
	return;
}

func setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int) {
	_, errno = socketcall(_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0);
	return;
}

func recvfrom(s int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, errno int) {
	var base uintptr;
	if len(p) > 0 {
		base = uintptr(unsafe.Pointer(&p[0]))
	}
	n, errno = socketcall(_RECVFROM, uintptr(s), base, uintptr(len(p)), uintptr(flags), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen)));
	return;
}

func sendto(s int, p []byte, flags int, to uintptr, addrlen _Socklen) (errno int) {
	var base uintptr;
	if len(p) > 0 {
		base = uintptr(unsafe.Pointer(&p[0]))
	}
	_, errno = socketcall(_SENDTO, uintptr(s), base, uintptr(len(p)), uintptr(flags), to, uintptr(addrlen));
	return;
}

func Listen(s int, n int) (errno int) {
	_, errno = socketcall(_LISTEN, uintptr(s), uintptr(n), 0, 0, 0, 0);
	return;
}

func (r *PtraceRegs) PC() uint64	{ return uint64(uint32(r.Eip)) }

func (r *PtraceRegs) SetPC(pc uint64)	{ r.Eip = int32(pc) }
<commit_msg>Add Shutdown to 32-bit GNU/Linux build.<commit_after>// Copyright 2009 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.

package syscall

import "unsafe"

func Getpagesize() int	{ return 4096 }

func TimespecToNsec(ts Timespec) int64	{ return int64(ts.Sec)*1e9 + int64(ts.Nsec) }

func NsecToTimespec(nsec int64) (ts Timespec) {
	ts.Sec = int32(nsec / 1e9);
	ts.Nsec = int32(nsec % 1e9);
	return;
}

func TimevalToNsec(tv Timeval) int64	{ return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }

func NsecToTimeval(nsec int64) (tv Timeval) {
	nsec += 999;	// round up to microsecond
	tv.Sec = int32(nsec / 1e9);
	tv.Usec = int32(nsec % 1e9 / 1e3);
	return;
}

// 64-bit file system and 32-bit uid calls
// (386 default is 32-bit file system and 16-bit uid).
//sys	Chown(path string, uid int, gid int) (errno int) = SYS_CHOWN32
//sys	Fchown(fd int, uid int, gid int) (errno int) = SYS_FCHOWN32
//sys	Fstat(fd int, stat *Stat_t) (errno int) = SYS_FSTAT64
//sys	Fstatfs(fd int, buf *Statfs_t) (errno int) = SYS_FSTATFS64
//sys	Getegid() (egid int) = SYS_GETEGID32
//sys	Geteuid() (euid int) = SYS_GETEUID32
//sys	Getgid() (gid int) = SYS_GETGID32
//sys	Getuid() (uid int) = SYS_GETUID32
//sys	Ioperm(from int, num int, on int) (errno int)
//sys	Iopl(level int) (errno int)
//sys	Lchown(path string, uid int, gid int) (errno int) = SYS_LCHOWN32
//sys	Lstat(path string, stat *Stat_t) (errno int) = SYS_LSTAT64
//sys	Setfsgid(gid int) (errno int) = SYS_SETFSGID32
//sys	Setfsuid(uid int) (errno int) = SYS_SETFSUID32
//sys	Setgid(gid int) (errno int) = SYS_SETGID32
//sys	Setregid(rgid int, egid int) (errno int) = SYS_SETREGID32
//sys	Setresgid(rgid int, egid int, sgid int) (errno int) = SYS_SETRESGID32
//sys	Setresuid(ruid int, euid int, suid int) (errno int) = SYS_SETRESUID32
//sys	Setreuid(ruid int, euid int) (errno int) = SYS_SETREUID32
//sys	Stat(path string, stat *Stat_t) (errno int) = SYS_STAT64
//sys	Statfs(path string, buf *Statfs_t) (errno int) = SYS_STATFS64
//sys	SyncFileRange(fd int, off int64, n int64, flags int) (errno int)
//sys	getgroups(n int, list *_Gid_t) (nn int, errno int) = SYS_GETGROUPS32
//sys	setgroups(n int, list *_Gid_t) (errno int) = SYS_SETGROUPS32
//sys	Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, errno int) = SYS__NEWSELECT

// Underlying system call writes to newoffset via pointer.
// Implemented in assembly to avoid allocation.
func Seek(fd int, offset int64, whence int) (newoffset int64, errno int)

// On x86 Linux, all the socket calls go through an extra indirection,
// I think because the 5-register system call interface can't handle
// the 6-argument calls like sendto and recvfrom.  Instead the
// arguments to the underlying system call are the number below
// and a pointer to an array of uintptr.  We hide the pointer in the
// socketcall assembly to avoid allocation on every system call.

const (
	// see linux/net.h
	_SOCKET		= 1;
	_BIND		= 2;
	_CONNECT	= 3;
	_LISTEN		= 4;
	_ACCEPT		= 5;
	_GETSOCKNAME	= 6;
	_GETPEERNAME	= 7;
	_SOCKETPAIR	= 8;
	_SEND		= 9;
	_RECV		= 10;
	_SENDTO		= 11;
	_RECVFROM	= 12;
	_SHUTDOWN	= 13;
	_SETSOCKOPT	= 14;
	_GETSOCKOPT	= 15;
	_SENDMSG	= 16;
	_RECVMSG	= 17;
)

func socketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, errno int)

func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, errno int) {
	fd, errno = socketcall(_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), 0, 0, 0);
	return;
}

func getsockname(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) {
	_, errno = socketcall(_GETSOCKNAME, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), 0, 0, 0);
	return;
}

func getpeername(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) {
	_, errno = socketcall(_GETPEERNAME, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), 0, 0, 0);
	return;
}

func bind(s int, addr uintptr, addrlen _Socklen) (errno int) {
	_, errno = socketcall(_BIND, uintptr(s), uintptr(addr), uintptr(addrlen), 0, 0, 0);
	return;
}

func connect(s int, addr uintptr, addrlen _Socklen) (errno int) {
	_, errno = socketcall(_CONNECT, uintptr(s), uintptr(addr), uintptr(addrlen), 0, 0, 0);
	return;
}

func socket(domain int, typ int, proto int) (fd int, errno int) {
	fd, errno = socketcall(_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto), 0, 0, 0);
	return;
}

func setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int) {
	_, errno = socketcall(_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0);
	return;
}

func recvfrom(s int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, errno int) {
	var base uintptr;
	if len(p) > 0 {
		base = uintptr(unsafe.Pointer(&p[0]))
	}
	n, errno = socketcall(_RECVFROM, uintptr(s), base, uintptr(len(p)), uintptr(flags), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen)));
	return;
}

func sendto(s int, p []byte, flags int, to uintptr, addrlen _Socklen) (errno int) {
	var base uintptr;
	if len(p) > 0 {
		base = uintptr(unsafe.Pointer(&p[0]))
	}
	_, errno = socketcall(_SENDTO, uintptr(s), base, uintptr(len(p)), uintptr(flags), to, uintptr(addrlen));
	return;
}

func Listen(s int, n int) (errno int) {
	_, errno = socketcall(_LISTEN, uintptr(s), uintptr(n), 0, 0, 0, 0);
	return;
}

func Shutdown(s, how int) (errno int) {
	_, errno = socketcall(_SHUTDOWN, uintptr(s), uintptr(how), 0, 0, 0, 0);
	return;
}

func (r *PtraceRegs) PC() uint64	{ return uint64(uint32(r.Eip)) }

func (r *PtraceRegs) SetPC(pc uint64)	{ r.Eip = int32(pc) }
<|endoftext|>


<commit_before>/*
Copyright 2014 Huawei Technologies Co., Ltd. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package middleware

import (
	"gopkg.in/macaron.v1"
)

func SetMiddlewares(m *macaron.Macaron) {
	//Set static file directory,static file access without log output
	m.Use(macaron.Static("external", macaron.StaticOptions{
		Expires: func() string { return "max-age=0" },
	}))

	//Set recovery handler to returns a middleware that recovers from any panics
	m.Use(macaron.Recovery())

	m.Use(func(ctx *macaron.Context) {
		ctx.Resp.Header().Set("Access-Control-Allow-Origin", "*")
	})
}
<commit_msg>add middleware allow all options request<commit_after>/*
Copyright 2014 Huawei Technologies Co., Ltd. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package middleware

import (
	"gopkg.in/macaron.v1"
)

func SetMiddlewares(m *macaron.Macaron) {
	//Set static file directory,static file access without log output
	m.Use(macaron.Static("external", macaron.StaticOptions{
		Expires: func() string { return "max-age=0" },
	}))

	//Set recovery handler to returns a middleware that recovers from any panics
	m.Use(macaron.Recovery())

	m.Use(func(ctx *macaron.Context) {
		ctx.Resp.Header().Set("Access-Control-Allow-Origin", "*")
		ctx.Resp.Header().Set("Access-Control-Allow-Methods", "POST,PUT,DELETE")
	})

	m.Use(func(ctx *macaron.Context) {
		if ctx.Req.Method == "OPTIONS" {
			ctx.Resp.Write([]byte("success"))
			ctx.Resp.Flush()
		}
	})
}
<|endoftext|>


<commit_before>// Package instant implements an addon that executes code whenever it changes
// and shows the result.
package instant

import (
	"github.com/elves/elvish/pkg/cli"
	"github.com/elves/elvish/pkg/cli/term"
	"github.com/elves/elvish/pkg/ui"
)

// Config keeps the configuration for the instant addon.
type Config struct {
	// Keybinding.
	Binding cli.Handler
	// The function to execute code and returns the output.
	Execute func(code string) ([]string, error)
}

type widget struct {
	Config
	app      cli.App
	textView cli.TextView
	lastCode string
	lastErr  error
}

func (w *widget) Render(width, height int) *term.Buffer {
	bb := term.NewBufferBuilder(width).
		WriteStyled(cli.ModeLine(" INSTANT ", false)).SetDotHere()
	if w.lastErr != nil {
		bb.Newline().Write(w.lastErr.Error(), ui.FgRed)
	}
	buf := bb.Buffer()
	if len(buf.Lines) >= height {
		buf.TrimToLines(0, height)
		return buf
	}
	bufTextView := w.textView.Render(width, height-len(buf.Lines))
	buf.Extend(bufTextView, false)
	return buf
}

func (w *widget) Focus() bool { return false }

func (w *widget) Handle(event term.Event) bool {
	handled := w.Binding.Handle(event)
	if !handled {
		codeArea := w.app.CodeArea()
		handled = codeArea.Handle(event)
	}
	w.update(false)
	return handled
}

func (w *widget) update(force bool) {
	code := w.app.CodeArea().CopyState().Buffer.Content
	if code == w.lastCode && !force {
		return
	}
	w.lastCode = code
	output, err := w.Execute(code)
	w.lastErr = err
	if err == nil {
		w.textView.MutateState(func(s *cli.TextViewState) {
			*s = cli.TextViewState{Lines: output, First: 0}
		})
	}
}

// Start starts the addon.
func Start(app cli.App, cfg Config) {
	if cfg.Execute == nil {
		app.Notify("executor is required")
		return
	}
	if cfg.Binding == nil {
		cfg.Binding = cli.DummyHandler{}
	}
	w := widget{
		Config:   cfg,
		app:      app,
		textView: cli.NewTextView(cli.TextViewSpec{Scrollable: true}),
	}
	app.MutateState(func(s *cli.State) { s.Addon = &w })
	w.update(true)
	app.Redraw()
}
<commit_msg>pkg/cli/addons/instant: Fix a race condition.<commit_after>// Package instant implements an addon that executes code whenever it changes
// and shows the result.
package instant

import (
	"github.com/elves/elvish/pkg/cli"
	"github.com/elves/elvish/pkg/cli/term"
	"github.com/elves/elvish/pkg/ui"
)

// Config keeps the configuration for the instant addon.
type Config struct {
	// Keybinding.
	Binding cli.Handler
	// The function to execute code and returns the output.
	Execute func(code string) ([]string, error)
}

type widget struct {
	Config
	app      cli.App
	textView cli.TextView
	lastCode string
	lastErr  error
}

func (w *widget) Render(width, height int) *term.Buffer {
	bb := term.NewBufferBuilder(width).
		WriteStyled(cli.ModeLine(" INSTANT ", false)).SetDotHere()
	if w.lastErr != nil {
		bb.Newline().Write(w.lastErr.Error(), ui.FgRed)
	}
	buf := bb.Buffer()
	if len(buf.Lines) >= height {
		buf.TrimToLines(0, height)
		return buf
	}
	bufTextView := w.textView.Render(width, height-len(buf.Lines))
	buf.Extend(bufTextView, false)
	return buf
}

func (w *widget) Focus() bool { return false }

func (w *widget) Handle(event term.Event) bool {
	handled := w.Binding.Handle(event)
	if !handled {
		codeArea := w.app.CodeArea()
		handled = codeArea.Handle(event)
	}
	w.update(false)
	return handled
}

func (w *widget) update(force bool) {
	code := w.app.CodeArea().CopyState().Buffer.Content
	if code == w.lastCode && !force {
		return
	}
	w.lastCode = code
	output, err := w.Execute(code)
	w.lastErr = err
	if err == nil {
		w.textView.MutateState(func(s *cli.TextViewState) {
			*s = cli.TextViewState{Lines: output, First: 0}
		})
	}
}

// Start starts the addon.
func Start(app cli.App, cfg Config) {
	if cfg.Execute == nil {
		app.Notify("executor is required")
		return
	}
	if cfg.Binding == nil {
		cfg.Binding = cli.DummyHandler{}
	}
	w := widget{
		Config:   cfg,
		app:      app,
		textView: cli.NewTextView(cli.TextViewSpec{Scrollable: true}),
	}
	w.update(true)
	app.MutateState(func(s *cli.State) { s.Addon = &w })
	app.Redraw()
}
<|endoftext|>


<commit_before>package kubernetes

import (
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"reflect"
	"time"

	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/record"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/cadvisor"
	kconfig "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/config"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/proxy"
	pconfig "github.com/GoogleCloudPlatform/kubernetes/pkg/proxy/config"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	kexec "github.com/GoogleCloudPlatform/kubernetes/pkg/util/exec"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util/iptables"
	dockerclient "github.com/fsouza/go-dockerclient"
	"github.com/golang/glog"

	cmdutil "github.com/openshift/origin/pkg/cmd/util"
	dockerutil "github.com/openshift/origin/pkg/cmd/util/docker"
	"github.com/openshift/origin/pkg/kubelet/app"
	"github.com/openshift/origin/pkg/service"
)

type commandExecutor interface {
	LookPath(executable string) (string, error)
	Run(command string, args ...string) error
}

type defaultCommandExecutor struct{}

func (ce defaultCommandExecutor) LookPath(executable string) (string, error) {
	return exec.LookPath(executable)
}

func (ce defaultCommandExecutor) Run(command string, args ...string) error {
	c := exec.Command(command, args...)
	return c.Run()
}

const minimumDockerAPIVersionWithPullByID = "1.18"

// EnsureDocker attempts to connect to the Docker daemon defined by the helper,
// and if it is unable to it will print a warning.
func (c *NodeConfig) EnsureDocker(docker *dockerutil.Helper) {
	dockerClient, dockerAddr := docker.GetClientOrExit()
	if err := dockerClient.Ping(); err != nil {
		if !c.AllowDisabledDocker {
			glog.Fatalf("ERROR: Docker could not be reached at %s.  Docker must be installed and running to start containers.\n%v", dockerAddr, err)
		}
		glog.Errorf("WARNING: Docker could not be reached at %s.  Docker must be installed and running to start containers.\n%v", dockerAddr, err)
		c.DockerClient = &dockertools.FakeDockerClient{VersionInfo: dockerclient.Env{"apiversion=1.15"}}
	} else {
		glog.Infof("Connecting to Docker at %s", dockerAddr)

		env, err := dockerClient.Version()
		if err != nil {
			glog.Fatalf("ERROR: Unable to check for Docker server version.\n%v", err)
		}

		serverVersionString := env.Get("ApiVersion")
		serverVersion, err := dockerclient.NewAPIVersion(serverVersionString)
		if err != nil {
			glog.Fatalf("ERROR: Unable to determine Docker server version from %q.\n%v", serverVersionString, err)
		}

		minimumPullByIDVersion, err := dockerclient.NewAPIVersion(minimumDockerAPIVersionWithPullByID)
		if err != nil {
			glog.Fatalf("ERROR: Unable to check for Docker server version.\n%v", err)
		}

		if serverVersion.LessThan(minimumPullByIDVersion) {
			glog.Fatal("ERROR: Docker 1.6 or later (server API version 1.18 or later) required.")
		}

		c.DockerClient = dockerClient
	}
}

// EnsureVolumeDir attempts to convert the provided volume directory argument to
// an absolute path and create the directory if it does not exist. Will exit if
// an error is encountered.
func (c *NodeConfig) EnsureVolumeDir() {
	if volumeDir, err := c.initializeVolumeDir(&defaultCommandExecutor{}, c.VolumeDir); err != nil {
		glog.Fatal(err)
	} else {
		c.VolumeDir = volumeDir
	}
}

func (c *NodeConfig) initializeVolumeDir(ce commandExecutor, path string) (string, error) {
	rootDirectory, err := filepath.Abs(path)
	if err != nil {
		return "", fmt.Errorf("Error converting volume directory to an absolute path: %v", err)
	}

	if _, err := os.Stat(rootDirectory); os.IsNotExist(err) {
		if err := os.MkdirAll(rootDirectory, 0750); err != nil {
			return "", fmt.Errorf("Couldn't create kubelet volume root directory '%s': %s", rootDirectory, err)
		}
		if chconPath, err := ce.LookPath("chcon"); err != nil {
			glog.V(2).Infof("Couldn't locate 'chcon' to set the kubelet volume root directory SELinux context: %s", err)
		} else {
			if err := ce.Run(chconPath, "-t", "svirt_sandbox_file_t", rootDirectory); err != nil {
				glog.Warningf("Error running 'chcon' to set the kubelet volume root directory SELinux context: %s", err)
			}
		}
	}
	return rootDirectory, nil
}

// RunKubelet starts the Kubelet.
func (c *NodeConfig) RunKubelet() {
	// TODO: clean this up and make it more formal (service named 'dns'?). Use multiple ports.
	clusterDNS := c.ClusterDNS
	if clusterDNS == nil {
		if service, err := c.Client.Endpoints(kapi.NamespaceDefault).Get("kubernetes"); err == nil {
			if ip, ok := firstIP(service, 53); ok {
				if err := cmdutil.WaitForSuccessfulDial(false, "tcp", fmt.Sprintf("%s:%d", ip, 53), 50*time.Millisecond, 0, 2); err == nil {
					clusterDNS = net.ParseIP(ip)
				}
			}
		}
	}

	cadvisorInterface, err := cadvisor.New(4194)
	if err == nil {
		// TODO: use VersionInfo after the next rebase
		_, err = cadvisorInterface.MachineInfo()
	}
	if err != nil {
		glog.Errorf("WARNING: cAdvisor cannot be started: %v", err)
		cadvisorInterface = &cadvisor.Fake{}
	}

	hostNetworkCapabilities := []string{kubelet.ApiserverSource, kubelet.FileSource}
	// initialize Kubelet
	// Allow privileged containers
	// TODO: make this configurable and not the default https://github.com/openshift/origin/issues/662
	kubelet.SetupCapabilities(true, hostNetworkCapabilities)
	recorder := record.NewBroadcaster().NewRecorder(kapi.EventSource{Component: "kubelet", Host: c.NodeHost})

	cfg := kconfig.NewPodConfig(kconfig.PodConfigNotificationSnapshotAndUpdates, recorder)
	kconfig.NewSourceApiserver(c.Client, c.NodeHost, cfg.Channel(kubelet.ApiserverSource))
	// define manifest file source for pods, if specified
	if len(c.PodManifestPath) > 0 {
		_, err = os.Stat(c.PodManifestPath)
		if err == nil {
			glog.Infof("Adding pod manifest file/dir: %v", c.PodManifestPath)
			kconfig.NewSourceFile(c.PodManifestPath, c.NodeHost,
				time.Duration(c.PodManifestCheckIntervalSeconds)*time.Second,
				cfg.Channel(kubelet.FileSource))
		} else {
			glog.Errorf("WARNING: PodManifestPath specified is not a valid file/directory: %v", err)
		}
	}

	gcPolicy := kubelet.ContainerGCPolicy{
		MinAge:             10 * time.Second,
		MaxPerPodContainer: 5,
		MaxContainers:      100,
	}
	imageGCPolicy := kubelet.ImageGCPolicy{
		HighThresholdPercent: 90,
		LowThresholdPercent:  80,
	}

	k, err := kubelet.NewMainKubelet(
		c.NodeHost,
		c.DockerClient,
		c.Client,
		c.VolumeDir,
		c.ImageFor("pod"),
		3*time.Second,
		0.0,
		10,
		gcPolicy,
		cfg.SeenAllSources,
		c.ClusterDomain,
		clusterDNS,
		kapi.NamespaceDefault,
		app.ProbeVolumePlugins(),
		app.ProbeNetworkPlugins(),
		c.NetworkPluginName,
		5*time.Minute,
		recorder,
		cadvisorInterface,
		imageGCPolicy,
		nil,
		15*time.Second,
	)
	if err != nil {
		glog.Fatalf("Couldn't run kubelet: %s", err)
	}
	go util.Forever(func() { k.Run(cfg.Updates()) }, 0)

	handler := kubelet.NewServer(k, true)

	server := &http.Server{
		Addr:           c.BindAddress,
		Handler:        &handler,
		ReadTimeout:    5 * time.Minute,
		WriteTimeout:   5 * time.Minute,
		MaxHeaderBytes: 1 << 20,
	}

	go util.Forever(func() {
		glog.Infof("Started Kubelet for node %s, server at %s, tls=%v", c.NodeHost, c.BindAddress, c.TLS)
		if clusterDNS != nil {
			glog.Infof("  Kubelet is setting %s as a DNS nameserver for domain %q", clusterDNS, c.ClusterDomain)
		}
		k.BirthCry()

		if c.TLS {
			server.TLSConfig = &tls.Config{
				// Change default from SSLv3 to TLSv1.0 (because of POODLE vulnerability)
				MinVersion: tls.VersionTLS10,
				// RequireAndVerifyClientCert lets us limit requests to ones with a valid client certificate
				ClientAuth: tls.RequireAndVerifyClientCert,
				ClientCAs:  c.ClientCAs,
			}
			glog.Fatal(server.ListenAndServeTLS(c.KubeletCertFile, c.KubeletKeyFile))
		} else {
			glog.Fatal(server.ListenAndServe())
		}
	}, 0)
}

// RunProxy starts the proxy
func (c *NodeConfig) RunProxy() {
	// initialize kube proxy
	serviceConfig := pconfig.NewServiceConfig()
	endpointsConfig := pconfig.NewEndpointsConfig()
	pconfig.NewSourceAPI(
		c.Client.Services(kapi.NamespaceAll),
		c.Client.Endpoints(kapi.NamespaceAll),
		30*time.Second,
		serviceConfig.Channel("api"),
		endpointsConfig.Channel("api"))
	loadBalancer := proxy.NewLoadBalancerRR()
	endpointsConfig.RegisterHandler(loadBalancer)

	host, _, err := net.SplitHostPort(c.BindAddress)
	if err != nil {
		glog.Fatalf("The provided value to bind to must be an ip:port %q", c.BindAddress)
	}
	ip := net.ParseIP(host)
	if ip == nil {
		glog.Fatalf("The provided value to bind to must be an ip:port: %q", c.BindAddress)
	}

	protocol := iptables.ProtocolIpv4
	if ip.To4() == nil {
		protocol = iptables.ProtocolIpv6
	}

	var proxier pconfig.ServiceConfigHandler
	proxier = proxy.NewProxier(loadBalancer, ip, iptables.New(kexec.New(), protocol))
	if proxier == nil || reflect.ValueOf(proxier).IsNil() { // explicitly declared interfaces aren't plain nil, you must reflect inside to see if it's really nil or not
		glog.Errorf("WARNING: Could not modify iptables.  iptables must be mutable by this process to use services.  Do you have root permissions?")
		proxier = &service.FailingServiceConfigProxy{}
	}
	serviceConfig.RegisterHandler(proxier)

	glog.Infof("Started Kubernetes Proxy on %s", host)
}

// TODO: more generic location
func includesPort(ports []kapi.EndpointPort, port int) bool {
	for _, p := range ports {
		if p.Port == port {
			return true
		}
	}
	return false
}

// TODO: more generic location
func firstIP(endpoints *kapi.Endpoints, port int) (string, bool) {
	for _, s := range endpoints.Subsets {
		if !includesPort(s.Ports, port) {
			continue
		}
		for _, a := range s.Addresses {
			return a.IP, true
		}
	}
	return "", false
}
<commit_msg>if AllowDisabledDocker, allow docker to be missing<commit_after>package kubernetes

import (
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"reflect"
	"time"

	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/record"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/cadvisor"
	kconfig "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/config"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/proxy"
	pconfig "github.com/GoogleCloudPlatform/kubernetes/pkg/proxy/config"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	kexec "github.com/GoogleCloudPlatform/kubernetes/pkg/util/exec"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util/iptables"
	dockerclient "github.com/fsouza/go-dockerclient"
	"github.com/golang/glog"

	cmdutil "github.com/openshift/origin/pkg/cmd/util"
	dockerutil "github.com/openshift/origin/pkg/cmd/util/docker"
	"github.com/openshift/origin/pkg/kubelet/app"
	"github.com/openshift/origin/pkg/service"
)

type commandExecutor interface {
	LookPath(executable string) (string, error)
	Run(command string, args ...string) error
}

type defaultCommandExecutor struct{}

func (ce defaultCommandExecutor) LookPath(executable string) (string, error) {
	return exec.LookPath(executable)
}

func (ce defaultCommandExecutor) Run(command string, args ...string) error {
	c := exec.Command(command, args...)
	return c.Run()
}

const minimumDockerAPIVersionWithPullByID = "1.18"

// EnsureDocker attempts to connect to the Docker daemon defined by the helper,
// and if it is unable to it will print a warning.
func (c *NodeConfig) EnsureDocker(docker *dockerutil.Helper) {
	dockerClient, dockerAddr := docker.GetClientOrExit()
	if err := dockerClient.Ping(); err != nil {
		c.HandleDockerError(fmt.Sprintf("Docker could not be reached at %s.  Docker must be installed and running to start containers.\n%v", dockerAddr, err))
		return
	}

	glog.Infof("Connecting to Docker at %s", dockerAddr)

	env, err := dockerClient.Version()
	if err != nil {
		c.HandleDockerError(fmt.Sprintf("Unable to check for Docker server version.\n%v", err))
		return
	}

	serverVersionString := env.Get("ApiVersion")
	serverVersion, err := dockerclient.NewAPIVersion(serverVersionString)
	if err != nil {
		c.HandleDockerError(fmt.Sprintf("Unable to determine Docker server version from %q.\n%v", serverVersionString, err))
		return
	}

	minimumPullByIDVersion, err := dockerclient.NewAPIVersion(minimumDockerAPIVersionWithPullByID)
	if err != nil {
		c.HandleDockerError(fmt.Sprintf("Unable to check for Docker server version.\n%v", err))
		return
	}

	if serverVersion.LessThan(minimumPullByIDVersion) {
		c.HandleDockerError(fmt.Sprintf("Docker 1.6 or later (server API version 1.18 or later) required."))
		return
	}

	c.DockerClient = dockerClient
}

func (c *NodeConfig) HandleDockerError(message string) {
	if !c.AllowDisabledDocker {
		glog.Fatalf("ERROR: %s", message)
	}
	glog.Errorf("WARNING: %s", message)
	c.DockerClient = &dockertools.FakeDockerClient{VersionInfo: dockerclient.Env{"apiversion=1.15"}}
}

// EnsureVolumeDir attempts to convert the provided volume directory argument to
// an absolute path and create the directory if it does not exist. Will exit if
// an error is encountered.
func (c *NodeConfig) EnsureVolumeDir() {
	if volumeDir, err := c.initializeVolumeDir(&defaultCommandExecutor{}, c.VolumeDir); err != nil {
		glog.Fatal(err)
	} else {
		c.VolumeDir = volumeDir
	}
}

func (c *NodeConfig) initializeVolumeDir(ce commandExecutor, path string) (string, error) {
	rootDirectory, err := filepath.Abs(path)
	if err != nil {
		return "", fmt.Errorf("Error converting volume directory to an absolute path: %v", err)
	}

	if _, err := os.Stat(rootDirectory); os.IsNotExist(err) {
		if err := os.MkdirAll(rootDirectory, 0750); err != nil {
			return "", fmt.Errorf("Couldn't create kubelet volume root directory '%s': %s", rootDirectory, err)
		}
		if chconPath, err := ce.LookPath("chcon"); err != nil {
			glog.V(2).Infof("Couldn't locate 'chcon' to set the kubelet volume root directory SELinux context: %s", err)
		} else {
			if err := ce.Run(chconPath, "-t", "svirt_sandbox_file_t", rootDirectory); err != nil {
				glog.Warningf("Error running 'chcon' to set the kubelet volume root directory SELinux context: %s", err)
			}
		}
	}
	return rootDirectory, nil
}

// RunKubelet starts the Kubelet.
func (c *NodeConfig) RunKubelet() {
	// TODO: clean this up and make it more formal (service named 'dns'?). Use multiple ports.
	clusterDNS := c.ClusterDNS
	if clusterDNS == nil {
		if service, err := c.Client.Endpoints(kapi.NamespaceDefault).Get("kubernetes"); err == nil {
			if ip, ok := firstIP(service, 53); ok {
				if err := cmdutil.WaitForSuccessfulDial(false, "tcp", fmt.Sprintf("%s:%d", ip, 53), 50*time.Millisecond, 0, 2); err == nil {
					clusterDNS = net.ParseIP(ip)
				}
			}
		}
	}

	cadvisorInterface, err := cadvisor.New(4194)
	if err == nil {
		// TODO: use VersionInfo after the next rebase
		_, err = cadvisorInterface.MachineInfo()
	}
	if err != nil {
		glog.Errorf("WARNING: cAdvisor cannot be started: %v", err)
		cadvisorInterface = &cadvisor.Fake{}
	}

	hostNetworkCapabilities := []string{kubelet.ApiserverSource, kubelet.FileSource}
	// initialize Kubelet
	// Allow privileged containers
	// TODO: make this configurable and not the default https://github.com/openshift/origin/issues/662
	kubelet.SetupCapabilities(true, hostNetworkCapabilities)
	recorder := record.NewBroadcaster().NewRecorder(kapi.EventSource{Component: "kubelet", Host: c.NodeHost})

	cfg := kconfig.NewPodConfig(kconfig.PodConfigNotificationSnapshotAndUpdates, recorder)
	kconfig.NewSourceApiserver(c.Client, c.NodeHost, cfg.Channel(kubelet.ApiserverSource))
	// define manifest file source for pods, if specified
	if len(c.PodManifestPath) > 0 {
		_, err = os.Stat(c.PodManifestPath)
		if err == nil {
			glog.Infof("Adding pod manifest file/dir: %v", c.PodManifestPath)
			kconfig.NewSourceFile(c.PodManifestPath, c.NodeHost,
				time.Duration(c.PodManifestCheckIntervalSeconds)*time.Second,
				cfg.Channel(kubelet.FileSource))
		} else {
			glog.Errorf("WARNING: PodManifestPath specified is not a valid file/directory: %v", err)
		}
	}

	gcPolicy := kubelet.ContainerGCPolicy{
		MinAge:             10 * time.Second,
		MaxPerPodContainer: 5,
		MaxContainers:      100,
	}
	imageGCPolicy := kubelet.ImageGCPolicy{
		HighThresholdPercent: 90,
		LowThresholdPercent:  80,
	}

	k, err := kubelet.NewMainKubelet(
		c.NodeHost,
		c.DockerClient,
		c.Client,
		c.VolumeDir,
		c.ImageFor("pod"),
		3*time.Second,
		0.0,
		10,
		gcPolicy,
		cfg.SeenAllSources,
		c.ClusterDomain,
		clusterDNS,
		kapi.NamespaceDefault,
		app.ProbeVolumePlugins(),
		app.ProbeNetworkPlugins(),
		c.NetworkPluginName,
		5*time.Minute,
		recorder,
		cadvisorInterface,
		imageGCPolicy,
		nil,
		15*time.Second,
	)
	if err != nil {
		glog.Fatalf("Couldn't run kubelet: %s", err)
	}
	go util.Forever(func() { k.Run(cfg.Updates()) }, 0)

	handler := kubelet.NewServer(k, true)

	server := &http.Server{
		Addr:           c.BindAddress,
		Handler:        &handler,
		ReadTimeout:    5 * time.Minute,
		WriteTimeout:   5 * time.Minute,
		MaxHeaderBytes: 1 << 20,
	}

	go util.Forever(func() {
		glog.Infof("Started Kubelet for node %s, server at %s, tls=%v", c.NodeHost, c.BindAddress, c.TLS)
		if clusterDNS != nil {
			glog.Infof("  Kubelet is setting %s as a DNS nameserver for domain %q", clusterDNS, c.ClusterDomain)
		}
		k.BirthCry()

		if c.TLS {
			server.TLSConfig = &tls.Config{
				// Change default from SSLv3 to TLSv1.0 (because of POODLE vulnerability)
				MinVersion: tls.VersionTLS10,
				// RequireAndVerifyClientCert lets us limit requests to ones with a valid client certificate
				ClientAuth: tls.RequireAndVerifyClientCert,
				ClientCAs:  c.ClientCAs,
			}
			glog.Fatal(server.ListenAndServeTLS(c.KubeletCertFile, c.KubeletKeyFile))
		} else {
			glog.Fatal(server.ListenAndServe())
		}
	}, 0)
}

// RunProxy starts the proxy
func (c *NodeConfig) RunProxy() {
	// initialize kube proxy
	serviceConfig := pconfig.NewServiceConfig()
	endpointsConfig := pconfig.NewEndpointsConfig()
	pconfig.NewSourceAPI(
		c.Client.Services(kapi.NamespaceAll),
		c.Client.Endpoints(kapi.NamespaceAll),
		30*time.Second,
		serviceConfig.Channel("api"),
		endpointsConfig.Channel("api"))
	loadBalancer := proxy.NewLoadBalancerRR()
	endpointsConfig.RegisterHandler(loadBalancer)

	host, _, err := net.SplitHostPort(c.BindAddress)
	if err != nil {
		glog.Fatalf("The provided value to bind to must be an ip:port %q", c.BindAddress)
	}
	ip := net.ParseIP(host)
	if ip == nil {
		glog.Fatalf("The provided value to bind to must be an ip:port: %q", c.BindAddress)
	}

	protocol := iptables.ProtocolIpv4
	if ip.To4() == nil {
		protocol = iptables.ProtocolIpv6
	}

	var proxier pconfig.ServiceConfigHandler
	proxier = proxy.NewProxier(loadBalancer, ip, iptables.New(kexec.New(), protocol))
	if proxier == nil || reflect.ValueOf(proxier).IsNil() { // explicitly declared interfaces aren't plain nil, you must reflect inside to see if it's really nil or not
		glog.Errorf("WARNING: Could not modify iptables.  iptables must be mutable by this process to use services.  Do you have root permissions?")
		proxier = &service.FailingServiceConfigProxy{}
	}
	serviceConfig.RegisterHandler(proxier)

	glog.Infof("Started Kubernetes Proxy on %s", host)
}

// TODO: more generic location
func includesPort(ports []kapi.EndpointPort, port int) bool {
	for _, p := range ports {
		if p.Port == port {
			return true
		}
	}
	return false
}

// TODO: more generic location
func firstIP(endpoints *kapi.Endpoints, port int) (string, bool) {
	for _, s := range endpoints.Subsets {
		if !includesPort(s.Ports, port) {
			continue
		}
		for _, a := range s.Addresses {
			return a.IP, true
		}
	}
	return "", false
}
<|endoftext|>


<commit_before>package clientcmd

import (
	"fmt"
	"strconv"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
	kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	kclientcmd "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
	cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
	"github.com/spf13/pflag"

	"github.com/openshift/origin/pkg/api/latest"
	"github.com/openshift/origin/pkg/client"
	"github.com/openshift/origin/pkg/cmd/cli/describe"
	deployapi "github.com/openshift/origin/pkg/deploy/api"
	deployreaper "github.com/openshift/origin/pkg/deploy/reaper"
	deploy "github.com/openshift/origin/pkg/deploy/scaler"
	routegen "github.com/openshift/origin/pkg/route/generator"
)

// New creates a default Factory for commands that should share identical server
// connection behavior. Most commands should use this method to get a factory.
func New(flags *pflag.FlagSet) *Factory {
	// Override global default to "" so we force the client to ask for user input
	// TODO refactor this usptream:
	// DefaultCluster should not be a global
	// A call to ClientConfig() should always return the best clientCfg possible
	// even if an error was returned, and let the caller decide what to do
	kclientcmd.DefaultCluster.Server = ""

	// TODO: there should be two client configs, one for OpenShift, and one for Kubernetes
	clientConfig := DefaultClientConfig(flags)
	f := NewFactory(clientConfig)
	f.BindFlags(flags)

	return f
}

// Factory provides common options for OpenShift commands
type Factory struct {
	*cmdutil.Factory
	OpenShiftClientConfig kclientcmd.ClientConfig
	clients               *clientCache
}

// NewFactory creates an object that holds common methods across all OpenShift commands
func NewFactory(clientConfig kclientcmd.ClientConfig) *Factory {
	mapper := ShortcutExpander{kubectl.ShortcutExpander{latest.RESTMapper}}

	clients := &clientCache{
		clients: make(map[string]*client.Client),
		loader:  clientConfig,
	}
	generators := map[string]kubectl.Generator{
		"route/v1":   routegen.RouteGenerator{},
		"service/v1": kubectl.ServiceGenerator{},
	}

	w := &Factory{
		Factory:               cmdutil.NewFactory(clientConfig),
		OpenShiftClientConfig: clientConfig,
		clients:               clients,
	}

	w.Object = func() (meta.RESTMapper, runtime.ObjectTyper) {
		if cfg, err := clientConfig.ClientConfig(); err == nil {
			return kubectl.OutputVersionMapper{mapper, cfg.Version}, api.Scheme
		}
		return mapper, api.Scheme
	}

	kRESTClient := w.Factory.RESTClient
	w.RESTClient = func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
		if latest.OriginKind(mapping.Kind, mapping.APIVersion) {
			client, err := clients.ClientForVersion(mapping.APIVersion)
			if err != nil {
				return nil, err
			}
			return client.RESTClient, nil
		}
		return kRESTClient(mapping)
	}

	// Save original Describer function
	kDescriberFunc := w.Factory.Describer
	w.Describer = func(mapping *meta.RESTMapping) (kubectl.Describer, error) {
		if latest.OriginKind(mapping.Kind, mapping.APIVersion) {
			oClient, kClient, err := w.Clients()
			if err != nil {
				return nil, fmt.Errorf("unable to create client %s: %v", mapping.Kind, err)
			}

			cfg, err := clients.ClientConfigForVersion(mapping.APIVersion)
			if err != nil {
				return nil, fmt.Errorf("unable to load a client %s: %v", mapping.Kind, err)
			}

			describer, ok := describe.DescriberFor(mapping.Kind, oClient, kClient, cfg.Host)
			if !ok {
				return nil, fmt.Errorf("no description has been implemented for %q", mapping.Kind)
			}
			return describer, nil
		}
		return kDescriberFunc(mapping)
	}
	w.Scaler = func(mapping *meta.RESTMapping) (kubectl.Scaler, error) {
		oc, kc, err := w.Clients()
		if err != nil {
			return nil, err
		}
		return deploy.ScalerFor(mapping.Kind, oc, kc)
	}
	w.Reaper = func(mapping *meta.RESTMapping) (kubectl.Reaper, error) {
		oc, kc, err := w.Clients()
		if err != nil {
			return nil, err
		}
		return deployreaper.ReaperFor(mapping.Kind, oc, kc)
	}
	w.Generator = func(name string) (kubectl.Generator, bool) {
		generator, ok := generators[name]
		return generator, ok
	}
	w.PodSelectorForObject = func(object runtime.Object) (string, error) {
		switch t := object.(type) {
		case *deployapi.DeploymentConfig:
			return kubectl.MakeLabels(t.Template.ControllerTemplate.Selector), nil
		case *api.ReplicationController:
			return kubectl.MakeLabels(t.Spec.Selector), nil
		case *api.Pod:
			if len(t.Labels) == 0 {
				return "", fmt.Errorf("the pod has no labels and cannot be exposed")
			}
			return kubectl.MakeLabels(t.Labels), nil
		case *api.Service:
			if t.Spec.Selector == nil {
				return "", fmt.Errorf("the service has no pod selector set")
			}
			return kubectl.MakeLabels(t.Spec.Selector), nil
		default:
			kind, err := meta.NewAccessor().Kind(object)
			if err != nil {
				return "", err
			}
			return "", fmt.Errorf("it is not possible to get a pod selector from %s", kind)
		}
	}
	w.PortsForObject = func(object runtime.Object) ([]string, error) {
		switch t := object.(type) {
		case *deployapi.DeploymentConfig:
			return getPorts(t.Template.ControllerTemplate.Template.Spec), nil
		case *api.ReplicationController:
			return getPorts(t.Spec.Template.Spec), nil
		case *api.Pod:
			return getPorts(t.Spec), nil
		default:
			kind, err := meta.NewAccessor().Kind(object)
			if err != nil {
				return nil, err
			}
			return nil, fmt.Errorf("it is not possible to get ports from %s", kind)
		}
	}
	w.Printer = func(mapping *meta.RESTMapping, noHeaders, withNamespace bool) (kubectl.ResourcePrinter, error) {
		return describe.NewHumanReadablePrinter(noHeaders, withNamespace), nil
	}

	return w
}

func getPorts(spec api.PodSpec) []string {
	result := []string{}
	for _, container := range spec.Containers {
		for _, port := range container.Ports {
			result = append(result, strconv.Itoa(port.ContainerPort))
		}
	}
	return result
}

// TODO: move to upstream
func (f *Factory) UpdatePodSpecForObject(obj runtime.Object, fn func(*api.PodSpec) error) (bool, error) {
	// TODO: replace with a swagger schema based approach (identify pod template via schema introspection)
	switch t := obj.(type) {
	case *api.Pod:
		return true, fn(&t.Spec)
	case *api.PodTemplate:
		return true, fn(&t.Template.Spec)
	case *api.ReplicationController:
		if t.Spec.TemplateRef != nil {
			return true, fmt.Errorf("references to pod templates (%s/%s) cannot be updated", t.Spec.TemplateRef.Namespace, t.Spec.TemplateRef.Name)
		}
		if t.Spec.Template == nil {
			t.Spec.Template = &api.PodTemplateSpec{}
		}
		return true, fn(&t.Spec.Template.Spec)
	case *deployapi.DeploymentConfig:
		template := t.Template.ControllerTemplate
		if template.TemplateRef != nil {
			return true, fmt.Errorf("DeploymentConfigs with references to pod templates (%s/%s) cannot be updated", template.TemplateRef.Namespace, template.TemplateRef.Name)
		}
		if template.Template == nil {
			template.Template = &api.PodTemplateSpec{}
		}
		return true, fn(&template.Template.Spec)
	default:
		return false, fmt.Errorf("the object is not a pod or does not have a pod template")
	}
}

// Clients returns an OpenShift and Kubernetes client.
func (f *Factory) Clients() (*client.Client, *kclient.Client, error) {
	kClient, err := f.Client()
	if err != nil {
		return nil, nil, err
	}
	osClient, err := f.clients.ClientForVersion("")
	if err != nil {
		return nil, nil, err
	}
	return osClient, kClient, nil
}

// ShortcutExpander is a RESTMapper that can be used for OpenShift resources.
type ShortcutExpander struct {
	meta.RESTMapper
}

// VersionAndKindForResource implements meta.RESTMapper. It expands the resource first, then invokes the wrapped
// mapper.
func (e ShortcutExpander) VersionAndKindForResource(resource string) (defaultVersion, kind string, err error) {
	resource = expandResourceShortcut(resource)
	return e.RESTMapper.VersionAndKindForResource(resource)
}

// AliasesForResource returns whether a resource has an alias or not
func (e ShortcutExpander) AliasesForResource(resource string) ([]string, bool) {
	aliases := map[string][]string{
		"all": latest.UserResources,
	}

	if res, ok := aliases[resource]; ok {
		return res, true
	}
	return nil, false
}

// expandResourceShortcut will return the expanded version of resource
// (something that a pkg/api/meta.RESTMapper can understand), if it is
// indeed a shortcut. Otherwise, will return resource unmodified.
func expandResourceShortcut(resource string) string {
	shortForms := map[string]string{
		"dc":      "deploymentConfigs",
		"bc":      "buildConfigs",
		"is":      "imageStreams",
		"istag":   "imageStreamTags",
		"isimage": "imageStreamImages",
	}
	if expanded, ok := shortForms[resource]; ok {
		return expanded
	}
	return resource
}

// clientCache caches previously loaded clients for reuse, and ensures MatchServerVersion
// is invoked only once
type clientCache struct {
	loader        kclientcmd.ClientConfig
	clients       map[string]*client.Client
	defaultConfig *kclient.Config
}

// ClientConfigForVersion returns the correct config for a server
func (c *clientCache) ClientConfigForVersion(version string) (*kclient.Config, error) {
	if c.defaultConfig == nil {
		config, err := c.loader.ClientConfig()
		if err != nil {
			return nil, err
		}
		c.defaultConfig = config
	}
	// TODO: have a better config copy method
	config := *c.defaultConfig
	if len(version) != 0 {
		config.Version = version
	}
	client.SetOpenShiftDefaults(&config)

	return &config, nil
}

// ClientForVersion initializes or reuses a client for the specified version, or returns an
// error if that is not possible
func (c *clientCache) ClientForVersion(version string) (*client.Client, error) {
	config, err := c.ClientConfigForVersion(version)
	if err != nil {
		return nil, err
	}

	if client, ok := c.clients[config.Version]; ok {
		return client, nil
	}

	client, err := client.New(config)
	if err != nil {
		return nil, err
	}

	c.clients[config.Version] = client
	return client, nil
}
<commit_msg>Add shortcuts for service accounts<commit_after>package clientcmd

import (
	"fmt"
	"strconv"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
	kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	kclientcmd "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
	cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
	"github.com/spf13/pflag"

	"github.com/openshift/origin/pkg/api/latest"
	"github.com/openshift/origin/pkg/client"
	"github.com/openshift/origin/pkg/cmd/cli/describe"
	deployapi "github.com/openshift/origin/pkg/deploy/api"
	deployreaper "github.com/openshift/origin/pkg/deploy/reaper"
	deploy "github.com/openshift/origin/pkg/deploy/scaler"
	routegen "github.com/openshift/origin/pkg/route/generator"
)

// New creates a default Factory for commands that should share identical server
// connection behavior. Most commands should use this method to get a factory.
func New(flags *pflag.FlagSet) *Factory {
	// Override global default to "" so we force the client to ask for user input
	// TODO refactor this usptream:
	// DefaultCluster should not be a global
	// A call to ClientConfig() should always return the best clientCfg possible
	// even if an error was returned, and let the caller decide what to do
	kclientcmd.DefaultCluster.Server = ""

	// TODO: there should be two client configs, one for OpenShift, and one for Kubernetes
	clientConfig := DefaultClientConfig(flags)
	f := NewFactory(clientConfig)
	f.BindFlags(flags)

	return f
}

// Factory provides common options for OpenShift commands
type Factory struct {
	*cmdutil.Factory
	OpenShiftClientConfig kclientcmd.ClientConfig
	clients               *clientCache
}

// NewFactory creates an object that holds common methods across all OpenShift commands
func NewFactory(clientConfig kclientcmd.ClientConfig) *Factory {
	mapper := ShortcutExpander{kubectl.ShortcutExpander{latest.RESTMapper}}

	clients := &clientCache{
		clients: make(map[string]*client.Client),
		loader:  clientConfig,
	}
	generators := map[string]kubectl.Generator{
		"route/v1":   routegen.RouteGenerator{},
		"service/v1": kubectl.ServiceGenerator{},
	}

	w := &Factory{
		Factory:               cmdutil.NewFactory(clientConfig),
		OpenShiftClientConfig: clientConfig,
		clients:               clients,
	}

	w.Object = func() (meta.RESTMapper, runtime.ObjectTyper) {
		if cfg, err := clientConfig.ClientConfig(); err == nil {
			return kubectl.OutputVersionMapper{mapper, cfg.Version}, api.Scheme
		}
		return mapper, api.Scheme
	}

	kRESTClient := w.Factory.RESTClient
	w.RESTClient = func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
		if latest.OriginKind(mapping.Kind, mapping.APIVersion) {
			client, err := clients.ClientForVersion(mapping.APIVersion)
			if err != nil {
				return nil, err
			}
			return client.RESTClient, nil
		}
		return kRESTClient(mapping)
	}

	// Save original Describer function
	kDescriberFunc := w.Factory.Describer
	w.Describer = func(mapping *meta.RESTMapping) (kubectl.Describer, error) {
		if latest.OriginKind(mapping.Kind, mapping.APIVersion) {
			oClient, kClient, err := w.Clients()
			if err != nil {
				return nil, fmt.Errorf("unable to create client %s: %v", mapping.Kind, err)
			}

			cfg, err := clients.ClientConfigForVersion(mapping.APIVersion)
			if err != nil {
				return nil, fmt.Errorf("unable to load a client %s: %v", mapping.Kind, err)
			}

			describer, ok := describe.DescriberFor(mapping.Kind, oClient, kClient, cfg.Host)
			if !ok {
				return nil, fmt.Errorf("no description has been implemented for %q", mapping.Kind)
			}
			return describer, nil
		}
		return kDescriberFunc(mapping)
	}
	w.Scaler = func(mapping *meta.RESTMapping) (kubectl.Scaler, error) {
		oc, kc, err := w.Clients()
		if err != nil {
			return nil, err
		}
		return deploy.ScalerFor(mapping.Kind, oc, kc)
	}
	w.Reaper = func(mapping *meta.RESTMapping) (kubectl.Reaper, error) {
		oc, kc, err := w.Clients()
		if err != nil {
			return nil, err
		}
		return deployreaper.ReaperFor(mapping.Kind, oc, kc)
	}
	w.Generator = func(name string) (kubectl.Generator, bool) {
		generator, ok := generators[name]
		return generator, ok
	}
	w.PodSelectorForObject = func(object runtime.Object) (string, error) {
		switch t := object.(type) {
		case *deployapi.DeploymentConfig:
			return kubectl.MakeLabels(t.Template.ControllerTemplate.Selector), nil
		case *api.ReplicationController:
			return kubectl.MakeLabels(t.Spec.Selector), nil
		case *api.Pod:
			if len(t.Labels) == 0 {
				return "", fmt.Errorf("the pod has no labels and cannot be exposed")
			}
			return kubectl.MakeLabels(t.Labels), nil
		case *api.Service:
			if t.Spec.Selector == nil {
				return "", fmt.Errorf("the service has no pod selector set")
			}
			return kubectl.MakeLabels(t.Spec.Selector), nil
		default:
			kind, err := meta.NewAccessor().Kind(object)
			if err != nil {
				return "", err
			}
			return "", fmt.Errorf("it is not possible to get a pod selector from %s", kind)
		}
	}
	w.PortsForObject = func(object runtime.Object) ([]string, error) {
		switch t := object.(type) {
		case *deployapi.DeploymentConfig:
			return getPorts(t.Template.ControllerTemplate.Template.Spec), nil
		case *api.ReplicationController:
			return getPorts(t.Spec.Template.Spec), nil
		case *api.Pod:
			return getPorts(t.Spec), nil
		default:
			kind, err := meta.NewAccessor().Kind(object)
			if err != nil {
				return nil, err
			}
			return nil, fmt.Errorf("it is not possible to get ports from %s", kind)
		}
	}
	w.Printer = func(mapping *meta.RESTMapping, noHeaders, withNamespace bool) (kubectl.ResourcePrinter, error) {
		return describe.NewHumanReadablePrinter(noHeaders, withNamespace), nil
	}

	return w
}

func getPorts(spec api.PodSpec) []string {
	result := []string{}
	for _, container := range spec.Containers {
		for _, port := range container.Ports {
			result = append(result, strconv.Itoa(port.ContainerPort))
		}
	}
	return result
}

// TODO: move to upstream
func (f *Factory) UpdatePodSpecForObject(obj runtime.Object, fn func(*api.PodSpec) error) (bool, error) {
	// TODO: replace with a swagger schema based approach (identify pod template via schema introspection)
	switch t := obj.(type) {
	case *api.Pod:
		return true, fn(&t.Spec)
	case *api.PodTemplate:
		return true, fn(&t.Template.Spec)
	case *api.ReplicationController:
		if t.Spec.TemplateRef != nil {
			return true, fmt.Errorf("references to pod templates (%s/%s) cannot be updated", t.Spec.TemplateRef.Namespace, t.Spec.TemplateRef.Name)
		}
		if t.Spec.Template == nil {
			t.Spec.Template = &api.PodTemplateSpec{}
		}
		return true, fn(&t.Spec.Template.Spec)
	case *deployapi.DeploymentConfig:
		template := t.Template.ControllerTemplate
		if template.TemplateRef != nil {
			return true, fmt.Errorf("DeploymentConfigs with references to pod templates (%s/%s) cannot be updated", template.TemplateRef.Namespace, template.TemplateRef.Name)
		}
		if template.Template == nil {
			template.Template = &api.PodTemplateSpec{}
		}
		return true, fn(&template.Template.Spec)
	default:
		return false, fmt.Errorf("the object is not a pod or does not have a pod template")
	}
}

// Clients returns an OpenShift and Kubernetes client.
func (f *Factory) Clients() (*client.Client, *kclient.Client, error) {
	kClient, err := f.Client()
	if err != nil {
		return nil, nil, err
	}
	osClient, err := f.clients.ClientForVersion("")
	if err != nil {
		return nil, nil, err
	}
	return osClient, kClient, nil
}

// ShortcutExpander is a RESTMapper that can be used for OpenShift resources.
type ShortcutExpander struct {
	meta.RESTMapper
}

// VersionAndKindForResource implements meta.RESTMapper. It expands the resource first, then invokes the wrapped
// mapper.
func (e ShortcutExpander) VersionAndKindForResource(resource string) (defaultVersion, kind string, err error) {
	resource = expandResourceShortcut(resource)
	return e.RESTMapper.VersionAndKindForResource(resource)
}

// AliasesForResource returns whether a resource has an alias or not
func (e ShortcutExpander) AliasesForResource(resource string) ([]string, bool) {
	aliases := map[string][]string{
		"all": latest.UserResources,
	}

	if res, ok := aliases[resource]; ok {
		return res, true
	}
	return nil, false
}

// expandResourceShortcut will return the expanded version of resource
// (something that a pkg/api/meta.RESTMapper can understand), if it is
// indeed a shortcut. Otherwise, will return resource unmodified.
func expandResourceShortcut(resource string) string {
	shortForms := map[string]string{
		"dc":      "deploymentConfigs",
		"bc":      "buildConfigs",
		"is":      "imageStreams",
		"istag":   "imageStreamTags",
		"isimage": "imageStreamImages",
		"sa":      "serviceAccounts",
		"pv":      "persistentVolumes",
		"pvc":     "persistentVolumeClaims",
	}
	if expanded, ok := shortForms[resource]; ok {
		return expanded
	}
	return resource
}

// clientCache caches previously loaded clients for reuse, and ensures MatchServerVersion
// is invoked only once
type clientCache struct {
	loader        kclientcmd.ClientConfig
	clients       map[string]*client.Client
	defaultConfig *kclient.Config
}

// ClientConfigForVersion returns the correct config for a server
func (c *clientCache) ClientConfigForVersion(version string) (*kclient.Config, error) {
	if c.defaultConfig == nil {
		config, err := c.loader.ClientConfig()
		if err != nil {
			return nil, err
		}
		c.defaultConfig = config
	}
	// TODO: have a better config copy method
	config := *c.defaultConfig
	if len(version) != 0 {
		config.Version = version
	}
	client.SetOpenShiftDefaults(&config)

	return &config, nil
}

// ClientForVersion initializes or reuses a client for the specified version, or returns an
// error if that is not possible
func (c *clientCache) ClientForVersion(version string) (*client.Client, error) {
	config, err := c.ClientConfigForVersion(version)
	if err != nil {
		return nil, err
	}

	if client, ok := c.clients[config.Version]; ok {
		return client, nil
	}

	client, err := client.New(config)
	if err != nil {
		return nil, err
	}

	c.clients[config.Version] = client
	return client, nil
}
<|endoftext|>


<commit_before>package postgres

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"strings"

	"github.com/caarlos0/env"
	"github.com/jmoiron/sqlx"
	// Used pg drive on sqlx
	_ "github.com/lib/pq"

	"database/sql"

	"github.com/nuveo/prest/api"
	"github.com/nuveo/prest/config"
)

const (
	pageNumberKey   = "_page"
	pageSizeKey     = "_page_size"
	defaultPageSize = 10
)

// Conn connect on PostgreSQL
// Used sqlx
func Conn() (db *sqlx.DB) {
	cfg := config.Prest{}
	env.Parse(&cfg)

	db, err := sqlx.Connect("postgres", fmt.Sprintf("user=%s dbname=%s host=%s port=%v sslmode=disable", cfg.PGUser, cfg.PGDatabase, cfg.PGHost, cfg.PGPort))
	if err != nil {
		panic(fmt.Sprintf("Unable to connection to database: %v\n", err))
	}
	return
}

// WhereByRequest create interface for queries + where
func WhereByRequest(r *http.Request, initialPlaceholderID int) (whereSyntax string, values []interface{}) {
	//whereMap := make(map[string]string)
	whereKey := []string{}
	whereValues := []string{}

	u, _ := url.Parse(r.URL.String())
	pid := initialPlaceholderID
	for key, val := range u.Query() {
		if !strings.HasPrefix(key, "_") {
			keyInfo := strings.Split(key, ":")
			if len(keyInfo) > 1 {
				switch keyInfo[1] {
				case "jsonb":
					jsonField := strings.Split(keyInfo[0], "->>")
					whereKey = append(whereKey, fmt.Sprintf("%s->>'%s'=$%d", jsonField[0], jsonField[1], pid))
					whereValues = append(whereValues, val[0])
				default:
					whereKey = append(whereKey, fmt.Sprintf("%s=$%d", keyInfo[0], pid))
					whereValues = append(whereValues, val[0])
				}
				continue
			}
			whereKey = append(whereKey, fmt.Sprintf("%s=$%d", key, pid))
			whereValues = append(whereValues, val[0])

			pid++
		}
	}

	for i := 0; i < len(whereKey); i++ {
		if whereSyntax == "" {
			whereSyntax += whereKey[i]
		} else {
			whereSyntax += " AND " + whereKey[i]
		}

		values = append(values, whereValues[i])
	}

	return
}

// Query process queries
func Query(SQL string, params ...interface{}) (jsonData []byte, err error) {
	db := Conn()

	prepare, err := db.Prepare(SQL)
	fmt.Println("SQL:", SQL)
	fmt.Println("Prepare ERR:", err)
	fmt.Printf("values: %#v\n", params)

	if err != nil {
		return
	}

	rows, err := prepare.Query(params...)
	if err != nil {
		return
	}
	defer rows.Close()

	columns, err := rows.Columns()
	if err != nil {
		return
	}

	count := len(columns)
	tableData := make([]map[string]interface{}, 0)
	values := make([]interface{}, count)
	valuePtrs := make([]interface{}, count)
	for rows.Next() {
		for i := 0; i < count; i++ {
			valuePtrs[i] = &values[i]
		}
		rows.Scan(valuePtrs...)
		entry := make(map[string]interface{})
		for i, col := range columns {
			var v interface{}
			val := values[i]
			b, ok := val.([]byte)
			if ok {
				v = string(b)
			} else {
				v = val
			}
			entry[col] = v
		}
		tableData = append(tableData, entry)
	}
	jsonData, err = json.Marshal(tableData)

	return
}

// PaginateIfPossible func
func PaginateIfPossible(r *http.Request) (paginatedQuery string, err error) {
	u, _ := url.Parse(r.URL.String())
	values := u.Query()
	if _, ok := values[pageNumberKey]; !ok {
		paginatedQuery = ""
		return
	}
	pageNumber, err := strconv.Atoi(values[pageNumberKey][0])
	if err != nil {
		return
	}
	pageSize := defaultPageSize
	if size, ok := values[pageSizeKey]; ok {
		pageSize, err = strconv.Atoi(size[0])
		if err != nil {
			return
		}
	}
	paginatedQuery = fmt.Sprintf("LIMIT %d OFFSET(%d - 1) * %d", pageSize, pageNumber, pageSize)
	return
}

// Insert execute insert sql into a table
func Insert(database, schema, table string, body api.Request) (jsonData []byte, err error) {
	var result sql.Result
	var rowsAffected int64

	fields := make([]string, 0)
	values := make([]string, 0)
	for key, value := range body.Data {
		fields = append(fields, key)
		values = append(values, value)
	}
	colsName := strings.Join(fields, ", ")
	colsValue := strings.Join(values, "', '")
	sql := fmt.Sprintf("INSERT INTO %s.%s.%s (%s) VALUES ('%s')", database, schema, table, colsName, colsValue)

	db := Conn()
	result, err = db.Exec(sql)
	if err != nil {
		return
	}
	rowsAffected, err = result.RowsAffected()
	if err != nil {
		return
	}

	data := make(map[string]interface{})
	data["rows_affected"] = rowsAffected
	jsonData, err = json.Marshal(data)
	return
}

// Delete execute delete sql into a table
func Delete(database, schema, table, where string, whereValues []interface{}) (jsonData []byte, err error) {
	var result sql.Result
	var rowsAffected int64

	sql := fmt.Sprintf("DELETE FROM %s.%s.%s", database, schema, table)
	if where != "" {
		sql = fmt.Sprint(
			sql,
			" WHERE ",
			where)
	}

	db := Conn()
	result, err = db.Exec(sql, whereValues...)
	if err != nil {
		return
	}
	rowsAffected, err = result.RowsAffected()
	if err != nil {
		return
	}

	data := make(map[string]interface{})
	data["rows_affected"] = rowsAffected
	jsonData, err = json.Marshal(data)
	return
}

// Update execute update sql into a table
func Update(database, schema, table, where string, whereValues []interface{}, body api.Request) (jsonData []byte, err error) {
	var result sql.Result
	var rowsAffected int64

	fields := []string{}
	values := make([]interface{}, 0)
	pid := len(whereValues) + 1 // placeholder id
	for key, value := range body.Data {
		fields = append(fields, fmt.Sprintf("%s=$%d", key, pid))
		values = append(values, value)
		pid++
	}
	setSyntax := strings.Join(fields, ", ")

	sql := fmt.Sprintf("UPDATE %s.%s.%s SET %s", database, schema, table, setSyntax)

	if where != "" {
		sql = fmt.Sprint(
			sql,
			" WHERE ",
			where)
		values = append(values, whereValues...)
	}

	fmt.Println("SQL:", sql)
	fmt.Printf("values: %#v\n", values)
	fmt.Printf("values2: %v\n", values)

	db := Conn()
	//result, err = db.Exec(sql, values)
	stmt, err := db.Prepare(sql)
	if err != nil {
		return
	}

	valuesAux := make([]interface{}, 0, len(values))

	for i := 0; i < len(values); i++ {
		valuesAux = append(valuesAux, values[i])
	}

	result, err = stmt.Exec(valuesAux...)
	if err != nil {
		return
	}

	rowsAffected, err = result.RowsAffected()
	if err != nil {
		return
	}

	data := make(map[string]interface{})
	data["rows_affected"] = rowsAffected
	jsonData, err = json.Marshal(data)
	return
}
<commit_msg>chkInvaidIndentifer return true if identifier is invalid<commit_after>package postgres

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"unicode"

	"github.com/caarlos0/env"
	"github.com/jmoiron/sqlx"
	// Used pg drive on sqlx
	_ "github.com/lib/pq"

	"database/sql"

	"github.com/nuveo/prest/api"
	"github.com/nuveo/prest/config"
)

const (
	pageNumberKey   = "_page"
	pageSizeKey     = "_page_size"
	defaultPageSize = 10
)

// Conn connect on PostgreSQL
// Used sqlx
func Conn() (db *sqlx.DB) {
	cfg := config.Prest{}
	env.Parse(&cfg)

	db, err := sqlx.Connect("postgres", fmt.Sprintf("user=%s dbname=%s host=%s port=%v sslmode=disable", cfg.PGUser, cfg.PGDatabase, cfg.PGHost, cfg.PGPort))
	if err != nil {
		panic(fmt.Sprintf("Unable to connection to database: %v\n", err))
	}
	return
}

// chkInvaidIdentifier return true if identifier is invalid
// https://www.postgresql.org/docs/current/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
func chkInvaidIdentifier(identifer string) bool {
	if len(identifer) > 63 ||
		unicode.IsDigit([]rune(identifer)[0]) {
		return true
	}

	for _, v := range identifer {
		if !unicode.IsLetter(v) &&
			!unicode.IsDigit(v) &&
			v != '_' {
			return true
		}
	}
	return false
}

// WhereByRequest create interface for queries + where
func WhereByRequest(r *http.Request, initialPlaceholderID int) (whereSyntax string, values []interface{}) {
	//whereMap := make(map[string]string)
	whereKey := []string{}
	whereValues := []string{}

	u, _ := url.Parse(r.URL.String())
	pid := initialPlaceholderID
	for key, val := range u.Query() {
		if !strings.HasPrefix(key, "_") {
			keyInfo := strings.Split(key, ":")
			if len(keyInfo) > 1 {
				switch keyInfo[1] {
				case "jsonb":
					jsonField := strings.Split(keyInfo[0], "->>")
					whereKey = append(whereKey, fmt.Sprintf("%s->>'%s'=$%d", jsonField[0], jsonField[1], pid))
					whereValues = append(whereValues, val[0])
				default:
					whereKey = append(whereKey, fmt.Sprintf("%s=$%d", keyInfo[0], pid))
					whereValues = append(whereValues, val[0])
				}
				continue
			}
			whereKey = append(whereKey, fmt.Sprintf("%s=$%d", key, pid))
			whereValues = append(whereValues, val[0])

			pid++
		}
	}

	for i := 0; i < len(whereKey); i++ {
		if whereSyntax == "" {
			whereSyntax += whereKey[i]
		} else {
			whereSyntax += " AND " + whereKey[i]
		}

		values = append(values, whereValues[i])
	}

	return
}

// Query process queries
func Query(SQL string, params ...interface{}) (jsonData []byte, err error) {
	db := Conn()

	prepare, err := db.Prepare(SQL)
	fmt.Println("SQL:", SQL)
	fmt.Println("Prepare ERR:", err)
	fmt.Printf("values: %#v\n", params)

	if err != nil {
		return
	}

	rows, err := prepare.Query(params...)
	if err != nil {
		return
	}
	defer rows.Close()

	columns, err := rows.Columns()
	if err != nil {
		return
	}

	count := len(columns)
	tableData := make([]map[string]interface{}, 0)
	values := make([]interface{}, count)
	valuePtrs := make([]interface{}, count)
	for rows.Next() {
		for i := 0; i < count; i++ {
			valuePtrs[i] = &values[i]
		}
		rows.Scan(valuePtrs...)
		entry := make(map[string]interface{})
		for i, col := range columns {
			var v interface{}
			val := values[i]
			b, ok := val.([]byte)
			if ok {
				v = string(b)
			} else {
				v = val
			}
			entry[col] = v
		}
		tableData = append(tableData, entry)
	}
	jsonData, err = json.Marshal(tableData)

	return
}

// PaginateIfPossible func
func PaginateIfPossible(r *http.Request) (paginatedQuery string, err error) {
	u, _ := url.Parse(r.URL.String())
	values := u.Query()
	if _, ok := values[pageNumberKey]; !ok {
		paginatedQuery = ""
		return
	}
	pageNumber, err := strconv.Atoi(values[pageNumberKey][0])
	if err != nil {
		return
	}
	pageSize := defaultPageSize
	if size, ok := values[pageSizeKey]; ok {
		pageSize, err = strconv.Atoi(size[0])
		if err != nil {
			return
		}
	}
	paginatedQuery = fmt.Sprintf("LIMIT %d OFFSET(%d - 1) * %d", pageSize, pageNumber, pageSize)
	return
}

// Insert execute insert sql into a table
func Insert(database, schema, table string, body api.Request) (jsonData []byte, err error) {
	var result sql.Result
	var rowsAffected int64

	fields := make([]string, 0)
	values := make([]string, 0)
	for key, value := range body.Data {
		fields = append(fields, key)
		values = append(values, value)
	}
	colsName := strings.Join(fields, ", ")
	colsValue := strings.Join(values, "', '")
	sql := fmt.Sprintf("INSERT INTO %s.%s.%s (%s) VALUES ('%s')", database, schema, table, colsName, colsValue)

	db := Conn()
	result, err = db.Exec(sql)
	if err != nil {
		return
	}
	rowsAffected, err = result.RowsAffected()
	if err != nil {
		return
	}

	data := make(map[string]interface{})
	data["rows_affected"] = rowsAffected
	jsonData, err = json.Marshal(data)
	return
}

// Delete execute delete sql into a table
func Delete(database, schema, table, where string, whereValues []interface{}) (jsonData []byte, err error) {
	var result sql.Result
	var rowsAffected int64

	sql := fmt.Sprintf("DELETE FROM %s.%s.%s", database, schema, table)
	if where != "" {
		sql = fmt.Sprint(
			sql,
			" WHERE ",
			where)
	}

	db := Conn()
	result, err = db.Exec(sql, whereValues...)
	if err != nil {
		return
	}
	rowsAffected, err = result.RowsAffected()
	if err != nil {
		return
	}

	data := make(map[string]interface{})
	data["rows_affected"] = rowsAffected
	jsonData, err = json.Marshal(data)
	return
}

// Update execute update sql into a table
func Update(database, schema, table, where string, whereValues []interface{}, body api.Request) (jsonData []byte, err error) {
	var result sql.Result
	var rowsAffected int64

	fields := []string{}
	values := make([]interface{}, 0)
	pid := len(whereValues) + 1 // placeholder id
	for key, value := range body.Data {
		fields = append(fields, fmt.Sprintf("%s=$%d", key, pid))
		values = append(values, value)
		pid++
	}
	setSyntax := strings.Join(fields, ", ")

	sql := fmt.Sprintf("UPDATE %s.%s.%s SET %s", database, schema, table, setSyntax)

	if where != "" {
		sql = fmt.Sprint(
			sql,
			" WHERE ",
			where)
		values = append(values, whereValues...)
	}

	fmt.Println("SQL:", sql)
	fmt.Printf("values: %#v\n", values)
	fmt.Printf("values2: %v\n", values)

	db := Conn()
	//result, err = db.Exec(sql, values)
	stmt, err := db.Prepare(sql)
	if err != nil {
		return
	}

	valuesAux := make([]interface{}, 0, len(values))

	for i := 0; i < len(values); i++ {
		valuesAux = append(valuesAux, values[i])
	}

	result, err = stmt.Exec(valuesAux...)
	if err != nil {
		return
	}

	rowsAffected, err = result.RowsAffected()
	if err != nil {
		return
	}

	data := make(map[string]interface{})
	data["rows_affected"] = rowsAffected
	jsonData, err = json.Marshal(data)
	return
}
<|endoftext|>


<commit_before>// Package kubernetes provides abstractions for the Kubernetes platform. At the
// moment, Kubernetes is the only supported platform, so we are directly
// returning Kubernetes objects. As we add more platforms, we will create
// abstractions and common data types in package platform.
package kubernetes

import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"os/exec"
	"strings"
	"time"

	"github.com/go-kit/kit/log"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/client/restclient"
	k8sclient "k8s.io/kubernetes/pkg/client/unversioned"
)

// These errors are returned by cluster methods.
var (
	ErrEmptySelector                          = errors.New("empty selector")
	ErrNoMatchingService                      = errors.New("no matching service")
	ErrNoMatchingReplicationController        = errors.New("no matching replication controllers")
	ErrMultipleMatchingReplicationControllers = errors.New("multiple matching replication controllers")
)

// Cluster is a handle to a Kubernetes API server.
// (Typically, this code is deployed into the same cluster.)
type Cluster struct {
	config  *restclient.Config
	client  *k8sclient.Client
	kubectl string
	logger  log.Logger
}

// NewCluster returns a usable cluster. Host should be of the form
// "http://hostname:8080".
func NewCluster(config *restclient.Config, logger log.Logger) (*Cluster, error) {
	client, err := k8sclient.New(config)
	if err != nil {
		return nil, err
	}

	kubectl, err := exec.LookPath("kubectl")
	if err != nil {
		return nil, err
	}
	logger.Log("kubectl", kubectl)

	return &Cluster{
		config:  config,
		client:  client,
		kubectl: kubectl,
		logger:  logger,
	}, nil
}

// Services returns the set of services currently active on the platform in the
// given namespace. Maybe it makes sense to move the namespace to the
// constructor? Depends on how it will be used. For now it is here.
//
// The user is expected to list services, and then choose the one that will
// receive a release. Releases operate on replication controllers, not services.
// For now, we make a simplifying assumption that there is a one-to-one mapping
// between services and replication controllers.
func (c *Cluster) Services(namespace string) ([]api.Service, error) {
	list, err := c.client.Services(namespace).List(api.ListOptions{})
	if err != nil {
		return nil, err
	}
	return list.Items, nil
}

// Release performs a rolling update of the service, from whatever it is
// currently, to what is described by the new replication controller. Release
// assumes there is a one-to-one mapping between services and replication
// controllers; this can be improved. Release blocks until the rolling update is
// complete; this can be improved. Release invokes `kubectl rolling-update` in a
// seperate process, and assumes kubectl is in the PATH; this can be improved.
func (c *Cluster) Release(namespace, serviceName string, newReplicationControllerDefinition []byte, updatePeriod time.Duration) error {
	logger := log.NewContext(c.logger).With("method", "Release", "namespace", namespace, "service", serviceName)
	logger.Log()

	rc, err := c.replicationControllerFor(namespace, serviceName)
	if err != nil {
		return err
	}
	logger.Log("RC", rc.Name)

	var args []string
	if c.config.Host != "" {
		args = append(args, fmt.Sprintf("--server=%s", c.config.Host))
	}
	if c.config.Username != "" {
		args = append(args, fmt.Sprintf("--username=%s", c.config.Username))
	}
	if c.config.Password != "" {
		args = append(args, fmt.Sprintf("--password=%s", c.config.Password))
	}
	if c.config.TLSClientConfig.CertFile != "" {
		args = append(args, fmt.Sprintf("--client-certificate=%s", c.config.TLSClientConfig.CertFile))
	}
	if c.config.TLSClientConfig.KeyFile != "" {
		args = append(args, fmt.Sprintf("--client-key=%s", c.config.KeyFile))
	}
	args = append(args, []string{
		"--validate=false", // for some reason, this is required with our defs
		"rolling-update",
		rc.Name,
		fmt.Sprintf("--update-period=%s", updatePeriod),
		"-f", "-", // take definition from stdin
	}...)

	cmd := exec.Command(c.kubectl, args...)
	cmd.Stdin = bytes.NewReader(newReplicationControllerDefinition)
	cmd.Stdout = ioutil.Discard
	cmd.Stderr = ioutil.Discard
	logger.Log("cmd", strings.Join(cmd.Args, " "))

	err = cmd.Run()
	result := "success"
	if err != nil {
		result = err.Error()
	}
	logger.Log("result", result)

	return err
}

func (c *Cluster) replicationControllerFor(namespace, serviceName string) (api.ReplicationController, error) {
	logger := log.NewContext(c.logger).With("method", "replicationControllerFor", "namespace", namespace, "serviceName", serviceName)
	logger.Log()

	// First, get the service spec selector, which determines the pods that the
	// service will load balance over.
	services, err := c.Services(namespace)
	if err != nil {
		return api.ReplicationController{}, err
	}
	service, ok := func() (api.Service, bool) {
		for _, service := range services {
			if service.Name == serviceName { // assume names are unique
				return service, true
			}
		}
		return api.Service{}, false
	}()
	if !ok {
		return api.ReplicationController{}, ErrNoMatchingService
	}
	selector := service.Spec.Selector

	// Now, find a replication controller which produces pods that match that
	// selector. We have to match all of the criteria in the selector, but we
	// don't need a perfect match of all of the replication controller's pod
	// properties.
	list, err := c.client.ReplicationControllers(namespace).List(api.ListOptions{})
	if err != nil {
		return api.ReplicationController{}, err
	}
	var matches []api.ReplicationController
	for _, rc := range list.Items {
		match := func() bool {
			// For each key=value pair in the service spec, check if the RC
			// annotates its pods in the same way. If any rule fails, the RC is
			// not a match. If all rules pass, the RC is a match.
			for k, v := range selector {
				labels := rc.Spec.Template.Labels
				if labels[k] != v {
					return false
				}
			}
			return true
		}()
		logger.Log("RC", rc.Name, "match", match)
		if match {
			matches = append(matches, rc)
		}
	}
	logger.Log("matches", len(matches))

	// Our naïve, simplifying assumption: every service is satisfied by
	// precisely 1 replication controller.
	switch len(matches) {
	case 0:
		return api.ReplicationController{}, ErrNoMatchingReplicationController
	case 1:
		return matches[0], nil
	default:
		return api.ReplicationController{}, ErrMultipleMatchingReplicationControllers
	}
}
<commit_msg>platform/kubernetes: rm debug logging; add duration logging<commit_after>// Package kubernetes provides abstractions for the Kubernetes platform. At the
// moment, Kubernetes is the only supported platform, so we are directly
// returning Kubernetes objects. As we add more platforms, we will create
// abstractions and common data types in package platform.
package kubernetes

import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"os/exec"
	"strings"
	"time"

	"github.com/go-kit/kit/log"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/client/restclient"
	k8sclient "k8s.io/kubernetes/pkg/client/unversioned"
)

// These errors are returned by cluster methods.
var (
	ErrEmptySelector                          = errors.New("empty selector")
	ErrNoMatchingService                      = errors.New("no matching service")
	ErrNoMatchingReplicationController        = errors.New("no matching replication controllers")
	ErrMultipleMatchingReplicationControllers = errors.New("multiple matching replication controllers")
)

// Cluster is a handle to a Kubernetes API server.
// (Typically, this code is deployed into the same cluster.)
type Cluster struct {
	config  *restclient.Config
	client  *k8sclient.Client
	kubectl string
	logger  log.Logger
}

// NewCluster returns a usable cluster. Host should be of the form
// "http://hostname:8080".
func NewCluster(config *restclient.Config, logger log.Logger) (*Cluster, error) {
	client, err := k8sclient.New(config)
	if err != nil {
		return nil, err
	}

	kubectl, err := exec.LookPath("kubectl")
	if err != nil {
		return nil, err
	}
	logger.Log("kubectl", kubectl)

	return &Cluster{
		config:  config,
		client:  client,
		kubectl: kubectl,
		logger:  logger,
	}, nil
}

// Services returns the set of services currently active on the platform in the
// given namespace. Maybe it makes sense to move the namespace to the
// constructor? Depends on how it will be used. For now it is here.
//
// The user is expected to list services, and then choose the one that will
// receive a release. Releases operate on replication controllers, not services.
// For now, we make a simplifying assumption that there is a one-to-one mapping
// between services and replication controllers.
func (c *Cluster) Services(namespace string) ([]api.Service, error) {
	list, err := c.client.Services(namespace).List(api.ListOptions{})
	if err != nil {
		return nil, err
	}
	return list.Items, nil
}

// Release performs a rolling update of the service, from whatever it is
// currently, to what is described by the new replication controller. Release
// assumes there is a one-to-one mapping between services and replication
// controllers; this can be improved. Release blocks until the rolling update is
// complete; this can be improved. Release invokes `kubectl rolling-update` in a
// seperate process, and assumes kubectl is in the PATH; this can be improved.
func (c *Cluster) Release(namespace, serviceName string, newReplicationControllerDefinition []byte, updatePeriod time.Duration) error {
	logger := log.NewContext(c.logger).With("method", "Release", "namespace", namespace, "service", serviceName)
	logger.Log()

	rc, err := c.replicationControllerFor(namespace, serviceName)
	if err != nil {
		return err
	}
	logger.Log("RC", rc.Name)

	var args []string
	if c.config.Host != "" {
		args = append(args, fmt.Sprintf("--server=%s", c.config.Host))
	}
	if c.config.Username != "" {
		args = append(args, fmt.Sprintf("--username=%s", c.config.Username))
	}
	if c.config.Password != "" {
		args = append(args, fmt.Sprintf("--password=%s", c.config.Password))
	}
	if c.config.TLSClientConfig.CertFile != "" {
		args = append(args, fmt.Sprintf("--client-certificate=%s", c.config.TLSClientConfig.CertFile))
	}
	if c.config.TLSClientConfig.KeyFile != "" {
		args = append(args, fmt.Sprintf("--client-key=%s", c.config.KeyFile))
	}
	args = append(args, []string{
		"--validate=false", // for some reason, this is required with our defs
		"rolling-update",
		rc.Name,
		fmt.Sprintf("--update-period=%s", updatePeriod),
		"-f", "-", // take definition from stdin
	}...)

	cmd := exec.Command(c.kubectl, args...)
	cmd.Stdin = bytes.NewReader(newReplicationControllerDefinition)
	cmd.Stdout = ioutil.Discard
	cmd.Stderr = ioutil.Discard
	logger.Log("cmd", strings.Join(cmd.Args, " "))

	begin := time.Now()
	err = cmd.Run()
	result := "success"
	if err != nil {
		result = err.Error()
	}
	logger.Log("result", result, "took", time.Since(begin).String())

	return err
}

func (c *Cluster) replicationControllerFor(namespace, serviceName string) (api.ReplicationController, error) {
	logger := log.NewContext(c.logger).With("method", "replicationControllerFor", "namespace", namespace, "serviceName", serviceName)
	logger.Log()

	// First, get the service spec selector, which determines the pods that the
	// service will load balance over.
	services, err := c.Services(namespace)
	if err != nil {
		return api.ReplicationController{}, err
	}
	service, ok := func() (api.Service, bool) {
		for _, service := range services {
			if service.Name == serviceName { // assume names are unique
				return service, true
			}
		}
		return api.Service{}, false
	}()
	if !ok {
		return api.ReplicationController{}, ErrNoMatchingService
	}
	selector := service.Spec.Selector

	// Now, find a replication controller which produces pods that match that
	// selector. We have to match all of the criteria in the selector, but we
	// don't need a perfect match of all of the replication controller's pod
	// properties.
	list, err := c.client.ReplicationControllers(namespace).List(api.ListOptions{})
	if err != nil {
		return api.ReplicationController{}, err
	}
	var matches []api.ReplicationController
	for _, rc := range list.Items {
		match := func() bool {
			// For each key=value pair in the service spec, check if the RC
			// annotates its pods in the same way. If any rule fails, the RC is
			// not a match. If all rules pass, the RC is a match.
			for k, v := range selector {
				labels := rc.Spec.Template.Labels
				if labels[k] != v {
					return false
				}
			}
			return true
		}()
		if match {
			matches = append(matches, rc)
		}
	}

	// Our naïve, simplifying assumption: every service is satisfied by
	// precisely 1 replication controller.
	switch len(matches) {
	case 0:
		return api.ReplicationController{}, ErrNoMatchingReplicationController
	case 1:
		return matches[0], nil
	default:
		return api.ReplicationController{}, ErrMultipleMatchingReplicationControllers
	}
}
<|endoftext|>