Expand on kube-discovery API and integrate container build.

This commit is contained in:
Devan Goodwin 2016-08-25 10:54:42 -03:00
parent e3278d965a
commit baebd7cfd9
14 changed files with 359 additions and 229 deletions

View File

@ -0,0 +1,18 @@
# 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

@ -0,0 +1,53 @@
# 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?=armel/debian:jessie
endif
ifeq ($(ARCH),arm64)
BASEIMAGE?=aarch64/debian:jessie
endif
ifeq ($(ARCH),ppc64le)
BASEIMAGE?=ppc64le/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 -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

@ -0,0 +1,45 @@
### 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

@ -1,19 +1,22 @@
/* /*
Copyright 2016 The Kubernetes Authors. Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package kubediscovery
package discovery
import ( import (
"encoding/base64"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@ -45,13 +48,13 @@ type fsCALoader struct {
} }
func (cl *fsCALoader) LoadPEM() (string, error) { func (cl *fsCALoader) LoadPEM() (string, error) {
if cl.certData != "" { if cl.certData == "" {
data, err := ioutil.ReadFile(CAPath) data, err := ioutil.ReadFile(CAPath)
if err != nil { if err != nil {
return "", err return "", err
} }
cl.certData = base64.StdEncoding.EncodeToString(data) cl.certData = string(data)
} }
return cl.certData, nil return cl.certData, nil
@ -92,11 +95,11 @@ type endpointsLoader interface {
LoadList() ([]string, error) LoadList() ([]string, error)
} }
type jsonFileEnpointsLoader struct { type jsonFileEndpointsLoader struct {
endpoints []string endpoints []string
} }
func (el *jsonFileEnpointsLoader) LoadList() ([]string, error) { func (el *jsonFileEndpointsLoader) LoadList() ([]string, error) {
if len(el.endpoints) == 0 { if len(el.endpoints) == 0 {
data, err := ioutil.ReadFile(EndpointListPath) data, err := ioutil.ReadFile(EndpointListPath)
if err != nil { if err != nil {
@ -121,7 +124,7 @@ func NewClusterInfoHandler() *ClusterInfoHandler {
return &ClusterInfoHandler{ return &ClusterInfoHandler{
tokenLoader: &jsonFileTokenLoader{}, tokenLoader: &jsonFileTokenLoader{},
caLoader: &fsCALoader{}, caLoader: &fsCALoader{},
endpointsLoader: &jsonFileEnpointsLoader{}, endpointsLoader: &jsonFileEndpointsLoader{},
} }
} }
@ -130,7 +133,7 @@ func (cih *ClusterInfoHandler) ServeHTTP(resp http.ResponseWriter, req *http.Req
log.Printf("Got token ID: %s", tokenID) log.Printf("Got token ID: %s", tokenID)
token, err := cih.tokenLoader.LoadAndLookup(tokenID) token, err := cih.tokenLoader.LoadAndLookup(tokenID)
if err != nil { if err != nil {
log.Printf("Invalid token: %s", err) log.Print(err)
http.Error(resp, "Forbidden", http.StatusForbidden) http.Error(resp, "Forbidden", http.StatusForbidden)
return return
} }
@ -138,6 +141,7 @@ func (cih *ClusterInfoHandler) ServeHTTP(resp http.ResponseWriter, req *http.Req
// TODO probably should not leak server-side errors to the client // TODO probably should not leak server-side errors to the client
caPEM, err := cih.caLoader.LoadPEM() caPEM, err := cih.caLoader.LoadPEM()
log.Printf("Loaded CA: %s", caPEM)
if err != nil { if err != nil {
err = fmt.Errorf("Error loading root CA certificate data: %s", err) err = fmt.Errorf("Error loading root CA certificate data: %s", err)
log.Println(err) log.Println(err)
@ -159,7 +163,10 @@ func (cih *ClusterInfoHandler) ServeHTTP(resp http.ResponseWriter, req *http.Req
} }
// Instantiate an signer using HMAC-SHA256. // Instantiate an signer using HMAC-SHA256.
signer, err := jose.NewSigner(jose.HS256, []byte(token)) hmacKey := []byte(token)
log.Printf("Key is %d bytes long", len(hmacKey))
signer, err := jose.NewSigner(jose.HS256, hmacKey)
if err != nil { if err != nil {
err = fmt.Errorf("Error creating JWS signer: %s", err) err = fmt.Errorf("Error creating JWS signer: %s", err)
log.Println(err) log.Println(err)

View File

@ -0,0 +1,208 @@
/*
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,16 +1,20 @@
/* /*
Copyright 2016 The Kubernetes Authors. Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package kubediscovery
package discovery
type ClusterInfo struct { type ClusterInfo struct {
// TODO Kind, apiVersion // TODO Kind, apiVersion

View File

@ -1,16 +1,20 @@
/* /*
Copyright 2016 The Kubernetes Authors. Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package kubediscovery
package discovery
import ( import (
"net/http" "net/http"

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 main
import (
_ "github.com/square/go-jose"
)
func main() {
}

View File

@ -1,15 +1,19 @@
/* /*
Copyright 2016 The Kubernetes Authors. Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package main package main
import ( import (
@ -17,7 +21,7 @@ import (
"net/http" "net/http"
"os" "os"
kd "k8s.io/kubernetes/pkg/kubediscovery" kd "k8s.io/kubernetes/cmd/kube-discovery/app"
) )
func main() { func main() {

View File

@ -1,6 +0,0 @@
FROM golang
ADD kubediscovery /usr/bin/
ENTRYPOINT /usr/bin/kubernetes-discovery
EXPOSE 8080

View File

@ -1,39 +0,0 @@
# kubernetes-discovery
An initial implementation of a Kubernetes discovery service using JSON Web Signatures.
This prototype is expected to be run by Kubernetes itself for the time being,
and will hopefully be merged into the core API at a later time.
## Requirements
Generate a CA cert save it to: /tmp/secret/ca.pem to run the service or unit tests. (will not be required for unit tests for long) Similarly when run within kubernetes we expect a secret to be provided at this location as well. (see below)
## Build And Run From Source
```
$ make WHAT=cmd/kubediscovery
$ _output/local/bin/linux/amd64/kubediscovery
2016/08/23 19:17:28 Listening for requests on port 9898.
```
## Running in Docker
This image is published temporarily on Docker Hub as dgoodwin/kubediscovery
`docker run --rm -p 9898:9898 -v /tmp/secret/ca.pem:/tmp/secret/ca.pem --name kubediscovery dgoodwin/kubediscovery`
## Running in Kubernetes
A dummy certificate is included in ca-secret.yaml.
```
kubectl create -f ca-secret.yaml
kubectl create -f kubediscovery.yaml
```
## 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.

View File

@ -1,7 +0,0 @@
apiVersion: v1
kind: Secret
metadata:
name: ca-secret
type: Opaque
data:
ca.pem: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUR4RENDQXF5Z0F3SUJBZ0lVV3pqUDl5RUk0eHlRSnBzVHVERU4yV2ROaUFzd0RRWUpLb1pJaHZjTkFRRUwKQlFBd2FERUxNQWtHQTFVRUJoTUNWVk14RHpBTkJnTlZCQWdUQms5eVpXZHZiakVSTUE4R0ExVUVCeE1JVUc5eQpkR3hoYm1ReEV6QVJCZ05WQkFvVENrdDFZbVZ5Ym1WMFpYTXhDekFKQmdOVkJBc1RBa05CTVJNd0VRWURWUVFECkV3cExkV0psY201bGRHVnpNQjRYRFRFMk1EZ3hNVEUyTkRnd01Gb1hEVEl4TURneE1ERTJORGd3TUZvd2FERUwKTUFrR0ExVUVCaE1DVlZNeER6QU5CZ05WQkFnVEJrOXlaV2R2YmpFUk1BOEdBMVVFQnhNSVVHOXlkR3hoYm1ReApFekFSQmdOVkJBb1RDa3QxWW1WeWJtVjBaWE14Q3pBSkJnTlZCQXNUQWtOQk1STXdFUVlEVlFRREV3cExkV0psCmNtNWxkR1Z6TUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF3QkhNOGN6anc0Q1cKK05wbklhV012RzZlcVhtelNZT20vbHdaNUhOMnVLck9xaTNHYUUyTjFKd2tzcGRmMXNOUGFZMHdPR2xkbURIZgoxSnlyTW8rUFdLVUVjWko1WGE4Vm02d2I0MlpjczN3MEp5dlEzWFJjaDQyMFJRWGRKayszcmMybWRvSVRkL0lmCnZjWms0N0RzQTMrQU5QSUlSTzdWRmZpS1JNRFpTUDR1OThnVjI2eW1zbjc0TzFVKzNVUHR1TEFTVTFLck9FTk4KR01FWG0ydTJpdmVvbTJrbjFlZTZuM1hCR1o2bU52cUNPdWUxRXdza0gvWkhoUVh1UDgyV1U5dVk0aGVORnoyQwpBNmR0Q0Q0c3Z6eHc3ZFQ2cVhsV0ZIWUYrc3VLVDhXNkczd3NkOWxzV0ZVY0ZWL0lwaTVobEVaTWprNFNoY3RqCjdpYnlrRURKM1FJREFRQUJvMll3WkRBT0JnTlZIUThCQWY4RUJBTUNBUVl3RWdZRFZSMFRBUUgvQkFnd0JnRUIKL3dJQkFqQWRCZ05WSFE0RUZnUVVOdnhRZ3o5ZTNXS2VscU1KTmZXNE1KUHYzc0V3SHdZRFZSMGpCQmd3Rm9BVQpOdnhRZ3o5ZTNXS2VscU1KTmZXNE1KUHYzc0V3RFFZSktvWklodmNOQVFFTEJRQURnZ0VCQUp1TUhYUms1TEVyCmxET1p4Mm9aRUNQZ29reXMzSGJsM05oempXd2pncXdxNVN6a011V3QrUnVkdnRTK0FUQjFtTjRjYTN0eSt2bWcKT09heTkvaDZoditmSE5jZHpYdWR5dFZYZW1KN3F4ZFoxd25DUUcwdnpqOWRZY0xFSGpJWi94dU1jNlY3dnJ4YwpSc0preGp5aE01UXBmRHd0eVZKeGpkUmVBZ0huSyswTkNieHdtQ3cyRGIvOXpudm9LWGk4TEQwbkQzOFQxY3R3CmhmdGxwTmRoZXFNRlpEZXBuTUYwY2g2cHo5TFV5Mkh1cnhrV2dkWVNjY2VNU0hPTzBMcG4xeVVBMWczOTJhUjUKWk81Zm5KMW95Vm1LVWFCeDJCMndsSVlUSXlES1ZiMnY1UXNHbnYvRHVTMDZhcmVLTmsvTGpHRTRlMXlHOHJkcwpacnZHMzNvUmtEbz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=

View File

@ -1,37 +0,0 @@
apiVersion: extensions/v1beta1
kind: ReplicaSet
metadata:
name: kubediscovery
# these labels can be applied automatically
# from the labels in the pod template if not set
# labels:
# app: guestbook
# tier: frontend
spec:
# this replicas value is default
# modify it according to your case
replicas: 1
# selector can be applied automatically
# from the labels in the pod template if not set,
# but we are specifying the selector here to
# demonstrate its usage.
template:
metadata:
labels:
app: kubediscovery
spec:
hostNetwork: true
containers:
- name: kubediscovery
image: dgoodwin/kubediscovery
imagePullPolicy: IfNotPresent
ports:
- containerPort: 9898
volumeMounts:
- name: ca-secret-vol
mountPath: /tmp/secret
readOnly: true
volumes:
- name: ca-secret-vol
secret:
secretName: ca-secret

View File

@ -1,100 +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.
*/
package kubediscovery
import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"github.com/square/go-jose"
)
func TestClusterInfoIndex(t *testing.T) {
tests := map[string]struct {
url string
expStatus int
}{
"no token": {
"/cluster-info/v1/",
http.StatusForbidden,
},
"valid token": {
fmt.Sprintf("/cluster-info/v1/?token-id=%s", tempTokenId),
http.StatusOK,
},
"invalid token": {
"/cluster-info/v1/?token-id=JUNK",
http.StatusForbidden,
},
}
for name, test := range tests {
t.Logf("Running test: %s", name)
// 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.
req, err := http.NewRequest("GET", test.url, nil)
if err != nil {
t.Fatal(err)
}
rr := httptest.NewRecorder()
// TODO: mock/stub here
handler := NewClusterInfoHandler()
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 := fromHexBytes(tempToken)
clusterInfoBytes, err = jws.Verify(hmacTestKey)
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 ci.RootCertificates == "" {
t.Error("No root certificates in response")
continue
}
}
}
}