mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-13 13:55:41 +00:00
Add standalone kube-discovery JWS discovery API.
This is a standalone pod which will be configured by kubeadm for the time being. A token ID/token map, endpoints list, and CA cert are provided as secrets. Callers request the cluster info by shared secret (token ID), and if the token ID matches a JWS signed payload is returned using the other half of the shared secret to validate.
This commit is contained in:
parent
b841a8bad3
commit
d17a236af3
41
cmd/kubediscovery/kubediscovery.go
Normal file
41
cmd/kubediscovery/kubediscovery.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
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 main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
kd "k8s.io/kubernetes/pkg/kubediscovery"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
// Make sure the CA cert for the cluster exists and is readable.
|
||||||
|
// We are expecting a base64 encoded version of the cert PEM as this is how
|
||||||
|
// the cert would most likely be provided via kubernetes secrets.
|
||||||
|
if _, err := os.Stat(kd.CAPath); os.IsNotExist(err) {
|
||||||
|
log.Fatalf("CA does not exist: %s", kd.CAPath)
|
||||||
|
}
|
||||||
|
// Test read permissions
|
||||||
|
file, err := os.Open(kd.CAPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("ERROR: Unable to read %s", kd.CAPath)
|
||||||
|
}
|
||||||
|
file.Close()
|
||||||
|
|
||||||
|
router := kd.NewRouter()
|
||||||
|
log.Printf("Listening for requests on port 9898.")
|
||||||
|
log.Fatal(http.ListenAndServe(":9898", router))
|
||||||
|
}
|
6
discovery/Dockerfile
Normal file
6
discovery/Dockerfile
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
FROM golang
|
||||||
|
|
||||||
|
ADD kubediscovery /usr/bin/
|
||||||
|
ENTRYPOINT /usr/bin/kubernetes-discovery
|
||||||
|
|
||||||
|
EXPOSE 8080
|
39
discovery/README.md
Normal file
39
discovery/README.md
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# 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.
|
7
discovery/ca-secret.yaml
Normal file
7
discovery/ca-secret.yaml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: ca-secret
|
||||||
|
type: Opaque
|
||||||
|
data:
|
||||||
|
ca.pem: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUR4RENDQXF5Z0F3SUJBZ0lVV3pqUDl5RUk0eHlRSnBzVHVERU4yV2ROaUFzd0RRWUpLb1pJaHZjTkFRRUwKQlFBd2FERUxNQWtHQTFVRUJoTUNWVk14RHpBTkJnTlZCQWdUQms5eVpXZHZiakVSTUE4R0ExVUVCeE1JVUc5eQpkR3hoYm1ReEV6QVJCZ05WQkFvVENrdDFZbVZ5Ym1WMFpYTXhDekFKQmdOVkJBc1RBa05CTVJNd0VRWURWUVFECkV3cExkV0psY201bGRHVnpNQjRYRFRFMk1EZ3hNVEUyTkRnd01Gb1hEVEl4TURneE1ERTJORGd3TUZvd2FERUwKTUFrR0ExVUVCaE1DVlZNeER6QU5CZ05WQkFnVEJrOXlaV2R2YmpFUk1BOEdBMVVFQnhNSVVHOXlkR3hoYm1ReApFekFSQmdOVkJBb1RDa3QxWW1WeWJtVjBaWE14Q3pBSkJnTlZCQXNUQWtOQk1STXdFUVlEVlFRREV3cExkV0psCmNtNWxkR1Z6TUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF3QkhNOGN6anc0Q1cKK05wbklhV012RzZlcVhtelNZT20vbHdaNUhOMnVLck9xaTNHYUUyTjFKd2tzcGRmMXNOUGFZMHdPR2xkbURIZgoxSnlyTW8rUFdLVUVjWko1WGE4Vm02d2I0MlpjczN3MEp5dlEzWFJjaDQyMFJRWGRKayszcmMybWRvSVRkL0lmCnZjWms0N0RzQTMrQU5QSUlSTzdWRmZpS1JNRFpTUDR1OThnVjI2eW1zbjc0TzFVKzNVUHR1TEFTVTFLck9FTk4KR01FWG0ydTJpdmVvbTJrbjFlZTZuM1hCR1o2bU52cUNPdWUxRXdza0gvWkhoUVh1UDgyV1U5dVk0aGVORnoyQwpBNmR0Q0Q0c3Z6eHc3ZFQ2cVhsV0ZIWUYrc3VLVDhXNkczd3NkOWxzV0ZVY0ZWL0lwaTVobEVaTWprNFNoY3RqCjdpYnlrRURKM1FJREFRQUJvMll3WkRBT0JnTlZIUThCQWY4RUJBTUNBUVl3RWdZRFZSMFRBUUgvQkFnd0JnRUIKL3dJQkFqQWRCZ05WSFE0RUZnUVVOdnhRZ3o5ZTNXS2VscU1KTmZXNE1KUHYzc0V3SHdZRFZSMGpCQmd3Rm9BVQpOdnhRZ3o5ZTNXS2VscU1KTmZXNE1KUHYzc0V3RFFZSktvWklodmNOQVFFTEJRQURnZ0VCQUp1TUhYUms1TEVyCmxET1p4Mm9aRUNQZ29reXMzSGJsM05oempXd2pncXdxNVN6a011V3QrUnVkdnRTK0FUQjFtTjRjYTN0eSt2bWcKT09heTkvaDZoditmSE5jZHpYdWR5dFZYZW1KN3F4ZFoxd25DUUcwdnpqOWRZY0xFSGpJWi94dU1jNlY3dnJ4YwpSc0preGp5aE01UXBmRHd0eVZKeGpkUmVBZ0huSyswTkNieHdtQ3cyRGIvOXpudm9LWGk4TEQwbkQzOFQxY3R3CmhmdGxwTmRoZXFNRlpEZXBuTUYwY2g2cHo5TFV5Mkh1cnhrV2dkWVNjY2VNU0hPTzBMcG4xeVVBMWczOTJhUjUKWk81Zm5KMW95Vm1LVWFCeDJCMndsSVlUSXlES1ZiMnY1UXNHbnYvRHVTMDZhcmVLTmsvTGpHRTRlMXlHOHJkcwpacnZHMzNvUmtEbz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
|
37
discovery/kubediscovery.yaml
Normal file
37
discovery/kubediscovery.yaml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
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
|
165
pkg/kubediscovery/handlers.go
Normal file
165
pkg/kubediscovery/handlers.go
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
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/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/square/go-jose"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: Just using a hardcoded token for now.
|
||||||
|
const tempTokenId string = "TOKENID"
|
||||||
|
const tempToken string = "EF1BA4F26DDA9FE2"
|
||||||
|
|
||||||
|
// 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 = "/tmp/secret/ca.pem"
|
||||||
|
|
||||||
|
// 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 to be hex encoded strings.
|
||||||
|
Lookup(tokenId string) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type hardcodedTokenLoader struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tl *hardcodedTokenLoader) Lookup(tokenId string) (string, error) {
|
||||||
|
if tokenId == tempTokenId {
|
||||||
|
return tempToken, nil
|
||||||
|
}
|
||||||
|
return "", errors.New(fmt.Sprintf("invalid token: %s", tokenId))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cl *fsCALoader) LoadPEM() (string, error) {
|
||||||
|
file, err := os.Open(CAPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := ioutil.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(data), 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
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClusterInfoHandler() *ClusterInfoHandler {
|
||||||
|
tl := hardcodedTokenLoader{}
|
||||||
|
cl := fsCALoader{}
|
||||||
|
return &ClusterInfoHandler{
|
||||||
|
tokenLoader: &tl,
|
||||||
|
caLoader: &cl,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.Lookup(tokenId)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Invalid token: %s", err)
|
||||||
|
http.Error(resp, "Forbidden", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("Loaded token: %s", token)
|
||||||
|
|
||||||
|
caPEM, err := cih.caLoader.LoadPEM()
|
||||||
|
caB64 := base64.StdEncoding.EncodeToString([]byte(caPEM))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
http.Error(resp, "Error encoding CA", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
clusterInfo := ClusterInfo{
|
||||||
|
Type: "ClusterInfo",
|
||||||
|
Version: "v1",
|
||||||
|
RootCertificates: caB64,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instantiate an signer using HMAC-SHA256.
|
||||||
|
hmacTestKey := fromHexBytes(token)
|
||||||
|
signer, err := jose.NewSigner(jose.HS256, hmacTestKey)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(resp, fmt.Sprintf("Error creating JWS signer: %s", err), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
payload, err := json.Marshal(clusterInfo)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(resp, fmt.Sprintf("Error serializing clusterInfo to JSON: %s", err),
|
||||||
|
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 {
|
||||||
|
http.Error(resp, fmt.Sprintf("Error signing clusterInfo to JSON: %s", err),
|
||||||
|
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))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Move into test package
|
||||||
|
// TODO: Should we use base64 instead?
|
||||||
|
func fromHexBytes(base16 string) []byte {
|
||||||
|
val, err := hex.DecodeString(base16)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("Invalid test data: %s", err))
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
100
pkg/kubediscovery/handlers_test.go
Normal file
100
pkg/kubediscovery/handlers_test.go
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
pkg/kubediscovery/model.go
Normal file
21
pkg/kubediscovery/model.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
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
|
||||||
|
|
||||||
|
// TODO: Sync with kubeadm api type
|
||||||
|
type ClusterInfo struct {
|
||||||
|
Type string
|
||||||
|
Version string
|
||||||
|
RootCertificates string `json:"rootCertificates"`
|
||||||
|
// TODO: ClusterID, Endpoints
|
||||||
|
}
|
51
pkg/kubediscovery/routes.go
Normal file
51
pkg/kubediscovery/routes.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
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 (
|
||||||
|
"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
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user