| ... | ... |
@@ -1 +1,64 @@ |
| 1 | 1 |
OpenShift Origin 3.0 |
| 2 |
+==================== |
|
| 3 |
+ |
|
| 4 |
+This is the source repository for the next version of OpenShift - the third architectural revision. It is based around [Docker](https://www.docker.io) containers and images and the [Kubernetes](https://github.com/GoogleCloudPlatform/kubernetes) container management solution. OpenShift adds developer centric and organization centric workflows on top of Kubernetes, and much of the core functionality of OpenShift is designed as plugins to the core Kubernetes concepts. |
|
| 5 |
+ |
|
| 6 |
+Please see the [OpenShift 3 Project Enhancement Proposal (PEP)](https://github.com/openshift/openshift-pep/blob/master/openshift-pep-013-openshift-3.md) for a deeper discussion of the features you see here. |
|
| 7 |
+ |
|
| 8 |
+NOTE: This is a very early prototype, and as such is designed for rapid iteration around core concepts. |
|
| 9 |
+ |
|
| 10 |
+Getting Started |
|
| 11 |
+--------------- |
|
| 12 |
+ |
|
| 13 |
+You'll need Docker and the Go language compilation tools installed. |
|
| 14 |
+ |
|
| 15 |
+1. [Install Docker](https://docs.docker.com/installation/#installation) |
|
| 16 |
+2. [Install the Go language toolkit](http://golang.org/doc/install) and set your GOPATH |
|
| 17 |
+3. Clone this git repository through the Go tools: |
|
| 18 |
+ |
|
| 19 |
+ $ go get github.com/openshift/origin |
|
| 20 |
+ $ cd $GOPATH/src/github.com/openshift/origin |
|
| 21 |
+ |
|
| 22 |
+4. Run a build |
|
| 23 |
+ |
|
| 24 |
+ $ go get github.com/coreos/etcd |
|
| 25 |
+ $ hack/build-go.sh |
|
| 26 |
+ |
|
| 27 |
+5. Start an OpenShift all-in-one server (includes everything you need to try OpenShift) |
|
| 28 |
+ |
|
| 29 |
+ $ output/go/bin/openshift start |
|
| 30 |
+ |
|
| 31 |
+6. In another terminal window, switch to the directory: |
|
| 32 |
+ |
|
| 33 |
+ $ cd $GOPATH/src/github.com/openshift/origin |
|
| 34 |
+ $ output/go/bin/openshift kube create services -c examples/test-service.json |
|
| 35 |
+ |
|
| 36 |
+Coming soon: Vagrant environments supporting OpenShift - see [Kubernetes README.md](https://github.com/GoogleCloudPlatform/kubernetes/blob/master/README.md) for now. |
|
| 37 |
+ |
|
| 38 |
+API |
|
| 39 |
+--- |
|
| 40 |
+ |
|
| 41 |
+The OpenShift APIs are exposed at `http://localhost:8081/osapi/v1beta1/*`. |
|
| 42 |
+ |
|
| 43 |
+* `http://localhost:8080/api/v1beta1/services` (stub) |
|
| 44 |
+ |
|
| 45 |
+The Kubernetes APIs are exposed at `http://localhost:8080/api/v1beta1/*`: |
|
| 46 |
+ |
|
| 47 |
+* `http://localhost:8080/api/v1beta1/pods` |
|
| 48 |
+* `http://localhost:8080/api/v1beta1/services` |
|
| 49 |
+* `http://localhost:8080/api/v1beta1/replicationControllers` |
|
| 50 |
+* `http://localhost:8080/api/v1beta1/operations` |
|
| 51 |
+ |
|
| 52 |
+An draft of the proposed API is available [in this repository](https://rawgit.com/csrwng/oo-api-v3/master/oov3.html). Expect significant changes. |
|
| 53 |
+ |
|
| 54 |
+ |
|
| 55 |
+Contributing |
|
| 56 |
+------------ |
|
| 57 |
+ |
|
| 58 |
+Contributions are welcome - a more formal process is coming soon. In the meantime, open issues as necessary, ask questions on the OpenShift IRC channel (#openshift-dev on freenode), or get involved in the [Kubernetes project](https://github.com/GoogleCloudPlatform/kubernetes). |
|
| 59 |
+ |
|
| 60 |
+ |
|
| 61 |
+License |
|
| 62 |
+------- |
|
| 63 |
+ |
|
| 64 |
+OpenShift is licensed under the Apache Software License 2.0. |
|
| 2 | 65 |
\ No newline at end of file |
| 3 | 66 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,26 @@ |
| 0 |
+package main |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "log" |
|
| 4 |
+ "net/http" |
|
| 5 |
+ "time" |
|
| 6 |
+ |
|
| 7 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" |
|
| 8 |
+ "github.com/openshift/origin/pkg/api" |
|
| 9 |
+ "github.com/openshift/origin/pkg/service" |
|
| 10 |
+) |
|
| 11 |
+ |
|
| 12 |
+func main() {
|
|
| 13 |
+ storage := map[string]apiserver.RESTStorage{
|
|
| 14 |
+ "services": service.NewRESTStorage(service.MakeMemoryRegistry()), |
|
| 15 |
+ } |
|
| 16 |
+ |
|
| 17 |
+ s := &http.Server{
|
|
| 18 |
+ Addr: "127.0.0.1:8081", |
|
| 19 |
+ Handler: apiserver.New(storage, api.Codec, "/osapi/v1beta1"), |
|
| 20 |
+ ReadTimeout: 10 * time.Second, |
|
| 21 |
+ WriteTimeout: 10 * time.Second, |
|
| 22 |
+ MaxHeaderBytes: 1 << 20, |
|
| 23 |
+ } |
|
| 24 |
+ log.Fatal(s.ListenAndServe()) |
|
| 25 |
+} |
| 0 | 26 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,62 @@ |
| 0 |
+package main |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "os" |
|
| 5 |
+ |
|
| 6 |
+ kubeversion "github.com/GoogleCloudPlatform/kubernetes/pkg/version" |
|
| 7 |
+ "github.com/openshift/origin/pkg/cmd/client" |
|
| 8 |
+ "github.com/openshift/origin/pkg/cmd/master" |
|
| 9 |
+ "github.com/openshift/origin/pkg/version" |
|
| 10 |
+ "github.com/spf13/cobra" |
|
| 11 |
+) |
|
| 12 |
+ |
|
| 13 |
+const longDescription = ` |
|
| 14 |
+OpenShift for Admins |
|
| 15 |
+ |
|
| 16 |
+OpenShift helps you build, deploy, and manage your applications. To start an all-in-one server, run: |
|
| 17 |
+ |
|
| 18 |
+ $ openshift start & |
|
| 19 |
+ $ openshift kube create service -c examples/test-service.json |
|
| 20 |
+ |
|
| 21 |
+OpenShift is built around Docker and the Kubernetes container orchestration service. You must have |
|
| 22 |
+Docker installed on this machine to start your server. |
|
| 23 |
+ |
|
| 24 |
+Note: This is an alpha release of OpenShift and will change significantly. See |
|
| 25 |
+ |
|
| 26 |
+ https://github.com/openshift/origin |
|
| 27 |
+ |
|
| 28 |
+for the latest information on OpenShift. |
|
| 29 |
+ |
|
| 30 |
+` |
|
| 31 |
+ |
|
| 32 |
+func main() {
|
|
| 33 |
+ openshiftCmd := &cobra.Command{
|
|
| 34 |
+ Use: "openshift", |
|
| 35 |
+ Short: "OpenShift helps you build, deploy, and manage your applications", |
|
| 36 |
+ Long: longDescription, |
|
| 37 |
+ Run: func(c *cobra.Command, args []string) {
|
|
| 38 |
+ c.Help() |
|
| 39 |
+ }, |
|
| 40 |
+ } |
|
| 41 |
+ |
|
| 42 |
+ openshiftCmd.AddCommand(master.NewCommandStartAllInOne("start"))
|
|
| 43 |
+ openshiftCmd.AddCommand(client.NewCommandKubecfg("kube"))
|
|
| 44 |
+ |
|
| 45 |
+ // version information |
|
| 46 |
+ versionCmd := &cobra.Command{
|
|
| 47 |
+ Use: "version", |
|
| 48 |
+ Short: "Display version", |
|
| 49 |
+ Run: func(c *cobra.Command, args []string) {
|
|
| 50 |
+ major, minor, git := version.Get() |
|
| 51 |
+ fmt.Printf("openshift version %s.%s, build %s\n", major, minor, git)
|
|
| 52 |
+ fmt.Printf("kubernetes %v\n", kubeversion.Get())
|
|
| 53 |
+ }, |
|
| 54 |
+ } |
|
| 55 |
+ openshiftCmd.AddCommand(versionCmd) |
|
| 56 |
+ |
|
| 57 |
+ if err := openshiftCmd.Execute(); err != nil {
|
|
| 58 |
+ fmt.Fprintf(os.Stderr, "Error: %s", err) |
|
| 59 |
+ os.Exit(1) |
|
| 60 |
+ } |
|
| 61 |
+} |
| 0 | 62 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,4 @@ |
| 0 |
+// This is the source repository for OpenShift Origin - the best way to build, manage, and deploy |
|
| 1 |
+// applications in the cloud. The OpenShift 3.0 codebase is based around Docker images and containers |
|
| 2 |
+// and the Kubernetes container management system. |
|
| 3 |
+package origin |
| 0 | 9 |
new file mode 100755 |
| ... | ... |
@@ -0,0 +1,20 @@ |
| 0 |
+#!/bin/bash |
|
| 1 |
+ |
|
| 2 |
+# This script sets up a go workspace locally and builds all go components. |
|
| 3 |
+ |
|
| 4 |
+set -e |
|
| 5 |
+ |
|
| 6 |
+# Update the version. |
|
| 7 |
+$(dirname $0)/version-gen.sh |
|
| 8 |
+ |
|
| 9 |
+source $(dirname $0)/config-go.sh |
|
| 10 |
+ |
|
| 11 |
+cd "${OS_TARGET}"
|
|
| 12 |
+ |
|
| 13 |
+BINARIES="cmd/openshift" |
|
| 14 |
+ |
|
| 15 |
+if [ $# -gt 0 ]; then |
|
| 16 |
+ BINARIES="$@" |
|
| 17 |
+fi |
|
| 18 |
+ |
|
| 19 |
+go install $(for b in $BINARIES; do echo "${OS_GO_PACKAGE}"/${b}; done)
|
| 0 | 20 |
new file mode 100755 |
| ... | ... |
@@ -0,0 +1,72 @@ |
| 0 |
+#!/bin/bash |
|
| 1 |
+ |
|
| 2 |
+# This script sets up a go workspace locally and builds all go components. |
|
| 3 |
+# You can 'source' this file if you want to set up GOPATH in your local shell. |
|
| 4 |
+ |
|
| 5 |
+if [ "$(which go)" == "" ]; then |
|
| 6 |
+ echo "Can't find 'go' in PATH, please fix and retry." |
|
| 7 |
+ echo "See http://golang.org/doc/install for installation instructions." |
|
| 8 |
+ exit 1 |
|
| 9 |
+fi |
|
| 10 |
+ |
|
| 11 |
+# Travis continuous build uses a head go release that doesn't report |
|
| 12 |
+# a version number, so we skip this check on Travis. Its unnecessary |
|
| 13 |
+# there anyway. |
|
| 14 |
+if [ "${TRAVIS}" != "true" ]; then
|
|
| 15 |
+ GO_VERSION=($(go version)) |
|
| 16 |
+ |
|
| 17 |
+ if [ ${GO_VERSION[2]} \< "go1.2" ]; then
|
|
| 18 |
+ echo "Detected go version: ${GO_VERSION}."
|
|
| 19 |
+ echo "OpenShift requires go version 1.2 or greater." |
|
| 20 |
+ echo "Please install Go version 1.2 or later" |
|
| 21 |
+ exit 1 |
|
| 22 |
+ fi |
|
| 23 |
+fi |
|
| 24 |
+ |
|
| 25 |
+pushd $(dirname "${BASH_SOURCE}")/.. >/dev/null
|
|
| 26 |
+OS_REPO_ROOT="${PWD}"
|
|
| 27 |
+OS_TARGET="${OS_REPO_ROOT}/output/go"
|
|
| 28 |
+popd >/dev/null |
|
| 29 |
+ |
|
| 30 |
+mkdir -p "${OS_TARGET}"
|
|
| 31 |
+ |
|
| 32 |
+OLD_GOPATH="${GOPATH}"
|
|
| 33 |
+export GOPATH="${OS_TARGET}"
|
|
| 34 |
+ |
|
| 35 |
+OS_GO_PACKAGE="github.com/openshift/origin" |
|
| 36 |
+OS_GO_PACKAGE_DIR="${GOPATH}/src/${OS_GO_PACKAGE}"
|
|
| 37 |
+ |
|
| 38 |
+ETCD_GO_PACKAGE="github.com/coreos/etcd" |
|
| 39 |
+ETCD_GO_PACKAGE_DIR="${GOPATH}/src/${ETCD_GO_PACKAGE}"
|
|
| 40 |
+if [ ! -d "${OLD_GOPATH}/src/${ETCD_GO_PACKAGE}" ]; then
|
|
| 41 |
+ echo "You must go get ${ETCD_GO_PACKAGE}"
|
|
| 42 |
+fi |
|
| 43 |
+ |
|
| 44 |
+( |
|
| 45 |
+ PACKAGE_BASE=$(dirname "${OS_GO_PACKAGE_DIR}")
|
|
| 46 |
+ if [ ! -d "${PACKAGE_BASE}" ]; then
|
|
| 47 |
+ mkdir -p "${PACKAGE_BASE}"
|
|
| 48 |
+ fi |
|
| 49 |
+ rm "${OS_GO_PACKAGE_DIR}" >/dev/null 2>&1 || true
|
|
| 50 |
+ ln -s "${OS_REPO_ROOT}" "${OS_GO_PACKAGE_DIR}"
|
|
| 51 |
+ |
|
| 52 |
+ PACKAGE_BASE=$(dirname "${ETCD_GO_PACKAGE_DIR}")
|
|
| 53 |
+ if [ ! -d "${PACKAGE_BASE}" ]; then
|
|
| 54 |
+ mkdir -p "${PACKAGE_BASE}"
|
|
| 55 |
+ fi |
|
| 56 |
+ rm "${ETCD_GO_PACKAGE_DIR}" >/dev/null 2>&1 || true
|
|
| 57 |
+ ln -s "${OLD_GOPATH}/src/${ETCD_GO_PACKAGE}" "${ETCD_GO_PACKAGE_DIR}"
|
|
| 58 |
+ |
|
| 59 |
+ |
|
| 60 |
+ if [[ "$OS_KUBE_PATH" != "" ]]; then |
|
| 61 |
+ echo "Using Kubernetes from source $OS_KUBE_PATH" |
|
| 62 |
+ OS_GO_KUBE_PACKAGE_DIR="${OS_TARGET}/src/github.com/GoogleCloudPlatform/kubernetes"
|
|
| 63 |
+ KUBE_PACKAGE_BASE=$(dirname "${OS_GO_KUBE_PACKAGE_DIR}")
|
|
| 64 |
+ if [ ! -d "${KUBE_PACKAGE_BASE}" ]; then
|
|
| 65 |
+ mkdir -p "${KUBE_PACKAGE_BASE}"
|
|
| 66 |
+ fi |
|
| 67 |
+ rm "${OS_GO_KUBE_PACKAGE_DIR}" >/dev/null 2>&1 || true
|
|
| 68 |
+ ln -s "${OS_KUBE_PATH}" "${OS_GO_KUBE_PACKAGE_DIR}"
|
|
| 69 |
+ fi |
|
| 70 |
+) |
|
| 71 |
+export GOPATH="${OS_TARGET}:${OS_REPO_ROOT}/third_party/src/github.com/GoogleCloudPlatform/kubernetes/third_party:${OS_REPO_ROOT}/third_party"
|
| 0 | 72 |
new file mode 100755 |
| ... | ... |
@@ -0,0 +1,35 @@ |
| 0 |
+#!/bin/bash |
|
| 1 |
+ |
|
| 2 |
+set -e |
|
| 3 |
+ |
|
| 4 |
+source $(dirname $0)/config-go.sh |
|
| 5 |
+ |
|
| 6 |
+ |
|
| 7 |
+find_test_dirs() {
|
|
| 8 |
+ ( |
|
| 9 |
+ cd src/${OS_GO_PACKAGE}
|
|
| 10 |
+ find . -not \( \ |
|
| 11 |
+ \( \ |
|
| 12 |
+ -wholename './third_party' \ |
|
| 13 |
+ -o -wholename './release' \ |
|
| 14 |
+ -o -wholename './target' \ |
|
| 15 |
+ -o -wholename '*/third_party/*' \ |
|
| 16 |
+ -o -wholename '*/output/*' \ |
|
| 17 |
+ \) -prune \ |
|
| 18 |
+ \) -name '*_test.go' -print0 | xargs -0n1 dirname | sort -u |
|
| 19 |
+ ) |
|
| 20 |
+} |
|
| 21 |
+ |
|
| 22 |
+# -covermode=atomic becomes default with -race in Go >=1.3 |
|
| 23 |
+COVER="-cover -covermode=atomic -coverprofile=tmp.out" |
|
| 24 |
+ |
|
| 25 |
+cd "${OS_TARGET}"
|
|
| 26 |
+ |
|
| 27 |
+if [ "$1" != "" ]; then |
|
| 28 |
+ go test -race -timeout 30s $COVER "$OS_GO_PACKAGE/$1" "${@:2}"
|
|
| 29 |
+ exit 0 |
|
| 30 |
+fi |
|
| 31 |
+ |
|
| 32 |
+for package in $(find_test_dirs); do |
|
| 33 |
+ go test -race -timeout 30s $COVER "${OS_GO_PACKAGE}/${package}" "${@:2}"
|
|
| 34 |
+done |
| 0 | 35 |
new file mode 100755 |
| ... | ... |
@@ -0,0 +1,11 @@ |
| 0 |
+#!/bin/bash |
|
| 1 |
+ |
|
| 2 |
+$(dirname $0)/../third_party/src/github.com/GoogleCloudPlatform/kubernetes/hack/version-gen.sh |
|
| 3 |
+ |
|
| 4 |
+# TODO: when we start making tags, switch to git describe? |
|
| 5 |
+desc=$(git rev-list --abbrev-commit --max-count=1 HEAD) |
|
| 6 |
+tab=$'\t' |
|
| 7 |
+script="6s/.*/${tab}commitFromGit = \`${desc}\`/"
|
|
| 8 |
+infile="$(dirname $0)/../pkg/version/template.go" |
|
| 9 |
+outfile="$(dirname $0)/../pkg/version/autogenerated.go" |
|
| 10 |
+sed "${script}" "${infile}" > "${outfile}"
|
| 0 | 11 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,86 @@ |
| 0 |
+package api |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
| 4 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" |
|
| 5 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/conversion" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+type ServiceList struct {
|
|
| 9 |
+ api.JSONBase `json:",inline" yaml:",inline"` |
|
| 10 |
+ Items []Service `json:"items" yaml:"items,omitempty"` |
|
| 11 |
+} |
|
| 12 |
+ |
|
| 13 |
+type Service struct {
|
|
| 14 |
+ api.JSONBase `json:",inline" yaml:",inline"` |
|
| 15 |
+ Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` |
|
| 16 |
+} |
|
| 17 |
+ |
|
| 18 |
+// codec defines methods for serializing and deserializing API |
|
| 19 |
+// objects |
|
| 20 |
+type codec interface {
|
|
| 21 |
+ Encode(obj interface{}) (data []byte, err error)
|
|
| 22 |
+ Decode(data []byte) (interface{}, error)
|
|
| 23 |
+ DecodeInto(data []byte, obj interface{}) error
|
|
| 24 |
+} |
|
| 25 |
+ |
|
| 26 |
+// resourceVersioner provides methods for setting and retrieving |
|
| 27 |
+// the resource version from an API object |
|
| 28 |
+type resourceVersioner interface {
|
|
| 29 |
+ SetResourceVersion(obj interface{}, version uint64) error
|
|
| 30 |
+ ResourceVersion(obj interface{}) (uint64, error)
|
|
| 31 |
+} |
|
| 32 |
+ |
|
| 33 |
+var Codec codec |
|
| 34 |
+var ResourceVersioner resourceVersioner |
|
| 35 |
+ |
|
| 36 |
+var scheme *conversion.Scheme |
|
| 37 |
+ |
|
| 38 |
+func init() {
|
|
| 39 |
+ scheme = conversion.NewScheme() |
|
| 40 |
+ scheme.InternalVersion = "" |
|
| 41 |
+ scheme.ExternalVersion = "v1beta1" |
|
| 42 |
+ scheme.MetaInsertionFactory = metaInsertion{}
|
|
| 43 |
+ scheme.AddKnownTypes("",
|
|
| 44 |
+ ServiceList{},
|
|
| 45 |
+ Service{},
|
|
| 46 |
+ api.Status{},
|
|
| 47 |
+ api.ServerOp{},
|
|
| 48 |
+ api.ServerOpList{},
|
|
| 49 |
+ ) |
|
| 50 |
+ scheme.AddKnownTypes("v1beta1",
|
|
| 51 |
+ ServiceList{},
|
|
| 52 |
+ Service{},
|
|
| 53 |
+ v1beta1.Status{},
|
|
| 54 |
+ v1beta1.ServerOp{},
|
|
| 55 |
+ v1beta1.ServerOpList{},
|
|
| 56 |
+ ) |
|
| 57 |
+ |
|
| 58 |
+ Codec = scheme |
|
| 59 |
+ ResourceVersioner = api.NewJSONBaseResourceVersioner() |
|
| 60 |
+} |
|
| 61 |
+ |
|
| 62 |
+// metaInsertion implements conversion.MetaInsertionFactory, which lets the conversion |
|
| 63 |
+// package figure out how to encode our object's types and versions. These fields are |
|
| 64 |
+// located in our JSONBase. |
|
| 65 |
+type metaInsertion struct {
|
|
| 66 |
+ JSONBase struct {
|
|
| 67 |
+ APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` |
|
| 68 |
+ Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` |
|
| 69 |
+ } `json:",inline" yaml:",inline"` |
|
| 70 |
+} |
|
| 71 |
+ |
|
| 72 |
+// Create returns a new metaInsertion with the version and kind fields set. |
|
| 73 |
+func (metaInsertion) Create(version, kind string) interface{} {
|
|
| 74 |
+ m := metaInsertion{}
|
|
| 75 |
+ m.JSONBase.APIVersion = version |
|
| 76 |
+ m.JSONBase.Kind = kind |
|
| 77 |
+ return &m |
|
| 78 |
+} |
|
| 79 |
+ |
|
| 80 |
+// Interpret returns the version and kind information from in, which must be |
|
| 81 |
+// a metaInsertion pointer object. |
|
| 82 |
+func (metaInsertion) Interpret(in interface{}) (version, kind string) {
|
|
| 83 |
+ m := in.(*metaInsertion) |
|
| 84 |
+ return m.JSONBase.APIVersion, m.JSONBase.Kind |
|
| 85 |
+} |
| 0 | 86 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,49 @@ |
| 0 |
+package client |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "os" |
|
| 4 |
+ "time" |
|
| 5 |
+ |
|
| 6 |
+ "github.com/spf13/cobra" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+const longDescription = ` |
|
| 10 |
+Kubernetes Command Line - kubecfg |
|
| 11 |
+ |
|
| 12 |
+OpenShift currently embeds the kubecfg command line for prototyping and debugging. |
|
| 13 |
+` |
|
| 14 |
+ |
|
| 15 |
+func NewCommandKubecfg(name string) *cobra.Command {
|
|
| 16 |
+ cfg := &KubeConfig{}
|
|
| 17 |
+ cmd := &cobra.Command{
|
|
| 18 |
+ Use: name, |
|
| 19 |
+ Short: "The Kubernetes command line client", |
|
| 20 |
+ Long: longDescription + usage(name), |
|
| 21 |
+ Run: func(c *cobra.Command, args []string) {
|
|
| 22 |
+ if len(args) < 1 {
|
|
| 23 |
+ c.Help() |
|
| 24 |
+ os.Exit(1) |
|
| 25 |
+ } |
|
| 26 |
+ cfg.Args = args |
|
| 27 |
+ cfg.Run() |
|
| 28 |
+ }, |
|
| 29 |
+ } |
|
| 30 |
+ flag := cmd.Flags() |
|
| 31 |
+ flag.BoolVar(&cfg.ServerVersion, "server_version", false, "Print the server's version number.") |
|
| 32 |
+ flag.BoolVar(&cfg.PreventSkew, "expect_version_match", false, "Fail if server's version doesn't match own version.") |
|
| 33 |
+ flag.StringVarP(&cfg.HttpServer, "host", "h", "", "The host to connect to.") |
|
| 34 |
+ flag.StringVarP(&cfg.Config, "config", "c", "", "Path to the config file.") |
|
| 35 |
+ flag.StringVarP(&cfg.Selector, "label", "l", "", "Selector (label query) to use for listing") |
|
| 36 |
+ flag.DurationVarP(&cfg.UpdatePeriod, "update", "u", 60*time.Second, "Update interval period") |
|
| 37 |
+ flag.StringVarP(&cfg.PortSpec, "port", "p", "", "The port spec, comma-separated list of <external>:<internal>,...") |
|
| 38 |
+ flag.IntVarP(&cfg.ServicePort, "service", "s", -1, "If positive, create and run a corresponding service on this port, only used with 'run'") |
|
| 39 |
+ flag.StringVar(&cfg.AuthConfig, "auth", os.Getenv("HOME")+"/.kubernetes_auth", "Path to the auth info file. If missing, prompt the user. Only used if doing https.")
|
|
| 40 |
+ flag.BoolVar(&cfg.JSON, "json", false, "If true, print raw JSON for responses") |
|
| 41 |
+ flag.BoolVar(&cfg.YAML, "yaml", false, "If true, print raw YAML for responses") |
|
| 42 |
+ flag.BoolVar(&cfg.Verbose, "verbose", false, "If true, print extra information") |
|
| 43 |
+ flag.BoolVar(&cfg.Proxy, "proxy", false, "If true, run a proxy to the api server") |
|
| 44 |
+ flag.StringVar(&cfg.WWW, "www", "", "If -proxy is true, use this directory to serve static files") |
|
| 45 |
+ flag.StringVar(&cfg.TemplateFile, "template_file", "", "If present, load this file as a golang template and use it for output printing") |
|
| 46 |
+ flag.StringVar(&cfg.TemplateStr, "template", "", "If present, parse this string as a golang template and use it for output printing") |
|
| 47 |
+ return cmd |
|
| 48 |
+} |
| 0 | 49 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,355 @@ |
| 0 |
+/* |
|
| 1 |
+Copyright 2014 Google Inc. All rights reserved. |
|
| 2 |
+ |
|
| 3 |
+Licensed under the Apache License, Version 2.0 (the "License"); |
|
| 4 |
+you may not use this file except in compliance with the License. |
|
| 5 |
+You may obtain a copy of the License at |
|
| 6 |
+ |
|
| 7 |
+ http://www.apache.org/licenses/LICENSE-2.0 |
|
| 8 |
+ |
|
| 9 |
+Unless required by applicable law or agreed to in writing, software |
|
| 10 |
+distributed under the License is distributed on an "AS IS" BASIS, |
|
| 11 |
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 12 |
+See the License for the specific language governing permissions and |
|
| 13 |
+limitations under the License. |
|
| 14 |
+*/ |
|
| 15 |
+ |
|
| 16 |
+package client |
|
| 17 |
+ |
|
| 18 |
+import ( |
|
| 19 |
+ "fmt" |
|
| 20 |
+ "io/ioutil" |
|
| 21 |
+ "net/url" |
|
| 22 |
+ "os" |
|
| 23 |
+ "reflect" |
|
| 24 |
+ "sort" |
|
| 25 |
+ "strconv" |
|
| 26 |
+ "strings" |
|
| 27 |
+ "text/template" |
|
| 28 |
+ "time" |
|
| 29 |
+ |
|
| 30 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
| 31 |
+ kubeclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client" |
|
| 32 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/kubecfg" |
|
| 33 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/util" |
|
| 34 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/version" |
|
| 35 |
+ "github.com/golang/glog" |
|
| 36 |
+) |
|
| 37 |
+ |
|
| 38 |
+type KubeConfig struct {
|
|
| 39 |
+ ServerVersion bool |
|
| 40 |
+ PreventSkew bool |
|
| 41 |
+ HttpServer string |
|
| 42 |
+ Config string |
|
| 43 |
+ Selector string |
|
| 44 |
+ UpdatePeriod time.Duration |
|
| 45 |
+ PortSpec string |
|
| 46 |
+ ServicePort int |
|
| 47 |
+ AuthConfig string |
|
| 48 |
+ JSON bool |
|
| 49 |
+ YAML bool |
|
| 50 |
+ Verbose bool |
|
| 51 |
+ Proxy bool |
|
| 52 |
+ WWW string |
|
| 53 |
+ TemplateFile string |
|
| 54 |
+ TemplateStr string |
|
| 55 |
+ |
|
| 56 |
+ Args []string |
|
| 57 |
+} |
|
| 58 |
+ |
|
| 59 |
+func (c *KubeConfig) Arg(index int) string {
|
|
| 60 |
+ if index >= len(c.Args) {
|
|
| 61 |
+ return "" |
|
| 62 |
+ } |
|
| 63 |
+ return c.Args[index] |
|
| 64 |
+} |
|
| 65 |
+ |
|
| 66 |
+func usage(name string) string {
|
|
| 67 |
+ return fmt.Sprintf(` |
|
| 68 |
+ Kubernetes REST API: |
|
| 69 |
+ %[1]s [OPTIONS] get|list|create|delete|update <%[2]s>[/<id>] |
|
| 70 |
+ |
|
| 71 |
+ Manage replication controllers: |
|
| 72 |
+ %[1]s [OPTIONS] stop|rm|rollingupdate <controller> |
|
| 73 |
+ %[1]s [OPTIONS] run <image> <replicas> <controller> |
|
| 74 |
+ %[1]s [OPTIONS] resize <controller> <replicas> |
|
| 75 |
+`, name, prettyWireStorage()) |
|
| 76 |
+} |
|
| 77 |
+ |
|
| 78 |
+func prettyWireStorage() string {
|
|
| 79 |
+ types := kubecfg.SupportedWireStorage() |
|
| 80 |
+ sort.Strings(types) |
|
| 81 |
+ return strings.Join(types, "|") |
|
| 82 |
+} |
|
| 83 |
+ |
|
| 84 |
+// readConfig reads and parses pod, replicationController, and service |
|
| 85 |
+// configuration files. If any errors log and exit non-zero. |
|
| 86 |
+func (c *KubeConfig) readConfig(storage string) []byte {
|
|
| 87 |
+ if len(c.Config) == 0 {
|
|
| 88 |
+ glog.Fatal("Need config file (-c)")
|
|
| 89 |
+ } |
|
| 90 |
+ data, err := ioutil.ReadFile(c.Config) |
|
| 91 |
+ if err != nil {
|
|
| 92 |
+ glog.Fatalf("Unable to read %v: %v\n", c.Config, err)
|
|
| 93 |
+ } |
|
| 94 |
+ data, err = kubecfg.ToWireFormat(data, storage) |
|
| 95 |
+ if err != nil {
|
|
| 96 |
+ glog.Fatalf("Error parsing %v as an object for %v: %v\n", c.Config, storage, err)
|
|
| 97 |
+ } |
|
| 98 |
+ if c.Verbose {
|
|
| 99 |
+ glog.Infof("Parsed config file successfully; sending:\n%v\n", string(data))
|
|
| 100 |
+ } |
|
| 101 |
+ return data |
|
| 102 |
+} |
|
| 103 |
+ |
|
| 104 |
+func (c *KubeConfig) Run() {
|
|
| 105 |
+ util.InitLogs() |
|
| 106 |
+ defer util.FlushLogs() |
|
| 107 |
+ |
|
| 108 |
+ secure := true |
|
| 109 |
+ var masterServer string |
|
| 110 |
+ if len(c.HttpServer) > 0 {
|
|
| 111 |
+ masterServer = c.HttpServer |
|
| 112 |
+ } else if len(os.Getenv("KUBERNETES_MASTER")) > 0 {
|
|
| 113 |
+ masterServer = os.Getenv("KUBERNETES_MASTER")
|
|
| 114 |
+ } else {
|
|
| 115 |
+ masterServer = "http://localhost:8080" |
|
| 116 |
+ } |
|
| 117 |
+ parsedURL, err := url.Parse(masterServer) |
|
| 118 |
+ if err != nil {
|
|
| 119 |
+ glog.Fatalf("Unable to parse %v as a URL\n", err)
|
|
| 120 |
+ } |
|
| 121 |
+ if parsedURL.Scheme != "" && parsedURL.Scheme != "https" {
|
|
| 122 |
+ secure = false |
|
| 123 |
+ } |
|
| 124 |
+ |
|
| 125 |
+ var auth *kubeclient.AuthInfo |
|
| 126 |
+ if secure {
|
|
| 127 |
+ auth, err = kubecfg.LoadAuthInfo(c.AuthConfig, os.Stdin) |
|
| 128 |
+ if err != nil {
|
|
| 129 |
+ glog.Fatalf("Error loading auth: %v", err)
|
|
| 130 |
+ } |
|
| 131 |
+ } |
|
| 132 |
+ |
|
| 133 |
+ client := kubeclient.New(masterServer, auth) |
|
| 134 |
+ |
|
| 135 |
+ if c.ServerVersion {
|
|
| 136 |
+ got, err := client.ServerVersion() |
|
| 137 |
+ if err != nil {
|
|
| 138 |
+ fmt.Printf("Couldn't read version from server: %v\n", err)
|
|
| 139 |
+ os.Exit(1) |
|
| 140 |
+ } |
|
| 141 |
+ fmt.Printf("Server Version: %#v\n", got)
|
|
| 142 |
+ os.Exit(0) |
|
| 143 |
+ } |
|
| 144 |
+ |
|
| 145 |
+ if c.PreventSkew {
|
|
| 146 |
+ got, err := client.ServerVersion() |
|
| 147 |
+ if err != nil {
|
|
| 148 |
+ fmt.Printf("Couldn't read version from server: %v\n", err)
|
|
| 149 |
+ os.Exit(1) |
|
| 150 |
+ } |
|
| 151 |
+ if c, s := version.Get(), *got; !reflect.DeepEqual(c, s) {
|
|
| 152 |
+ fmt.Printf("Server version (%#v) differs from client version (%#v)!\n", s, c)
|
|
| 153 |
+ os.Exit(1) |
|
| 154 |
+ } |
|
| 155 |
+ } |
|
| 156 |
+ |
|
| 157 |
+ if c.Proxy {
|
|
| 158 |
+ glog.Info("Starting to serve on localhost:8001")
|
|
| 159 |
+ server := kubecfg.NewProxyServer(c.WWW, masterServer, auth) |
|
| 160 |
+ glog.Fatal(server.Serve()) |
|
| 161 |
+ } |
|
| 162 |
+ |
|
| 163 |
+ method := c.Arg(0) |
|
| 164 |
+ |
|
| 165 |
+ matchFound := c.executeAPIRequest(method, client) || c.executeControllerRequest(method, client) |
|
| 166 |
+ if matchFound == false {
|
|
| 167 |
+ glog.Fatalf("Unknown command %s", method)
|
|
| 168 |
+ } |
|
| 169 |
+} |
|
| 170 |
+ |
|
| 171 |
+// storagePathFromArg normalizes a path and breaks out the first segment if available |
|
| 172 |
+func storagePathFromArg(arg string) (storage, path string, hasSuffix bool) {
|
|
| 173 |
+ path = strings.Trim(arg, "/") |
|
| 174 |
+ segments := strings.SplitN(path, "/", 2) |
|
| 175 |
+ storage = segments[0] |
|
| 176 |
+ if len(segments) > 1 && segments[1] != "" {
|
|
| 177 |
+ hasSuffix = true |
|
| 178 |
+ } |
|
| 179 |
+ return storage, path, hasSuffix |
|
| 180 |
+} |
|
| 181 |
+ |
|
| 182 |
+//checkStorage returns true if the provided storage is valid |
|
| 183 |
+func checkStorage(storage string) bool {
|
|
| 184 |
+ for _, allowed := range kubecfg.SupportedWireStorage() {
|
|
| 185 |
+ if allowed == storage {
|
|
| 186 |
+ return true |
|
| 187 |
+ } |
|
| 188 |
+ } |
|
| 189 |
+ return false |
|
| 190 |
+} |
|
| 191 |
+ |
|
| 192 |
+func (c *KubeConfig) executeAPIRequest(method string, client *kubeclient.Client) bool {
|
|
| 193 |
+ storage, path, hasSuffix := storagePathFromArg(c.Arg(1)) |
|
| 194 |
+ validStorage := checkStorage(storage) |
|
| 195 |
+ verb := "" |
|
| 196 |
+ setBody := false |
|
| 197 |
+ var version uint64 |
|
| 198 |
+ switch method {
|
|
| 199 |
+ case "get": |
|
| 200 |
+ verb = "GET" |
|
| 201 |
+ if !validStorage || !hasSuffix {
|
|
| 202 |
+ glog.Fatalf("usage: kubecfg [OPTIONS] %s <%s>[/<id>]", method, prettyWireStorage())
|
|
| 203 |
+ } |
|
| 204 |
+ case "list": |
|
| 205 |
+ verb = "GET" |
|
| 206 |
+ if !validStorage || hasSuffix {
|
|
| 207 |
+ glog.Fatalf("usage: kubecfg [OPTIONS] %s <%s>", method, prettyWireStorage())
|
|
| 208 |
+ } |
|
| 209 |
+ case "delete": |
|
| 210 |
+ verb = "DELETE" |
|
| 211 |
+ if !validStorage || !hasSuffix {
|
|
| 212 |
+ glog.Fatalf("usage: kubecfg [OPTIONS] %s <%s>/<id>", method, prettyWireStorage())
|
|
| 213 |
+ } |
|
| 214 |
+ case "create": |
|
| 215 |
+ verb = "POST" |
|
| 216 |
+ setBody = true |
|
| 217 |
+ if !validStorage || hasSuffix {
|
|
| 218 |
+ glog.Fatalf("usage: kubecfg [OPTIONS] %s <%s>", method, prettyWireStorage())
|
|
| 219 |
+ } |
|
| 220 |
+ case "update": |
|
| 221 |
+ obj, err := client.Verb("GET").Path(path).Do().Get()
|
|
| 222 |
+ if err != nil {
|
|
| 223 |
+ glog.Fatalf("error obtaining resource version for update: %v", err)
|
|
| 224 |
+ } |
|
| 225 |
+ jsonBase, err := api.FindJSONBase(obj) |
|
| 226 |
+ if err != nil {
|
|
| 227 |
+ glog.Fatalf("error finding json base for update: %v", err)
|
|
| 228 |
+ } |
|
| 229 |
+ version = jsonBase.ResourceVersion() |
|
| 230 |
+ verb = "PUT" |
|
| 231 |
+ setBody = true |
|
| 232 |
+ if !validStorage || !hasSuffix {
|
|
| 233 |
+ glog.Fatalf("usage: kubecfg [OPTIONS] %s <%s>/<id>", method, prettyWireStorage())
|
|
| 234 |
+ } |
|
| 235 |
+ default: |
|
| 236 |
+ return false |
|
| 237 |
+ } |
|
| 238 |
+ |
|
| 239 |
+ r := client.Verb(verb). |
|
| 240 |
+ Path(path). |
|
| 241 |
+ ParseSelector(c.Selector) |
|
| 242 |
+ if setBody {
|
|
| 243 |
+ if version != 0 {
|
|
| 244 |
+ data := c.readConfig(storage) |
|
| 245 |
+ obj, err := api.Decode(data) |
|
| 246 |
+ if err != nil {
|
|
| 247 |
+ glog.Fatalf("error setting resource version: %v", err)
|
|
| 248 |
+ } |
|
| 249 |
+ jsonBase, err := api.FindJSONBase(obj) |
|
| 250 |
+ if err != nil {
|
|
| 251 |
+ glog.Fatalf("error setting resource version: %v", err)
|
|
| 252 |
+ } |
|
| 253 |
+ jsonBase.SetResourceVersion(version) |
|
| 254 |
+ data, err = api.Encode(obj) |
|
| 255 |
+ if err != nil {
|
|
| 256 |
+ glog.Fatalf("error setting resource version: %v", err)
|
|
| 257 |
+ } |
|
| 258 |
+ r.Body(data) |
|
| 259 |
+ } else {
|
|
| 260 |
+ r.Body(c.readConfig(storage)) |
|
| 261 |
+ } |
|
| 262 |
+ } |
|
| 263 |
+ result := r.Do() |
|
| 264 |
+ obj, err := result.Get() |
|
| 265 |
+ if err != nil {
|
|
| 266 |
+ glog.Fatalf("Got request error: %v\n", err)
|
|
| 267 |
+ return false |
|
| 268 |
+ } |
|
| 269 |
+ |
|
| 270 |
+ var printer kubecfg.ResourcePrinter |
|
| 271 |
+ switch {
|
|
| 272 |
+ case c.JSON: |
|
| 273 |
+ printer = &kubecfg.IdentityPrinter{}
|
|
| 274 |
+ case c.YAML: |
|
| 275 |
+ printer = &kubecfg.YAMLPrinter{}
|
|
| 276 |
+ case len(c.TemplateFile) > 0 || len(c.TemplateStr) > 0: |
|
| 277 |
+ var data []byte |
|
| 278 |
+ if len(c.TemplateFile) > 0 {
|
|
| 279 |
+ var err error |
|
| 280 |
+ data, err = ioutil.ReadFile(c.TemplateFile) |
|
| 281 |
+ if err != nil {
|
|
| 282 |
+ glog.Fatalf("Error reading template %s, %v\n", c.TemplateFile, err)
|
|
| 283 |
+ return false |
|
| 284 |
+ } |
|
| 285 |
+ } else {
|
|
| 286 |
+ data = []byte(c.TemplateStr) |
|
| 287 |
+ } |
|
| 288 |
+ tmpl, err := template.New("output").Parse(string(data))
|
|
| 289 |
+ if err != nil {
|
|
| 290 |
+ glog.Fatalf("Error parsing template %s, %v\n", string(data), err)
|
|
| 291 |
+ return false |
|
| 292 |
+ } |
|
| 293 |
+ printer = &kubecfg.TemplatePrinter{
|
|
| 294 |
+ Template: tmpl, |
|
| 295 |
+ } |
|
| 296 |
+ default: |
|
| 297 |
+ printer = &kubecfg.HumanReadablePrinter{}
|
|
| 298 |
+ } |
|
| 299 |
+ |
|
| 300 |
+ if err = printer.PrintObj(obj, os.Stdout); err != nil {
|
|
| 301 |
+ body, _ := result.Raw() |
|
| 302 |
+ glog.Fatalf("Failed to print: %v\nRaw received object:\n%#v\n\nBody received: %v", err, obj, string(body))
|
|
| 303 |
+ } |
|
| 304 |
+ fmt.Print("\n")
|
|
| 305 |
+ |
|
| 306 |
+ return true |
|
| 307 |
+} |
|
| 308 |
+ |
|
| 309 |
+func (c *KubeConfig) executeControllerRequest(method string, client *kubeclient.Client) bool {
|
|
| 310 |
+ parseController := func() string {
|
|
| 311 |
+ if len(c.Args) != 2 {
|
|
| 312 |
+ glog.Fatal("usage: kubecfg [OPTIONS] stop|rm|rollingupdate <controller>")
|
|
| 313 |
+ } |
|
| 314 |
+ return c.Arg(1) |
|
| 315 |
+ } |
|
| 316 |
+ |
|
| 317 |
+ var err error |
|
| 318 |
+ switch method {
|
|
| 319 |
+ case "stop": |
|
| 320 |
+ err = kubecfg.StopController(parseController(), client) |
|
| 321 |
+ case "rm": |
|
| 322 |
+ err = kubecfg.DeleteController(parseController(), client) |
|
| 323 |
+ case "rollingupdate": |
|
| 324 |
+ err = kubecfg.Update(parseController(), client, c.UpdatePeriod) |
|
| 325 |
+ case "run": |
|
| 326 |
+ if len(c.Args) != 4 {
|
|
| 327 |
+ glog.Fatal("usage: kubecfg [OPTIONS] run <image> <replicas> <controller>")
|
|
| 328 |
+ } |
|
| 329 |
+ image := c.Arg(1) |
|
| 330 |
+ replicas, err := strconv.Atoi(c.Arg(2)) |
|
| 331 |
+ name := c.Arg(3) |
|
| 332 |
+ if err != nil {
|
|
| 333 |
+ glog.Fatalf("Error parsing replicas: %v", err)
|
|
| 334 |
+ } |
|
| 335 |
+ err = kubecfg.RunController(image, name, replicas, client, c.PortSpec, c.ServicePort) |
|
| 336 |
+ case "resize": |
|
| 337 |
+ args := c.Args |
|
| 338 |
+ if len(args) < 3 {
|
|
| 339 |
+ glog.Fatal("usage: kubecfg resize <controller> <replicas>")
|
|
| 340 |
+ } |
|
| 341 |
+ name := args[1] |
|
| 342 |
+ replicas, err := strconv.Atoi(args[2]) |
|
| 343 |
+ if err != nil {
|
|
| 344 |
+ glog.Fatalf("Error parsing replicas: %v", err)
|
|
| 345 |
+ } |
|
| 346 |
+ err = kubecfg.ResizeController(name, replicas, client) |
|
| 347 |
+ default: |
|
| 348 |
+ return false |
|
| 349 |
+ } |
|
| 350 |
+ if err != nil {
|
|
| 351 |
+ glog.Fatalf("Error: %v", err)
|
|
| 352 |
+ } |
|
| 353 |
+ return true |
|
| 354 |
+} |
| 0 | 355 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,162 @@ |
| 0 |
+package master |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "net/http" |
|
| 5 |
+ "os" |
|
| 6 |
+ "path" |
|
| 7 |
+ "time" |
|
| 8 |
+ |
|
| 9 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" |
|
| 10 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/client" |
|
| 11 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/controller" |
|
| 12 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet" |
|
| 13 |
+ kconfig "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/config" |
|
| 14 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/master" |
|
| 15 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/proxy" |
|
| 16 |
+ pconfig "github.com/GoogleCloudPlatform/kubernetes/pkg/proxy/config" |
|
| 17 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/tools" |
|
| 18 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/util" |
|
| 19 |
+ etcdconfig "github.com/coreos/etcd/config" |
|
| 20 |
+ "github.com/coreos/etcd/etcd" |
|
| 21 |
+ etcdclient "github.com/coreos/go-etcd/etcd" |
|
| 22 |
+ "github.com/fsouza/go-dockerclient" |
|
| 23 |
+ "github.com/golang/glog" |
|
| 24 |
+ "github.com/google/cadvisor/client" |
|
| 25 |
+ "github.com/openshift/origin/pkg/api" |
|
| 26 |
+ "github.com/openshift/origin/pkg/service" |
|
| 27 |
+ "github.com/spf13/cobra" |
|
| 28 |
+) |
|
| 29 |
+ |
|
| 30 |
+func NewCommandStartAllInOne(name string) *cobra.Command {
|
|
| 31 |
+ return &cobra.Command{
|
|
| 32 |
+ Use: name, |
|
| 33 |
+ Short: "Launch in all-in-one mode", |
|
| 34 |
+ Run: func(c *cobra.Command, args []string) {
|
|
| 35 |
+ startAllInOne() |
|
| 36 |
+ }, |
|
| 37 |
+ } |
|
| 38 |
+} |
|
| 39 |
+ |
|
| 40 |
+func startAllInOne() {
|
|
| 41 |
+ dockerAddr := getDockerEndpoint("")
|
|
| 42 |
+ dockerClient, err := docker.NewClient(dockerAddr) |
|
| 43 |
+ if err != nil {
|
|
| 44 |
+ glog.Fatal("Couldn't connect to docker.")
|
|
| 45 |
+ } |
|
| 46 |
+ if err := dockerClient.Ping(); err != nil {
|
|
| 47 |
+ glog.Errorf("WARNING: Docker could not be reached at %s. Docker must be installed and running to start containers.\n%v", dockerAddr, err)
|
|
| 48 |
+ } |
|
| 49 |
+ |
|
| 50 |
+ cadvisorClient, err := cadvisor.NewClient("http://127.0.0.1:4194")
|
|
| 51 |
+ if err != nil {
|
|
| 52 |
+ glog.Errorf("Error on creating cadvisor client: %v", err)
|
|
| 53 |
+ } |
|
| 54 |
+ |
|
| 55 |
+ // initialize etcd |
|
| 56 |
+ etcdAddr := "127.0.0.1:4001" |
|
| 57 |
+ etcdServers := []string{} // default
|
|
| 58 |
+ etcdConfig := etcdconfig.New() |
|
| 59 |
+ etcdConfig.BindAddr = etcdAddr |
|
| 60 |
+ etcdServer := etcd.New(etcdConfig) |
|
| 61 |
+ go util.Forever(func() { etcdServer.Run() }, 0)
|
|
| 62 |
+ glog.Infof("Started etcd at http://%s", etcdAddr)
|
|
| 63 |
+ |
|
| 64 |
+ etcdClient := etcdclient.NewClient(etcdServers) |
|
| 65 |
+ for i := 0; ; i += 1 {
|
|
| 66 |
+ _, err := etcdClient.Get("/", false, false)
|
|
| 67 |
+ if err == nil || tools.IsEtcdNotFound(err) {
|
|
| 68 |
+ break |
|
| 69 |
+ } |
|
| 70 |
+ if i > 100 {
|
|
| 71 |
+ glog.Fatal("Could not reach etcd: %v", err)
|
|
| 72 |
+ } |
|
| 73 |
+ time.Sleep(50 * time.Millisecond) |
|
| 74 |
+ } |
|
| 75 |
+ |
|
| 76 |
+ // initialize Kubelet |
|
| 77 |
+ minionHost := "127.0.0.1" |
|
| 78 |
+ minionPort := 10250 |
|
| 79 |
+ rootDirectory := path.Clean("/var/lib/openshift")
|
|
| 80 |
+ os.MkdirAll(rootDirectory, 0750) |
|
| 81 |
+ cfg := kconfig.NewPodConfig(kconfig.PodConfigNotificationSnapshotAndUpdates) |
|
| 82 |
+ kconfig.NewSourceEtcd(kconfig.EtcdKeyForHost(minionHost), etcdClient, 30*time.Second, cfg.Channel("etcd"))
|
|
| 83 |
+ k := kubelet.NewMainKubelet( |
|
| 84 |
+ minionHost, |
|
| 85 |
+ dockerClient, |
|
| 86 |
+ cadvisorClient, |
|
| 87 |
+ etcdClient, |
|
| 88 |
+ rootDirectory) |
|
| 89 |
+ go util.Forever(func() { k.Run(cfg.Updates()) }, 0)
|
|
| 90 |
+ go util.Forever(func() {
|
|
| 91 |
+ kubelet.ListenAndServeKubeletServer(k, cfg.Channel("http"), http.DefaultServeMux, minionHost, uint(minionPort))
|
|
| 92 |
+ }, 0) |
|
| 93 |
+ |
|
| 94 |
+ // initialize OpenShift API |
|
| 95 |
+ storage := map[string]apiserver.RESTStorage{
|
|
| 96 |
+ "services": service.NewRESTStorage(service.MakeMemoryRegistry()), |
|
| 97 |
+ } |
|
| 98 |
+ osAddr := "127.0.0.1:8081" |
|
| 99 |
+ osPrefix := "/osapi/v1beta1" |
|
| 100 |
+ osApi := &http.Server{
|
|
| 101 |
+ Addr: osAddr, |
|
| 102 |
+ Handler: apiserver.New(storage, api.Codec, osPrefix), |
|
| 103 |
+ ReadTimeout: 10 * time.Second, |
|
| 104 |
+ WriteTimeout: 10 * time.Second, |
|
| 105 |
+ MaxHeaderBytes: 1 << 20, |
|
| 106 |
+ } |
|
| 107 |
+ go util.Forever(func() { glog.Fatal(osApi.ListenAndServe()) }, 0)
|
|
| 108 |
+ glog.Infof("Started OpenShift API at http://%s%s", osAddr, osPrefix)
|
|
| 109 |
+ |
|
| 110 |
+ // initialize Kubernetes API |
|
| 111 |
+ kubeAddr := "127.0.0.1:8080" |
|
| 112 |
+ kubePrefix := "/api/v1beta1" |
|
| 113 |
+ kubeClient := client.New(fmt.Sprintf("http://%s", kubeAddr), nil)
|
|
| 114 |
+ podInfoGetter := &client.HTTPPodInfoGetter{
|
|
| 115 |
+ Client: http.DefaultClient, |
|
| 116 |
+ Port: uint(minionPort), |
|
| 117 |
+ } |
|
| 118 |
+ masterConfig := &master.Config{
|
|
| 119 |
+ Client: kubeClient, |
|
| 120 |
+ EtcdServers: etcdServers, |
|
| 121 |
+ HealthCheckMinions: true, |
|
| 122 |
+ Minions: []string{minionHost},
|
|
| 123 |
+ PodInfoGetter: podInfoGetter, |
|
| 124 |
+ } |
|
| 125 |
+ m := master.New(masterConfig) |
|
| 126 |
+ go util.Forever(func() { m.Run(kubeAddr, kubePrefix) }, 0)
|
|
| 127 |
+ glog.Infof("Started Kubernetes API at http://%s%s", kubeAddr, kubePrefix)
|
|
| 128 |
+ |
|
| 129 |
+ // initialize kube proxy |
|
| 130 |
+ serviceConfig := pconfig.NewServiceConfig() |
|
| 131 |
+ endpointsConfig := pconfig.NewEndpointsConfig() |
|
| 132 |
+ pconfig.NewConfigSourceEtcd(etcdClient, |
|
| 133 |
+ serviceConfig.Channel("etcd"),
|
|
| 134 |
+ endpointsConfig.Channel("etcd"))
|
|
| 135 |
+ loadBalancer := proxy.NewLoadBalancerRR() |
|
| 136 |
+ proxier := proxy.NewProxier(loadBalancer) |
|
| 137 |
+ serviceConfig.RegisterHandler(proxier) |
|
| 138 |
+ endpointsConfig.RegisterHandler(loadBalancer) |
|
| 139 |
+ glog.Infof("Started Kubernetes Proxy")
|
|
| 140 |
+ |
|
| 141 |
+ // initialize replication manager |
|
| 142 |
+ controllerManager := controller.MakeReplicationManager(kubeClient) |
|
| 143 |
+ controllerManager.Run(10 * time.Second) |
|
| 144 |
+ glog.Infof("Started Kubernetes Replication Manager")
|
|
| 145 |
+ |
|
| 146 |
+ select {}
|
|
| 147 |
+} |
|
| 148 |
+ |
|
| 149 |
+func getDockerEndpoint(dockerEndpoint string) string {
|
|
| 150 |
+ var endpoint string |
|
| 151 |
+ if len(dockerEndpoint) > 0 {
|
|
| 152 |
+ endpoint = dockerEndpoint |
|
| 153 |
+ } else if len(os.Getenv("DOCKER_HOST")) > 0 {
|
|
| 154 |
+ endpoint = os.Getenv("DOCKER_HOST")
|
|
| 155 |
+ } else {
|
|
| 156 |
+ endpoint = "unix:///var/run/docker.sock" |
|
| 157 |
+ } |
|
| 158 |
+ glog.Infof("Connecting to docker on %s", endpoint)
|
|
| 159 |
+ |
|
| 160 |
+ return endpoint |
|
| 161 |
+} |
| 0 | 162 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,19 @@ |
| 0 |
+package service |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" |
|
| 4 |
+ "github.com/openshift/origin/pkg/api" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+// ServiceRegistry is an interface implemented by things that know how to store Service objects. |
|
| 8 |
+type ServiceRegistry interface {
|
|
| 9 |
+ // ListServices obtains a list of services that match selector. |
|
| 10 |
+ ListServices(selector labels.Selector) ([]api.Service, error) |
|
| 11 |
+ // Get a specific service |
|
| 12 |
+ GetService(serviceID string) (*api.Service, error) |
|
| 13 |
+ CreateService(service api.Service) error |
|
| 14 |
+ // Update an existing service |
|
| 15 |
+ UpdateService(service api.Service) error |
|
| 16 |
+ // Delete an existing service |
|
| 17 |
+ DeleteService(serviceID string) error |
|
| 18 |
+} |
| 0 | 19 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,51 @@ |
| 0 |
+package service |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" |
|
| 4 |
+ "github.com/openshift/origin/pkg/api" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+// Mainly used for testing. |
|
| 8 |
+type MemoryRegistry struct {
|
|
| 9 |
+ serviceData map[string]api.Service |
|
| 10 |
+} |
|
| 11 |
+ |
|
| 12 |
+func MakeMemoryRegistry() *MemoryRegistry {
|
|
| 13 |
+ return &MemoryRegistry{
|
|
| 14 |
+ serviceData: map[string]api.Service{},
|
|
| 15 |
+ } |
|
| 16 |
+} |
|
| 17 |
+ |
|
| 18 |
+func (registry *MemoryRegistry) ListServices(selector labels.Selector) ([]api.Service, error) {
|
|
| 19 |
+ result := []api.Service{}
|
|
| 20 |
+ for _, value := range registry.serviceData {
|
|
| 21 |
+ if selector.Matches(labels.Set(value.Labels)) {
|
|
| 22 |
+ result = append(result, value) |
|
| 23 |
+ } |
|
| 24 |
+ } |
|
| 25 |
+ return result, nil |
|
| 26 |
+} |
|
| 27 |
+ |
|
| 28 |
+func (registry *MemoryRegistry) GetService(serviceID string) (*api.Service, error) {
|
|
| 29 |
+ service, found := registry.serviceData[serviceID] |
|
| 30 |
+ if found {
|
|
| 31 |
+ return &service, nil |
|
| 32 |
+ } else {
|
|
| 33 |
+ return nil, nil |
|
| 34 |
+ } |
|
| 35 |
+} |
|
| 36 |
+ |
|
| 37 |
+func (registry *MemoryRegistry) CreateService(service api.Service) error {
|
|
| 38 |
+ registry.serviceData[service.ID] = service |
|
| 39 |
+ return nil |
|
| 40 |
+} |
|
| 41 |
+ |
|
| 42 |
+func (registry *MemoryRegistry) DeleteService(serviceID string) error {
|
|
| 43 |
+ delete(registry.serviceData, serviceID) |
|
| 44 |
+ return nil |
|
| 45 |
+} |
|
| 46 |
+ |
|
| 47 |
+func (registry *MemoryRegistry) UpdateService(service api.Service) error {
|
|
| 48 |
+ registry.serviceData[service.ID] = service |
|
| 49 |
+ return nil |
|
| 50 |
+} |
| 0 | 51 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,77 @@ |
| 0 |
+package service |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ |
|
| 5 |
+ baseapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
| 6 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" |
|
| 7 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" |
|
| 8 |
+ "github.com/openshift/origin/pkg/api" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+type ServiceRegistryStorage struct {
|
|
| 12 |
+ registry ServiceRegistry |
|
| 13 |
+} |
|
| 14 |
+ |
|
| 15 |
+func NewRESTStorage(registry ServiceRegistry) apiserver.RESTStorage {
|
|
| 16 |
+ return &ServiceRegistryStorage{registry}
|
|
| 17 |
+} |
|
| 18 |
+ |
|
| 19 |
+func (s *ServiceRegistryStorage) New() interface{} {
|
|
| 20 |
+ return &api.Service{}
|
|
| 21 |
+} |
|
| 22 |
+ |
|
| 23 |
+func (s *ServiceRegistryStorage) Get(id string) (interface{}, error) {
|
|
| 24 |
+ service, err := s.registry.GetService(id) |
|
| 25 |
+ if err != nil {
|
|
| 26 |
+ return service, err |
|
| 27 |
+ } |
|
| 28 |
+ if service == nil {
|
|
| 29 |
+ return service, nil |
|
| 30 |
+ } |
|
| 31 |
+ return service, err |
|
| 32 |
+} |
|
| 33 |
+ |
|
| 34 |
+func (s *ServiceRegistryStorage) List(selector labels.Selector) (interface{}, error) {
|
|
| 35 |
+ var result api.ServiceList |
|
| 36 |
+ services, err := s.registry.ListServices(selector) |
|
| 37 |
+ if err == nil {
|
|
| 38 |
+ result.Items = services |
|
| 39 |
+ } |
|
| 40 |
+ return result, err |
|
| 41 |
+} |
|
| 42 |
+ |
|
| 43 |
+func (s *ServiceRegistryStorage) Delete(id string) (<-chan interface{}, error) {
|
|
| 44 |
+ return apiserver.MakeAsync(func() (interface{}, error) {
|
|
| 45 |
+ return baseapi.Status{Status: baseapi.StatusSuccess}, s.registry.DeleteService(id)
|
|
| 46 |
+ }), nil |
|
| 47 |
+} |
|
| 48 |
+ |
|
| 49 |
+func (s *ServiceRegistryStorage) Create(obj interface{}) (<-chan interface{}, error) {
|
|
| 50 |
+ service := obj.(api.Service) |
|
| 51 |
+ if len(service.ID) == 0 {
|
|
| 52 |
+ return nil, fmt.Errorf("id is unspecified: %#v", service)
|
|
| 53 |
+ } |
|
| 54 |
+ |
|
| 55 |
+ return apiserver.MakeAsync(func() (interface{}, error) {
|
|
| 56 |
+ if err := s.registry.CreateService(service); err != nil {
|
|
| 57 |
+ return nil, err |
|
| 58 |
+ } |
|
| 59 |
+ return s.Get(service.ID) |
|
| 60 |
+ }), nil |
|
| 61 |
+} |
|
| 62 |
+ |
|
| 63 |
+func (s *ServiceRegistryStorage) Update(obj interface{}) (<-chan interface{}, error) {
|
|
| 64 |
+ service := obj.(api.Service) |
|
| 65 |
+ if len(service.ID) == 0 {
|
|
| 66 |
+ return nil, fmt.Errorf("id is unspecified: %#v", service)
|
|
| 67 |
+ } |
|
| 68 |
+ |
|
| 69 |
+ return apiserver.MakeAsync(func() (interface{}, error) {
|
|
| 70 |
+ err := s.registry.UpdateService(service) |
|
| 71 |
+ if err != nil {
|
|
| 72 |
+ return nil, err |
|
| 73 |
+ } |
|
| 74 |
+ return s.Get(service.ID) |
|
| 75 |
+ }), nil |
|
| 76 |
+} |
| 0 | 5 |
new file mode 100755 |
| ... | ... |
@@ -0,0 +1,11 @@ |
| 0 |
+TOP_PACKAGES=" |
|
| 1 |
+ github.com/GoogleCloudPlatform/kubernetes |
|
| 2 |
+ github.com/spf13/cobra |
|
| 3 |
+" |
|
| 4 |
+ |
|
| 5 |
+DEP_PACKAGES=" |
|
| 6 |
+ github.com/spf13/pflag |
|
| 7 |
+ github.com/coreos/go-etcd |
|
| 8 |
+" |
|
| 9 |
+ |
|
| 10 |
+PACKAGES="$TOP_PACKAGES $DEP_PACKAGES" |
|
| 0 | 11 |
\ No newline at end of file |
| 1 | 12 |
new file mode 100755 |
| ... | ... |
@@ -0,0 +1,87 @@ |
| 0 |
+#!/bin/bash |
|
| 1 |
+ |
|
| 2 |
+set -e |
|
| 3 |
+ |
|
| 4 |
+if ! git diff-index --quiet HEAD -- || test $(git ls-files --exclude-standard --others | wc -l) != 0; then |
|
| 5 |
+ echo "You can't have any staged files in git when updating packages." |
|
| 6 |
+ echo "Either commit them or unstage them to continue." |
|
| 7 |
+ exit 1 |
|
| 8 |
+fi |
|
| 9 |
+ |
|
| 10 |
+THIRD_PARTY_DIR=$(dirname $0) |
|
| 11 |
+cd $THIRD_PARTY_DIR |
|
| 12 |
+ |
|
| 13 |
+. ./deps.sh |
|
| 14 |
+ |
|
| 15 |
+if [ $# -gt 0 ]; then |
|
| 16 |
+ PACKAGES="$@" |
|
| 17 |
+fi |
|
| 18 |
+ |
|
| 19 |
+# Create a temp GOPATH root. It must be an absolute path |
|
| 20 |
+mkdir -p ../output/go_dep_update |
|
| 21 |
+cd ../output/go_dep_update |
|
| 22 |
+TMP_GO_ROOT=$PWD |
|
| 23 |
+cd - |
|
| 24 |
+export GOPATH=${TMP_GO_ROOT}
|
|
| 25 |
+ |
|
| 26 |
+for p in $PACKAGES; do |
|
| 27 |
+ echo "Fetching $p" |
|
| 28 |
+ |
|
| 29 |
+ # this is the target directory |
|
| 30 |
+ mkdir -p src/$p |
|
| 31 |
+ |
|
| 32 |
+ extra= |
|
| 33 |
+ if [[ "$FROM_GOPATH" != "" ]]; then |
|
| 34 |
+ extra=" (fork)" |
|
| 35 |
+ mkdir -p "${TMP_GO_ROOT}/src/$p"
|
|
| 36 |
+ frompath="${FROM_GOPATH}/src/$p/"
|
|
| 37 |
+ cd "${frompath}"
|
|
| 38 |
+ if ! git diff-index --quiet HEAD -- || test $(git ls-files --exclude-standard --others | wc -l) != 0; then |
|
| 39 |
+ echo "You can't have any staged files in ${frompath} git when updating packages."
|
|
| 40 |
+ echo "Either commit them or unstage them to continue." |
|
| 41 |
+ exit 1 |
|
| 42 |
+ fi |
|
| 43 |
+ cd - |
|
| 44 |
+ rsync -a -z -r "${frompath}" "${TMP_GO_ROOT}/src/$p"
|
|
| 45 |
+ else |
|
| 46 |
+ # This will checkout the project into src |
|
| 47 |
+ if [ $p == "github.com/GoogleCloudPlatform/kubernetes" ]; then |
|
| 48 |
+ git clone https://github.com/GoogleCloudPlatform/kubernetes.git $TMP_GO_ROOT/src/$p |
|
| 49 |
+ else |
|
| 50 |
+ go get -u -d $p |
|
| 51 |
+ fi |
|
| 52 |
+ fi |
|
| 53 |
+ |
|
| 54 |
+ if [ $p == "github.com/GoogleCloudPlatform/kubernetes" ]; then |
|
| 55 |
+ export GOPATH=${TMP_GO_ROOT}/src/github.com/GoogleCloudPlatform/kubernetes/third_party:${TMP_GO_ROOT}
|
|
| 56 |
+ fi |
|
| 57 |
+ |
|
| 58 |
+ # The go get path |
|
| 59 |
+ gp=$TMP_GO_ROOT/src/$p |
|
| 60 |
+ |
|
| 61 |
+ # Attempt to find the commit hash of the repo |
|
| 62 |
+ cd $gp |
|
| 63 |
+ |
|
| 64 |
+ HEAD= |
|
| 65 |
+ REL_PATH=$(git rev-parse --show-prefix 2>/dev/null) |
|
| 66 |
+ if [[ -z "$HEAD" && $REL_PATH != *target/go_dep_update* ]]; then |
|
| 67 |
+ # Grab the head if it is git |
|
| 68 |
+ HEAD=$(git rev-parse HEAD) |
|
| 69 |
+ fi |
|
| 70 |
+ |
|
| 71 |
+ # Grab the head if it is mercurial |
|
| 72 |
+ if [[ -z "$HEAD" ]] && hg root >/dev/null 2>&1; then |
|
| 73 |
+ HEAD=$(hg id -i) |
|
| 74 |
+ fi |
|
| 75 |
+ |
|
| 76 |
+ cd - |
|
| 77 |
+ |
|
| 78 |
+ # Copy the code into the final directory |
|
| 79 |
+ rsync -a -z -r --exclude '.git/' --exclude '.hg/' $TMP_GO_ROOT/src/$p/ src/$p |
|
| 80 |
+ |
|
| 81 |
+ # Make a nice commit about what everything bumped to |
|
| 82 |
+ git add src/$p |
|
| 83 |
+ if ! git diff --cached --exit-code > /dev/null 2>&1; then |
|
| 84 |
+ git commit -m "bump(${p}): ${HEAD}${extra}"
|
|
| 85 |
+ fi |
|
| 86 |
+done |