Remove the kube-discovery binary from the tree

pull/6/head
Lucas Käldström 2017-02-24 21:23:26 +02:00
parent 6edd079024
commit c4e989f51c
No known key found for this signature in database
GPG Key ID: 3FA3783D77751514
15 changed files with 0 additions and 747 deletions

View File

@ -71,7 +71,6 @@ SERVER_TARGETS = [
"//cmd/hyperkube",
"//cmd/kube-apiserver",
"//cmd/kube-controller-manager",
"//cmd/kube-discovery",
"//cmd/kubeadm",
"//plugin/cmd/kube-scheduler",
]

View File

@ -1,18 +0,0 @@
# Copyright 2016 The Kubernetes Authors.
#
# 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.
FROM BASEIMAGE
COPY kube-discovery /usr/local/bin
ENTRYPOINT "/usr/local/bin/kube-discovery"

View File

@ -1,57 +0,0 @@
# Copyright 2016 The Kubernetes Authors.
#
# 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.
# Build the kube-discovery image.
#
# Requires a pre-built kube-discovery binary:
# build/run.sh /bin/bash -c "KUBE_BUILD_PLATFORMS=linux/ARCH make WHAT=cmd/kube-discovery"
#
# Usage:
# [ARCH=amd64] [REGISTRY="gcr.io/google_containers"] make (build|push) VERSION={some_released_version_of_kubernetes}
REGISTRY?=gcr.io/google_containers
ARCH?=amd64
TEMP_DIR:=$(shell mktemp -d)
VERSION?=1.0
ifeq ($(ARCH),amd64)
BASEIMAGE?=debian:jessie
endif
ifeq ($(ARCH),arm)
BASEIMAGE?=armhf/debian:jessie
endif
ifeq ($(ARCH),arm64)
BASEIMAGE?=aarch64/debian:jessie
endif
ifeq ($(ARCH),ppc64le)
BASEIMAGE?=ppc64le/debian:jessie
endif
ifeq ($(ARCH),s390x)
BASEIMAGE?=s390x/debian:jessie
endif
all: build
build:
cp -r ./* ${TEMP_DIR}
cp ../../../_output/dockerized/bin/linux/${ARCH}/kube-discovery ${TEMP_DIR}
cd ${TEMP_DIR} && sed -i.back "s|BASEIMAGE|${BASEIMAGE}|g" Dockerfile
docker build --pull -t ${REGISTRY}/kube-discovery-${ARCH}:${VERSION} ${TEMP_DIR}
rm -rf "${TEMP_DIR}"
push: build
gcloud docker -- push ${REGISTRY}/kube-discovery-${ARCH}:${VERSION}
.PHONY: all

View File

@ -1,45 +0,0 @@
### kube-discovery
An initial implementation of a Kubernetes discovery service using JSON Web Signatures.
This prototype is configured by kubeadm and run within Kubernetes itself.
## Requirements
This pod expects the cluster CA, endpoints list, and token map to exist in /tmp/secret. This allows us to pass them in as kubernetes secrets when deployed as a pod.
```
$ cd /tmp/secret
$ ls
ca.pem endpoint-list.json token-map.json
$ cat endpoint-list.json
["http://192.168.1.5:8080", "http://192.168.1.6:8080"]
$ cat token-map.json
{
"TOKENID": "ABCDEF1234123456"
}
```
## Build And Run From Source
```
$ build/run.sh /bin/bash -c "KUBE_BUILD_PLATFORMS=linux/amd64 make WHAT=cmd/kube-discovery"
$ _output/dockerized/bin/linux/amd64/kube-discovery
2016/08/23 19:17:28 Listening for requests on port 9898.
```
## Running in Docker
This image is published at: gcr.io/google_containers/kube-discovery
`docker run -d -p 9898:9898 -v /tmp/secret/ca.pem:/tmp/secret/ca.pem -v /tmp/secret/endpoint-list.json:/tmp/secret/endpoint-list.json -v /tmp/secret/token-map.json:/tmp/secret/token-map.json --name kubediscovery gcr.io/google_containers/kube-discovery`
## Testing the API
`curl "http://localhost:9898/cluster-info/v1/?token-id=TOKENID"`
You should see JSON containing a signed payload. For code to verify and decode that payload see handler_test.go.
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/cluster/images/kube-discovery/README.md?pixel)]()

View File

@ -25,7 +25,6 @@ filegroup(
"//cmd/hyperkube:all-srcs",
"//cmd/kube-apiserver:all-srcs",
"//cmd/kube-controller-manager:all-srcs",
"//cmd/kube-discovery:all-srcs",
"//cmd/kube-proxy:all-srcs",
"//cmd/kubeadm:all-srcs",
"//cmd/kubectl:all-srcs",

View File

@ -1,38 +0,0 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
)
go_binary(
name = "kube-discovery",
library = ":go_default_library",
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = ["kubediscovery.go"],
tags = ["automanaged"],
deps = ["//cmd/kube-discovery/app:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//cmd/kube-discovery/app:all-srcs",
],
tags = ["automanaged"],
)

View File

@ -1,44 +0,0 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"handlers.go",
"model.go",
"routes.go",
],
tags = ["automanaged"],
deps = [
"//vendor:github.com/gorilla/mux",
"//vendor:github.com/square/go-jose",
],
)
go_test(
name = "go_default_test",
srcs = ["handlers_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = ["//vendor:github.com/square/go-jose"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View File

@ -1,203 +0,0 @@
/*
Copyright 2014 The Kubernetes Authors.
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 discovery
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"log"
"net/http"
"github.com/square/go-jose"
)
const secretPath = "/tmp/secret"
// CAPath is the expected location of our cluster's CA to be distributed to
// clients looking to connect. Because we expect to use kubernetes secrets
// for the time being, this file is expected to be a base64 encoded version
// of the normal cert PEM.
const CAPath = secretPath + "/ca.pem"
// caLoader is an interface for abstracting how we load the CA certificates
// for the cluster.
type caLoader interface {
LoadPEM() (string, error)
}
// fsCALoader is a caLoader for loading the PEM encoded CA from
// /tmp/secret/ca.pem.
type fsCALoader struct {
certData string
}
func (cl *fsCALoader) LoadPEM() (string, error) {
if cl.certData == "" {
data, err := ioutil.ReadFile(CAPath)
if err != nil {
return "", err
}
cl.certData = string(data)
}
return cl.certData, nil
}
const TokenMapPath = secretPath + "/token-map.json"
const EndpointListPath = secretPath + "/endpoint-list.json"
// tokenLoader is an interface for abstracting how we validate
// token IDs and lookup their corresponding token.
type tokenLoader interface {
// Lookup returns the token for a given token ID, or an error if the token ID
// does not exist. Both token and it's ID are expected be strings.
LoadAndLookup(tokenID string) (string, error)
}
type jsonFileTokenLoader struct {
tokenMap map[string]string
}
func (tl *jsonFileTokenLoader) LoadAndLookup(tokenID string) (string, error) {
if len(tl.tokenMap) == 0 {
data, err := ioutil.ReadFile(TokenMapPath)
if err != nil {
return "", err
}
if err := json.Unmarshal(data, &tl.tokenMap); err != nil {
return "", err
}
}
if val, ok := tl.tokenMap[tokenID]; ok {
return val, nil
}
return "", errors.New(fmt.Sprintf("invalid token: %s", tokenID))
}
type endpointsLoader interface {
LoadList() ([]string, error)
}
type jsonFileEndpointsLoader struct {
endpoints []string
}
func (el *jsonFileEndpointsLoader) LoadList() ([]string, error) {
if len(el.endpoints) == 0 {
data, err := ioutil.ReadFile(EndpointListPath)
if err != nil {
return nil, err
}
if err := json.Unmarshal(data, &el.endpoints); err != nil {
return nil, err
}
}
return el.endpoints, nil
}
// ClusterInfoHandler implements the http.ServeHTTP method and allows us to
// mock out portions of the request handler in tests.
type ClusterInfoHandler struct {
tokenLoader tokenLoader
caLoader caLoader
endpointsLoader endpointsLoader
}
func NewClusterInfoHandler() *ClusterInfoHandler {
return &ClusterInfoHandler{
tokenLoader: &jsonFileTokenLoader{},
caLoader: &fsCALoader{},
endpointsLoader: &jsonFileEndpointsLoader{},
}
}
func (cih *ClusterInfoHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
tokenID := req.FormValue("token-id")
log.Printf("Got token ID: %s", tokenID)
token, err := cih.tokenLoader.LoadAndLookup(tokenID)
if err != nil {
log.Print(err)
http.Error(resp, "Forbidden", http.StatusForbidden)
return
}
log.Printf("Loaded token: %s", token)
// TODO probably should not leak server-side errors to the client
caPEM, err := cih.caLoader.LoadPEM()
if err != nil {
err = fmt.Errorf("Error loading root CA certificate data: %s", err)
log.Println(err)
http.Error(resp, err.Error(), http.StatusInternalServerError)
return
}
log.Printf("Loaded CA: %s", caPEM)
endpoints, err := cih.endpointsLoader.LoadList()
if err != nil {
err = fmt.Errorf("Error loading list of API endpoints: %s", err)
log.Println(err)
http.Error(resp, err.Error(), http.StatusInternalServerError)
return
}
clusterInfo := ClusterInfo{
CertificateAuthorities: []string{caPEM},
Endpoints: endpoints,
}
// Instantiate an signer using HMAC-SHA256.
hmacKey := []byte(token)
log.Printf("Key is %d bytes long", len(hmacKey))
signer, err := jose.NewSigner(jose.HS256, hmacKey)
if err != nil {
err = fmt.Errorf("Error creating JWS signer: %s", err)
log.Println(err)
http.Error(resp, err.Error(), http.StatusInternalServerError)
return
}
payload, err := json.Marshal(clusterInfo)
if err != nil {
err = fmt.Errorf("Error serializing clusterInfo to JSON: %s", err)
log.Println(err)
http.Error(resp, err.Error(), http.StatusInternalServerError)
return
}
// Sign a sample payload. Calling the signer returns a protected JWS object,
// which can then be serialized for output afterwards. An error would
// indicate a problem in an underlying cryptographic primitive.
jws, err := signer.Sign(payload)
if err != nil {
err = fmt.Errorf("Error signing clusterInfo with JWS: %s", err)
log.Println(err)
http.Error(resp, err.Error(), http.StatusInternalServerError)
return
}
// Serialize the encrypted object using the full serialization format.
// Alternatively you can also use the compact format here by calling
// object.CompactSerialize() instead.
serialized := jws.FullSerialize()
resp.Write([]byte(serialized))
}

View File

@ -1,208 +0,0 @@
/*
Copyright 2014 The Kubernetes Authors.
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 discovery
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/square/go-jose"
)
type mockTokenLoader struct {
tokenID string
token string
}
func (tl *mockTokenLoader) LoadAndLookup(tokenID string) (string, error) {
if tokenID == tl.tokenID {
return tl.token, nil
}
return "", errors.New(fmt.Sprintf("invalid token: %s", tokenID))
}
const mockEndpoint1 = "https://192.168.1.5:8080"
const mockEndpoint2 = "https://192.168.1.6:8080"
type mockEndpointsLoader struct {
}
func (el *mockEndpointsLoader) LoadList() ([]string, error) {
return []string{mockEndpoint1, mockEndpoint2}, nil
}
const mockCA = "---BEGIN------END---DUMMYDATA"
type mockCALoader struct {
}
func (cl *mockCALoader) LoadPEM() (string, error) {
return mockCA, nil
}
const mockTokenID = "AAAAAA"
const mockToken = "9537434E638E4378"
const mockTokenIDCustom = "SHAREDSECRET"
const mockTokenCustom = "VERYSECRETTOKEN"
func TestClusterInfoIndex(t *testing.T) {
longToken := strings.Repeat("a", 1000)
tests := map[string]struct {
tokenID string // token ID the mock loader will use
token string // token the mock loader will use
reqTokenID string // token ID the will request with
reqToken string // token the caller will validate response with
expStatus int
expVerifyFailure bool
}{
"no token": {
tokenID: mockTokenID,
token: mockToken,
reqTokenID: "",
reqToken: "",
expStatus: http.StatusForbidden,
},
"valid token ID": {
tokenID: mockTokenID,
token: mockToken,
reqTokenID: mockTokenID,
reqToken: mockToken,
expStatus: http.StatusOK,
},
"valid arbitrary string token": {
tokenID: mockTokenIDCustom,
token: mockTokenCustom,
reqTokenID: mockTokenIDCustom,
reqToken: mockTokenCustom,
expStatus: http.StatusOK,
},
"valid arbitrary long string token": {
tokenID: "LONGTOKENTEST",
token: longToken,
reqTokenID: "LONGTOKENTEST",
reqToken: longToken,
expStatus: http.StatusOK,
},
"invalid token ID": {
tokenID: mockTokenID,
token: mockToken,
reqTokenID: "BADTOKENID",
reqToken: mockToken,
expStatus: http.StatusForbidden,
},
"invalid token": {
tokenID: mockTokenID,
token: mockToken,
reqTokenID: mockTokenID,
reqToken: "badtoken",
expStatus: http.StatusOK,
expVerifyFailure: true,
},
}
for name, test := range tests {
t.Logf("Running test: %s", name)
tokenLoader := &mockTokenLoader{test.tokenID, test.token}
// Create a request to pass to our handler. We don't have any query parameters for now, so we'll
// pass 'nil' as the third parameter.
url := "/cluster-info/v1/"
if test.tokenID != "" {
url = fmt.Sprintf("%s?token-id=%s", url, test.reqTokenID)
}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
t.Fatal(err)
}
rr := httptest.NewRecorder()
handler := &ClusterInfoHandler{
tokenLoader: tokenLoader,
caLoader: &mockCALoader{},
endpointsLoader: &mockEndpointsLoader{},
}
handler.ServeHTTP(rr, req)
if status := rr.Code; status != test.expStatus {
t.Errorf("handler returned wrong status code: got %v want %v",
status, test.expStatus)
continue
}
// If we were expecting valid status validate the body:
if test.expStatus == http.StatusOK {
var ci ClusterInfo
body := string(rr.Body.Bytes())
// Parse the JSON web signature:
jws, err := jose.ParseSigned(body)
if err != nil {
t.Errorf("Error parsing JWS from request body: %s", err)
continue
}
// Now we can verify the signature on the payload. An error here would
// indicate the the message failed to verify, e.g. because the signature was
// broken or the message was tampered with.
var clusterInfoBytes []byte
hmacTestKey := []byte(test.reqToken)
clusterInfoBytes, err = jws.Verify(hmacTestKey)
if test.expVerifyFailure {
if err == nil {
t.Errorf("Signature verification did not fail as expected.")
}
// We are done the test here either way.
continue
}
if err != nil {
t.Errorf("Error verifing signature: %s", err)
continue
}
err = json.Unmarshal(clusterInfoBytes, &ci)
if err != nil {
t.Errorf("Unable to unmarshall payload to JSON: error=%s body=%s", err, rr.Body.String())
continue
}
if len(ci.Endpoints) != 2 {
t.Errorf("Expected 2 endpoints, got: %d", len(ci.Endpoints))
}
if mockEndpoint1 != ci.Endpoints[0] {
t.Errorf("Unexpected endpoint: %s", ci.Endpoints[0])
}
if mockEndpoint2 != ci.Endpoints[1] {
t.Errorf("Unexpected endpoint: %s", ci.Endpoints[1])
}
if len(ci.CertificateAuthorities) != 1 {
t.Errorf("Expected 1 root certificate, got: %d", len(ci.CertificateAuthorities))
}
if ci.CertificateAuthorities[0] != mockCA {
t.Errorf("Expected CA: %s, got: %s", mockCA, ci.CertificateAuthorities[0])
}
}
}
}

View File

@ -1,24 +0,0 @@
/*
Copyright 2014 The Kubernetes Authors.
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 discovery
type ClusterInfo struct {
// TODO Kind, apiVersion
// TODO clusterId, fetchedTime, expiredTime
CertificateAuthorities []string `json:"certificateAuthorities,omitempty"`
Endpoints []string `json:"endpoints,omitempty"`
}

View File

@ -1,55 +0,0 @@
/*
Copyright 2014 The Kubernetes Authors.
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 discovery
import (
"net/http"
"github.com/gorilla/mux"
)
type Route struct {
Name string
Method string
Pattern string
Handler http.Handler
}
type Routes []Route
var routes = Routes{
Route{
"ClusterInfoIndex",
"GET",
"/cluster-info/v1/",
NewClusterInfoHandler(),
},
}
func NewRouter() *mux.Router {
router := mux.NewRouter().StrictSlash(true)
for _, route := range routes {
router.
Methods(route.Method).
Path(route.Pattern).
Name(route.Name).
Handler(route.Handler)
}
return router
}

View File

@ -1,49 +0,0 @@
/*
Copyright 2014 The Kubernetes Authors.
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 main
import (
"log"
"net/http"
"os"
kd "k8s.io/kubernetes/cmd/kube-discovery/app"
)
func main() {
// Make sure we can load critical files, and be nice to the user by
// printing descriptive error message when we fail.
for desc, path := range map[string]string{
"root CA certificate": kd.CAPath,
"token map file": kd.TokenMapPath,
"list of API endpoints": kd.EndpointListPath,
} {
if _, err := os.Stat(path); os.IsNotExist(err) {
log.Fatalf("%s does not exist: %s", desc, path)
}
// Test read permissions
file, err := os.Open(path)
if err != nil {
log.Fatalf("Unable to open %s (%q [%s])", desc, path, err)
}
file.Close()
}
router := kd.NewRouter()
log.Printf("Listening for requests on port 9898.")
log.Fatal(http.ListenAndServe(":9898", router))
}

View File

@ -13,7 +13,6 @@ cmd/kube-apiserver/app
cmd/kube-apiserver/app/options
cmd/kube-controller-manager
cmd/kube-controller-manager/app/options
cmd/kube-discovery
cmd/kube-proxy
cmd/kubeadm
cmd/kubeadm/app/apis/kubeadm/install

View File

@ -31,7 +31,6 @@ kube::golang::server_targets() {
cmd/kubelet
cmd/kubeadm
cmd/hyperkube
cmd/kube-discovery
vendor/k8s.io/kube-aggregator
plugin/cmd/kube-scheduler
)
@ -188,7 +187,6 @@ readonly KUBE_STATIC_LIBRARIES=(
kube-controller-manager
kube-scheduler
kube-proxy
kube-discovery
kube-aggregator
kubeadm
kubectl

View File

@ -569,7 +569,6 @@ k8s.io/kubernetes/cmd/genutils,rmmh,1,
k8s.io/kubernetes/cmd/hyperkube,jbeda,0,
k8s.io/kubernetes/cmd/kube-apiserver/app/options,nikhiljindal,0,
k8s.io/kubernetes/cmd/kube-controller-manager/app,dchen1107,1,
k8s.io/kubernetes/cmd/kube-discovery/app,pmorie,1,
k8s.io/kubernetes/cmd/kube-proxy/app,luxas,1,
k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/install,ixdy,1,
k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation,caesarxuchao,1,

1 name owner auto-assigned sig
569 k8s.io/kubernetes/cmd/hyperkube jbeda 0
570 k8s.io/kubernetes/cmd/kube-apiserver/app/options nikhiljindal 0
571 k8s.io/kubernetes/cmd/kube-controller-manager/app dchen1107 1
k8s.io/kubernetes/cmd/kube-discovery/app pmorie 1
572 k8s.io/kubernetes/cmd/kube-proxy/app luxas 1
573 k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/install ixdy 1
574 k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation caesarxuchao 1