diff --git a/bootstrap/cmd/bootstrap/app/SaKey.go b/bootstrap/cmd/bootstrap/app/SaKey.go new file mode 100644 index 00000000000..939fe6f16f2 --- /dev/null +++ b/bootstrap/cmd/bootstrap/app/SaKey.go @@ -0,0 +1,100 @@ +package app + +import ( + "golang.org/x/net/context" + "cloud.google.com/go/container/apiv1" + iamadmin "cloud.google.com/go/iam/admin/apiv1" + "google.golang.org/api/option" + "golang.org/x/oauth2" + "k8s.io/client-go/rest" + containerpb "google.golang.org/genproto/googleapis/container/v1" + "google.golang.org/genproto/googleapis/iam/admin/v1" + "fmt" + log "github.com/sirupsen/logrus" + "k8s.io/api/core/v1" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clientset "k8s.io/client-go/kubernetes" + "encoding/base64" +) + + +type InsertSaKeyRequest struct { + Cluster string + Namespace string + Project string + SecretKey string + SecretName string + ServiceAccount string + Token string + Zone string +} + +func buildClusterConfig(ctx context.Context, ts oauth2.TokenSource, request InsertSaKeyRequest) (*rest.Config, error) { + c, err := container.NewClusterManagerClient(ctx, option.WithTokenSource(ts)) + if err != nil { + return nil, err + } + req := &containerpb.GetClusterRequest{ + ProjectId: request.Project, + Zone: request.Zone, + ClusterId: request.Cluster, + } + resp, err := c.GetCluster(ctx, req) + if err != nil { + return nil, err + } + Token, err := ts.Token() + caDec, _ := base64.StdEncoding.DecodeString(resp.MasterAuth.ClusterCaCertificate) + return &rest.Config{ + Host: "https://" + resp.Endpoint, + BearerToken: Token.AccessToken, + TLSClientConfig: rest.TLSClientConfig { + CAData: []byte(string(caDec)), + }, + }, nil +} + +func (s *ksServer) InsertSaKey(ctx context.Context, request InsertSaKeyRequest) error { + ts := oauth2.StaticTokenSource(&oauth2.Token{ + AccessToken: request.Token, + }) + k8sConfig, err := buildClusterConfig(ctx, ts, request) + if err != nil { + log.Errorf("Failed getting GKE cluster info: %v", err) + return err + } + + c, err := iamadmin.NewIamClient(ctx, option.WithTokenSource(ts)) + if err != nil { + log.Errorf("Cannot create iam admin client: %v", err) + return err + } + createServiceAccountKeyRequest := admin.CreateServiceAccountKeyRequest{ + Name: fmt.Sprintf("projects/%v/serviceAccounts/%v", request.Project, request.ServiceAccount), + } + + s.iamMux.Lock() + defer s.iamMux.Unlock() + + createdKey, err := c.CreateServiceAccountKey(ctx, &createServiceAccountKeyRequest) + if err != nil { + log.Errorf("Failed creating sa key: %v", err) + return err + } + k8sClientset, err := clientset.NewForConfig(k8sConfig) + secretData := make(map[string][]byte) + secretData[request.SecretKey] = createdKey.PrivateKeyData + _, err = k8sClientset.CoreV1().Secrets(request.Namespace).Create( + &v1.Secret{ + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: request.Namespace, + Name: request.SecretName, + }, + Data: secretData, + }) + if err != nil { + log.Errorf("Failed creating secret in GKE cluster: %v", err) + return err + } + return nil +} \ No newline at end of file diff --git a/bootstrap/cmd/bootstrap/app/ksServer.go b/bootstrap/cmd/bootstrap/app/ksServer.go index 15298e50d2c..98230a4081b 100644 --- a/bootstrap/cmd/bootstrap/app/ksServer.go +++ b/bootstrap/cmd/bootstrap/app/ksServer.go @@ -32,6 +32,7 @@ const JUPYTER_PROTOTYPE = "jupyterhub" type KsService interface { // CreateApp creates a ksonnet application. CreateApp(context.Context, CreateRequest) error + InsertSaKey(context.Context, InsertSaKeyRequest) error } // appInfo keeps track of information about apps. @@ -58,6 +59,7 @@ type ksServer struct { apps map[string]*appInfo appsMux sync.Mutex + iamMux sync.Mutex } // NewServer constructs a ksServer. @@ -134,11 +136,19 @@ type CreateRequest struct { AutoConfigure bool } -// createRequest is the response to a createRequest -type createResponse struct { +// basicServerResponse is general response contains nil if handler raise no error, otherwise an error message. +type basicServerResponse struct { Err string `json:"err,omitempty"` // errors don't JSON-marshal, so we use a string } +type HealthzRequest struct { + Msg string +} + +type HealthzResponse struct { + Reply string +} + // Request to apply an app. type ApplyRequest struct { // Name of the app to apply @@ -515,8 +525,30 @@ func makeCreateAppEndpoint(svc KsService) endpoint.Endpoint { req := request.(CreateRequest) err := svc.CreateApp(ctx, req) - r := &createResponse{} + r := &basicServerResponse{} + + if err != nil { + r.Err = err.Error() + } + return r, nil + } +} + +func makeHealthzEndpoint(svc KsService) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(HealthzRequest) + r := &HealthzResponse{} + r.Reply = req.Msg + "accepted! Sill alive!" + log.Info("response info: " + r.Reply) + return r, nil + } +} +func makeSaKeyEndpoint(svc KsService) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + req := request.(InsertSaKeyRequest) + err := svc.InsertSaKey(ctx, req) + r := &basicServerResponse{} if err != nil { r.Err = err.Error() } @@ -550,6 +582,35 @@ func (s *ksServer) StartHttp(port int) { encodeResponse, ) + healthzHandler := httptransport.NewServer( + makeHealthzEndpoint(s), + func (_ context.Context, r *http.Request) (interface{}, error) { + var request HealthzRequest + if err := json.NewDecoder(r.Body).Decode(&request); err != nil { + log.Info("Err decoding request: " + err.Error()) + return nil, err + } + log.Info("Request received: " + request.Msg) + return request, nil + }, + encodeResponse, + ) + + insertSaKeyHandler := httptransport.NewServer( + makeSaKeyEndpoint(s), + func (_ context.Context, r *http.Request) (interface{}, error) { + var request InsertSaKeyRequest + if err := json.NewDecoder(r.Body).Decode(&request); err != nil { + return nil, err + } + return request, nil + }, + encodeResponse, + ) + http.Handle("/apps/create", createAppHandler) + http.Handle("/healthz", healthzHandler) + http.Handle("/iam/insertSaKey", insertSaKeyHandler) + log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil)) } diff --git a/bootstrap/glide.lock b/bootstrap/glide.lock index 0585181c491..7b235125f9e 100644 --- a/bootstrap/glide.lock +++ b/bootstrap/glide.lock @@ -1,11 +1,16 @@ -hash: 2b8757afcc7c0ea61f24e5561283321c7b443b36e64035bedc68adb1e637f76f -updated: 2018-07-07T18:44:50.277401711-07:00 +hash: fc3ed8b04539672e17befa4853231ba10f35ff0713b20b8bf304a7587e08c194 +updated: 2018-08-07T14:34:21.793301-07:00 imports: - name: cloud.google.com/go - version: 3b1ae45394a234c385be014e9a488f2bb6eef821 + version: 64a2037ec6be8a4b0c1d1f706ed35b428b989239 subpackages: - compute/metadata - - internal + - container/apiv1 + - internal/version +- name: contrib.go.opencensus.io/exporter/stackdriver + version: 2b93072101d466aa4120b3c23c2e1b08af01541c + subpackages: + - propagation - name: github.com/Azure/go-autorest version: 58f6f26e200fa5dfb40c9cd1c83f3e2c860d779d subpackages: @@ -57,12 +62,14 @@ imports: - name: github.com/golang/glog version: 44145f04b68cf362d9c4df2182967c2275eaefed - name: github.com/golang/protobuf - version: 4bd1920723d7b7c925de087aa32e2187708897f7 + version: b4deda0973fb4c70b50d226b1af49f3da59f5265 subpackages: - proto + - protoc-gen-go/descriptor - ptypes - ptypes/any - ptypes/duration + - ptypes/empty - ptypes/timestamp - name: github.com/google/btree version: 7d79101e329e5a3adf994758c578dab82b90c017 @@ -81,6 +88,8 @@ imports: - query - name: github.com/google/gofuzz version: 44d81051d367757e1c7c6a5a86423ece9afcf63c +- name: github.com/googleapis/gax-go + version: 1ef592c90f479e3ab30c6c2312e20e13881b7ea6 - name: github.com/googleapis/gnostic version: 0c5108395e2debce0d731cf0287ddf7242066aba subpackages: @@ -176,7 +185,7 @@ imports: - name: github.com/PuerkitoBio/urlesc version: 5bd2802263f21d8788851d5305584c82a5c75d7e - name: github.com/sirupsen/logrus - version: c155da19408a8799da419ed3eeb0cb5db0ad5dbc + version: 3e01752db0189b9157070a0e1668a620f9a85da2 - name: github.com/spf13/afero version: 63644898a8da0bc22138abf860edaf5277b6102e subpackages: @@ -184,12 +193,24 @@ imports: - name: github.com/spf13/cobra version: 4dab30cb33e6633c33c787106bafbfbfdde7842d - name: github.com/spf13/pflag - version: 1ce0cc6db4029d97571db82f85092fccedb572ce + version: 583c0c0531f06d5278b7d917446061adc344b5cd - name: github.com/stretchr/testify version: f35b8ab0b5a2cef36673838d662e249dd9c94686 +- name: go.opencensus.io + version: 7b558058b7cc960667590e5413ef55157b06652e subpackages: - - assert - - mock + - internal + - internal/tagencoding + - plugin/ocgrpc + - plugin/ochttp + - plugin/ochttp/propagation/b3 + - stats + - stats/internal + - stats/view + - tag + - trace + - trace/internal + - trace/propagation - name: golang.org/x/crypto version: 81e90905daefcd6fd217b62423c0908922eadb30 subpackages: @@ -202,9 +223,11 @@ imports: - http2 - http2/hpack - idna + - internal/timeseries - lex/httplex + - trace - name: golang.org/x/oauth2 - version: a6bd8cefa1811bd24b86f8902872e4e8225f74c4 + version: fdc9e635145ae97e6c2cb777c48305600cf515cb subpackages: - google - internal @@ -229,6 +252,15 @@ imports: - unicode/bidi - unicode/norm - width +- name: google.golang.org/api + version: 9bdf771af19ab16b6a5084e5d542a7e04cb97004 + subpackages: + - googleapi/transport + - internal + - option + - transport + - transport/grpc + - transport/http - name: google.golang.org/appengine version: ad39d7fab7c60b2493fdc318c3d2cdb2128f92a4 subpackages: @@ -239,8 +271,45 @@ imports: - internal/log - internal/modules - internal/remote_api + - internal/socket - internal/urlfetch + - socket - urlfetch +- name: google.golang.org/genproto + version: daca94659cb50e9f37c1b834680f2e46358f10b0 + subpackages: + - googleapis/api/annotations + - googleapis/container/v1 + - googleapis/rpc/status +- name: google.golang.org/grpc + version: 07ef407d991f1004e6c3367c8f452ed9a02f17ff + subpackages: + - balancer + - balancer/base + - balancer/roundrobin + - codes + - connectivity + - credentials + - credentials/oauth + - encoding + - encoding/proto + - grpclog + - internal + - internal/backoff + - internal/channelz + - internal/envconfig + - internal/grpcrand + - internal/transport + - keepalive + - metadata + - naming + - peer + - resolver + - resolver/dns + - resolver/passthrough + - stats + - status + - tap - name: gopkg.in/inf.v0 version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 - name: gopkg.in/yaml.v2 @@ -263,7 +332,6 @@ imports: - certificates/v1beta1 - core/v1 - extensions/v1beta1 - - imagepolicy/v1alpha1 - networking/v1 - policy/v1beta1 - rbac/v1 @@ -370,10 +438,4 @@ imports: version: 868f2f29720b192240e18284659231b440f9cda5 subpackages: - pkg/common -testImports: -- name: github.com/pmezard/go-difflib - version: d8ed2627bdf02c080bf22230dbb337003b7aba2d - subpackages: - - difflib -- name: github.com/stretchr/objx - version: 8a3f7159479fbc75b30357fbc48f380b7320f08e +testImports: [] diff --git a/bootstrap/glide.yaml b/bootstrap/glide.yaml index 5992e2e5780..4972789b191 100644 --- a/bootstrap/glide.yaml +++ b/bootstrap/glide.yaml @@ -13,3 +13,12 @@ import: version: ^0.7.0 subpackages: - endpoint +- package: cloud.google.com/go + version: ^0.25.0 +- package: google.golang.org/genproto +- package: github.com/spf13/pflag + version: ^1.0.1 +- package: github.com/golang/protobuf + version: ^1.1.0 +- package: golang.org/x/oauth2 + version: fdc9e635145ae97e6c2cb777c48305600cf515cb