{"action":"created","comment":{"url":"https://api.github.com/repos/kubernetes/kubernetes/pulls/comments/64126408","id":64126408,"diff_hunk":"@@ -0,0 +1,830 @@\n+/*\n+Copyright 2016 The Kubernetes Authors All rights reserved.\n+\n+Licensed under the Apache License, Version 2.0 (the \"License\");\n+you may not use this file except in compliance with the License.\n+You may obtain a copy of the License at\n+\n+    http://www.apache.org/licenses/LICENSE-2.0\n+\n+Unless required by applicable law or agreed to in writing, software\n+distributed under the License is distributed on an \"AS IS\" BASIS,\n+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+See the License for the specific language governing permissions and\n+limitations under the License.\n+*/\n+\n+package service\n+\n+import (\n+\t\"fmt\"\n+\t\"sync\"\n+\t\"time\"\n+\n+\t\"reflect\"\n+\n+\t\"github.com/golang/glog\"\n+\t\"k8s.io/kubernetes/pkg/api\"\n+\t\"k8s.io/kubernetes/pkg/api/v1\"\n+\t\"k8s.io/kubernetes/pkg/api/errors\"\n+\tcache \"k8s.io/kubernetes/pkg/client/cache\"\n+\tfederationcache \"k8s.io/kubernetes/federation/client/cache\"\n+\tclientset \"k8s.io/kubernetes/pkg/client/clientset_generated/release_1_3\"\n+\tfederationclientset \"k8s.io/kubernetes/federation/client/clientset_generated/federation_release_1_3\"\n+\t\"k8s.io/kubernetes/pkg/client/record\"\n+\t\"k8s.io/kubernetes/pkg/controller\"\n+\t\"k8s.io/kubernetes/pkg/controller/framework\"\n+\tpkg_runtime \"k8s.io/kubernetes/pkg/runtime\"\n+\t\"k8s.io/kubernetes/pkg/util/runtime\"\n+\t\"k8s.io/kubernetes/pkg/util/workqueue\"\n+\t\"k8s.io/kubernetes/pkg/util/sets\"\n+\t\"k8s.io/kubernetes/pkg/watch\"\n+\t\"k8s.io/kubernetes/pkg/util/wait\"\n+\t\"k8s.io/kubernetes/federation/apis/federation/v1alpha1\"\n+\t\"k8s.io/kubernetes/federation/pkg/dnsprovider\"\n+\t//\"k8s.io/kubernetes/federation/pkg/dnsprovider/rrstype\"\n+\n+\t\"k8s.io/kubernetes/pkg/conversion\"\n+)\n+\n+const (\n+\t// TODO update to 10 mins before merge\n+\tserviceSyncPeriod = 30 * time.Second\n+\tclusterSyncPeriod = 100 * time.Second\n+\n+\t// How long to wait before retrying the processing of a service change.\n+\t// If this changes, the sleep in hack/jenkins/e2e.sh before downing a cluster\n+\t// should be changed appropriately.\n+\tminRetryDelay = 5 * time.Second\n+\tmaxRetryDelay = 300 * time.Second\n+\n+\t// client retry count and interval is when accessing a remote kube-apiserver or federation apiserver\n+\t// how many times should be attempted and how long it should sleep when failure occurs\n+\t// the retry should be in short time so no exponential backoff\n+\tclientRetryCount    = 5\n+\tclientRetryInterval = 5 * time.Second\n+\n+\tretryable    = true\n+\n+\tdoNotRetry = time.Duration(0)\n+\n+\tUserAgentName = \"Federation-Service-Controller\"\n+\tKubeAPIQPS    = 20.0\n+\tKubeAPIBurst  = 30\n+)\n+\n+type cachedService struct {\n+\n+\t//release_1_3 client returns v1 service\n+\tlastState      *v1.Service\n+\t// The state as successfully applied to the DNS server\n+\tappliedState   *v1.Service\n+\t// cluster endpoint map hold subnet info from kubernetes clusters\n+\t// key clusterName\n+\tendpointMap    map[string]int\n+\t// cluster service map hold serivice status info from kubernetes clusters\n+\t// key clusterName\n+\n+\t// TODO only save LB status for now,\n+\t// as caching all services is not a good idea,\n+\t// need to consider how to handle external IP\n+\tserviceStatusMap    map[string]v1.LoadBalancerStatus\n+\n+\t// Ensures only one goroutine can operate on this service at any given time.\n+\trwlock         sync.Mutex\n+\n+\t// Controls error back-off\n+\tlastRetryDelay time.Duration\n+}\n+\n+type serviceCache struct {\n+\trwlock        sync.Mutex // protects serviceMap\n+\t// federation service map contains all service received from federation apiserver\n+\t// key serviceName\n+\tfedServiceMap map[string]*cachedService\n+}\n+\n+type ServiceController struct {\n+\tdns               dnsprovider.Interface\n+\tfederationClient  federationclientset.Interface\n+\tfederationName    string\n+\t// each federation should be configured with a single zone (e.g. \"mycompany.com\")\n+\tzones             []dnsprovider.Zone\n+\tserviceCache      *serviceCache\n+\tclusterCache      *clusterClientCache\n+\t// A store of services, populated by the serviceController\n+\tserviceStore      cache.StoreToServiceLister\n+\t// Watches changes to all services\n+\tserviceController *framework.Controller\n+\t// A store of services, populated by the serviceController\n+\tclusterStore      federationcache.StoreToClusterLister\n+\t// Watches changes to all services\n+\tclusterController *framework.Controller\n+\teventBroadcaster  record.EventBroadcaster\n+\teventRecorder     record.EventRecorder\n+\t//clusterLister     federationcache.StoreToClusterLister\n+\t// services that need to be synced\n+\tqueue             *workqueue.Type\n+\tknownClusterSet   sets.String\n+}\n+\n+// New returns a new service controller to keep DNS provider service resources\n+// (like Kubernetes Services and DNS server records for service discovery) in sync with the registry.\n+\n+func New(federationClient federationclientset.Interface, dns dnsprovider.Interface, federationName string) *ServiceController {\n+\tbroadcaster := record.NewBroadcaster()\n+\t// federationClient event is not supported yet?\n+\t//broadcaster.StartRecordingToSink(&unversioned_core.EventSinkImpl{Interface: kubeClient.Core().Events(\"\")})\n+\trecorder := broadcaster.NewRecorder(api.EventSource{Component: \"federated-service-controller\"})\n+\n+\ts := &ServiceController{\n+\t\tdns:              dns,\n+\t\tfederationClient: federationClient,\n+\t\tfederationName:   federationName,\n+\t\tserviceCache:     &serviceCache{fedServiceMap: make(map[string]*cachedService)},\n+\t\tclusterCache: \t  &clusterClientCache{\n+\t\t\trwlock: sync.Mutex{},\n+\t\t\tclientMap: make(map[string]*clusterCache),\n+\t\t},\n+\t\teventBroadcaster: broadcaster,\n+\t\teventRecorder:    recorder,\n+\t\tqueue:            workqueue.New(),\n+\t\tknownClusterSet:    make(sets.String),\n+\t}\n+\ts.serviceStore.Store, s.serviceController = framework.NewInformer(\n+\t\t&cache.ListWatch{\n+\t\t\tListFunc: func(options api.ListOptions) (pkg_runtime.Object, error) {\n+\t\t\t\treturn s.federationClient.Core().Services(api.NamespaceAll).List(options)\n+\t\t\t},\n+\t\t\tWatchFunc: func(options api.ListOptions) (watch.Interface, error) {\n+\t\t\t\treturn s.federationClient.Core().Services(api.NamespaceAll).Watch(options)\n+\t\t\t},\n+\t\t},\n+\t\t&v1.Service{},\n+\t\tserviceSyncPeriod,\n+\t\tframework.ResourceEventHandlerFuncs{\n+\t\t\tAddFunc: s.enqueueService,\n+\t\t\tUpdateFunc: func(old, cur interface{}) {\n+\t\t\t\t// there is case that old and new are equals but we still catch the event now.\n+\t\t\t\tif !reflect.DeepEqual(old, cur) {\n+\t\t\t\t\ts.enqueueService(cur)\n+\t\t\t\t}\n+\t\t\t},\n+\t\t\tDeleteFunc: s.enqueueService,\n+\t\t},\n+\t)\n+\ts.clusterStore.Store, s.clusterController = framework.NewInformer(\n+\t\t&cache.ListWatch{\n+\t\t\tListFunc: func(options api.ListOptions) (pkg_runtime.Object, error) {\n+\t\t\t\treturn s.federationClient.Federation().Clusters().List(options)\n+\t\t\t},\n+\t\t\tWatchFunc: func(options api.ListOptions) (watch.Interface, error) {\n+\t\t\t\treturn s.federationClient.Federation().Clusters().Watch(options)\n+\t\t\t},\n+\t\t},\n+\t\t&v1alpha1.Cluster{},\n+\t\tclusterSyncPeriod,\n+\t\tframework.ResourceEventHandlerFuncs{\n+\t\t\tDeleteFunc: s.clusterCache.delFromClusterSet,\n+\t\t\tAddFunc:    s.clusterCache.addToClientMap,\n+\t\t\tUpdateFunc:    func(old, cur interface{}) {\n+\t\t\t\ts.clusterCache.addToClientMap(cur)\n+\t\t\t},\n+\t\t},\n+\t)\n+\treturn s\n+}\n+\n+// obj could be an *api.Service, or a DeletionFinalStateUnknown marker item.\n+func (s *ServiceController) enqueueService (obj interface{}) {\n+\tkey, err := controller.KeyFunc(obj)\n+\tif err != nil {\n+\t\tglog.Errorf(\"Couldn't get key for object %+v: %v\", obj, err)\n+\t\treturn\n+\t}\n+\ts.queue.Add(key)\n+}\n+\n+// Run starts a background goroutine that watches for changes to Federated services\n+// and ensures that they have Kubernetes services created, updated or deleted appropriately.\n+// federationSyncPeriod controls how often we check the federation's services to\n+// ensure that the correct Kubernetes services (and associated DNS entries) exist.\n+// This is only necessary to fudge over failed watches.\n+// clusterSyncPeriod controls how often we check the federation's underlying clusters and\n+// their Kubernetes services to ensure that matching services created independently of the Federation\n+// (e.g. directly via the underlying cluster's API) are correctly accounted for.\n+\n+// It's an error to call Run() more than once for a given ServiceController\n+// object.\n+func (s *ServiceController) Run(workers int, stopCh <-chan struct{}) error {\n+\tif err := s.init(); err != nil {\n+\t\treturn err\n+\t}\n+\n+\tdefer runtime.HandleCrash()\n+\tgo s.serviceController.Run(stopCh)\n+\tgo s.clusterController.Run(stopCh)\n+\tfor i := 0; i < workers; i++ {\n+\t\tgo wait.Until(s.fedServiceWorker, time.Second, stopCh)\n+\t}\n+\tgo wait.Until(s.clusterEndpointWorker, time.Second, wait.NeverStop)\n+\tgo wait.Until(s.clusterServiceWorker, time.Second, wait.NeverStop)\n+\tgo wait.Until(s.clusterSyncLoop, clusterSyncPeriod, stopCh)\n+\t<-stopCh\n+\tglog.Infof(\"Shutting down Service Controller\")\n+\ts.queue.ShutDown()\n+\treturn nil\n+}\n+\n+// worker runs a worker thread that just dequeues items, processes them, and marks them done.\n+// It enforces that the syncHandler is never invoked concurrently with the same key.\n+func (s *ServiceController) fedServiceWorker() {\n+\tfor {\n+\t\tfunc() {\n+\t\t\tkey, quit := s.queue.Get()\n+\t\t\tif quit {\n+\t\t\t\treturn\n+\t\t\t}\n+\t\t\tdefer s.queue.Done(key)\n+\t\t\terr := s.syncService(key.(string))\n+\t\t\tif err != nil {\n+\t\t\t\tglog.Errorf(\"Error syncing service: %v\", err)\n+\t\t\t}\n+\t\t}()\n+\t}\n+}\n+\n+func (s *ServiceController) init() error {\n+\t//TODO\n+\treturn nil\n+}\n+\n+func wantsDNSRecords(service *v1.Service) bool{\n+\treturn service.Spec.Type == v1.ServiceTypeLoadBalancer\n+}\n+\n+// processServiceForCluster create or update serivce to all registered running clusters,\n+// update DNS records and update the service info with DNS entries to federation apiserver.\n+// the function returns any error caught, and the flag indicates if retryable\n+func (s *ServiceController) processServiceForCluster(clusterName string, service *v1.Service, client *clientset.Clientset) error {\n+\tglog.V(4).Infof(\"process service %s/%s for cluster %s\", service.Namespace, service.Name, clusterName)\n+\t// Create or Update k8s Service\n+\terr := s.ensureClusterServices(clusterName, service, client)\n+\tif err != nil {\n+\t\tglog.V(4).Infof(\"Failed to process service %s/%s for cluster %s\", service.Namespace, service.Name, clusterName)\n+\t\treturn err\n+\t}\n+\tglog.V(4).Infof(\"Successfully process service %s/%s for cluster %s\", service.Namespace, service.Name, clusterName)\n+\treturn nil\n+}\n+\n+// Returns whatever error occurred along with a boolean indicator of whether it\n+// should be retried.\n+func (s *ServiceController) updateFederationService(key string, fedService *v1.Service) (error, bool) {\n+\t// Clone federation service, and create them in underlying k8s cluster\n+\tclone, err := conversion.NewCloner().DeepCopy(fedService)\n+\tif err != nil {\n+\t\treturn err, !retryable\n+\t}\n+\tservice, ok := clone.(*v1.Service)\n+\tif !ok {\n+\t\treturn fmt.Errorf(\"Unexpected service cast error : %v\\n\", service), !retryable\n+\t}\n+\n+\t// handle available clusters one by one\n+\tvar hasErr bool\n+\tfor clusterName, cluster := range s.clusterCache.clientMap {\n+\t\tgo func(){\n+\t\t\terr = s.processServiceForCluster(clusterName, service, cluster.clientset)\n+\t\t\tif err != nil {\n+\t\t\t\thasErr = true\n+\t\t\t}\n+\t\t}()\n+\t}\n+\tif hasErr {\n+\t\t// detail error has been dumpped inside the loop\n+\t\treturn fmt.Errorf(\"service %s/%s was not successfully updated to all clusters\", service.Namespace, service.Name), retryable\n+\t}\n+\treturn nil, !retryable\n+}\n+\n+func (s *ServiceController) deleteFederationService(service *v1.Service) (error, bool) {\n+\t// handle available clusters one by one\n+\tfor clusterName, cluster := range s.clusterCache.clientMap {\n+\t\ts.deleteClusterService(clusterName, service, cluster.clientset)\n+\t}\n+\treturn nil, !retryable\n+}\n+func (s *ServiceController) deleteClusterService(clusterName string, service *v1.Service, clientset *clientset.Clientset) (error, bool) {\n+\tglog.V(4).Infof(\"Deleting service %s/%s from cluster %s\", service.Namespace, service.Name, clusterName)\n+\tvar err error\n+\tfor i := 0; i < clientRetryCount; i++ {\n+\t\terr = clientset.Core().Services(service.Namespace).Delete(service.Name, &api.DeleteOptions{})\n+\t\tif err == nil || errors.IsNotFound(err) {\n+\t\t\tglog.V(4).Infof(\"service %s/%s deleted from cluster %s\", service.Namespace, service.Name, clusterName)\n+\t\t\treturn nil, !retryable\n+\t\t}\n+\t\ttime.Sleep(clientRetryInterval)\n+\t}\n+\tglog.V(4).Infof(\"failed to delete service %s/%s from cluster %s, %+v\", service.Namespace, service.Name, clusterName, err)\n+\treturn err, !retryable\n+}\n+\n+func (s *ServiceController) ensureClusterServices(clusterName string, service *v1.Service, client *clientset.Clientset) error {","path":"federation/pkg/federation-controller/service/servicecontroller.go","position":333,"original_position":333,"commit_id":"4fbc3c773c41ffbc07673e4a710aaa3099b7a6f8","original_commit_id":"4fbc3c773c41ffbc07673e4a710aaa3099b7a6f8","user":{"login":"mfanjie","id":16643108,"avatar_url":"https://avatars.githubusercontent.com/u/16643108?v=3","gravatar_id":"","url":"https://api.github.com/users/mfanjie","html_url":"https://github.com/mfanjie","followers_url":"https://api.github.com/users/mfanjie/followers","following_url":"https://api.github.com/users/mfanjie/following{/other_user}","gists_url":"https://api.github.com/users/mfanjie/gists{/gist_id}","starred_url":"https://api.github.com/users/mfanjie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mfanjie/subscriptions","organizations_url":"https://api.github.com/users/mfanjie/orgs","repos_url":"https://api.github.com/users/mfanjie/repos","events_url":"https://api.github.com/users/mfanjie/events{/privacy}","received_events_url":"https://api.github.com/users/mfanjie/received_events","type":"User","site_admin":false},"body":"ok","created_at":"2016-05-21T02:10:50Z","updated_at":"2016-05-21T02:10:50Z","html_url":"https://github.com/kubernetes/kubernetes/pull/25517#discussion_r64126408","pull_request_url":"https://api.github.com/repos/kubernetes/kubernetes/pulls/25517","_links":{"self":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/comments/64126408"},"html":{"href":"https://github.com/kubernetes/kubernetes/pull/25517#discussion_r64126408"},"pull_request":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/25517"}}},"pull_request":{"url":"https://api.github.com/repos/kubernetes/kubernetes/pulls/25517","id":69765296,"html_url":"https://github.com/kubernetes/kubernetes/pull/25517","diff_url":"https://github.com/kubernetes/kubernetes/pull/25517.diff","patch_url":"https://github.com/kubernetes/kubernetes/pull/25517.patch","issue_url":"https://api.github.com/repos/kubernetes/kubernetes/issues/25517","number":25517,"state":"open","locked":false,"title":"[WIP]Fed service controller","user":{"login":"mfanjie","id":16643108,"avatar_url":"https://avatars.githubusercontent.com/u/16643108?v=3","gravatar_id":"","url":"https://api.github.com/users/mfanjie","html_url":"https://github.com/mfanjie","followers_url":"https://api.github.com/users/mfanjie/followers","following_url":"https://api.github.com/users/mfanjie/following{/other_user}","gists_url":"https://api.github.com/users/mfanjie/gists{/gist_id}","starred_url":"https://api.github.com/users/mfanjie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mfanjie/subscriptions","organizations_url":"https://api.github.com/users/mfanjie/orgs","repos_url":"https://api.github.com/users/mfanjie/repos","events_url":"https://api.github.com/users/mfanjie/events{/privacy}","received_events_url":"https://api.github.com/users/mfanjie/received_events","type":"User","site_admin":false},"body":"The code is based on Nikhil's latest clientset PR.\r\nThere are still lot of things to go.\r\n1. separate the logic of service creation and dns entry written.\r\nwhen watching the federation apiserver, if there is new service creation or update, the action is to create service in underlying k8s cluster, but do not create dns info.\r\n2. list and watch all underlying k8s clusters for service. if LB info is created, then create dns info\r\n3. generate random name when create k8s service to avoid name conflict.\r\n4. add logic to support service label based on hash, and use the labels to link federation service and k8s services.\r\n5. cluster add path.\r\n6. partial failure handling\r\n\r\n@quinton-hoole ","created_at":"2016-05-12T00:55:40Z","updated_at":"2016-05-21T02:10:50Z","closed_at":null,"merged_at":null,"merge_commit_sha":"ef0f6b1d533862eb3c750b2a00910a5470ed0779","assignee":{"login":"quinton-hoole","id":10390785,"avatar_url":"https://avatars.githubusercontent.com/u/10390785?v=3","gravatar_id":"","url":"https://api.github.com/users/quinton-hoole","html_url":"https://github.com/quinton-hoole","followers_url":"https://api.github.com/users/quinton-hoole/followers","following_url":"https://api.github.com/users/quinton-hoole/following{/other_user}","gists_url":"https://api.github.com/users/quinton-hoole/gists{/gist_id}","starred_url":"https://api.github.com/users/quinton-hoole/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/quinton-hoole/subscriptions","organizations_url":"https://api.github.com/users/quinton-hoole/orgs","repos_url":"https://api.github.com/users/quinton-hoole/repos","events_url":"https://api.github.com/users/quinton-hoole/events{/privacy}","received_events_url":"https://api.github.com/users/quinton-hoole/received_events","type":"User","site_admin":false},"milestone":{"url":"https://api.github.com/repos/kubernetes/kubernetes/milestones/18","html_url":"https://github.com/kubernetes/kubernetes/milestones/v1.3","labels_url":"https://api.github.com/repos/kubernetes/kubernetes/milestones/18/labels","id":1638460,"number":18,"title":"v1.3","description":"https://github.com/kubernetes/kubernetes/wiki/Release-1.3","creator":{"login":"bgrant0607","id":7725777,"avatar_url":"https://avatars.githubusercontent.com/u/7725777?v=3","gravatar_id":"","url":"https://api.github.com/users/bgrant0607","html_url":"https://github.com/bgrant0607","followers_url":"https://api.github.com/users/bgrant0607/followers","following_url":"https://api.github.com/users/bgrant0607/following{/other_user}","gists_url":"https://api.github.com/users/bgrant0607/gists{/gist_id}","starred_url":"https://api.github.com/users/bgrant0607/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bgrant0607/subscriptions","organizations_url":"https://api.github.com/users/bgrant0607/orgs","repos_url":"https://api.github.com/users/bgrant0607/repos","events_url":"https://api.github.com/users/bgrant0607/events{/privacy}","received_events_url":"https://api.github.com/users/bgrant0607/received_events","type":"User","site_admin":false},"open_issues":229,"closed_issues":147,"state":"open","created_at":"2016-03-11T07:40:20Z","updated_at":"2016-05-21T00:02:16Z","due_on":"2016-06-24T07:00:00Z","closed_at":null},"commits_url":"https://api.github.com/repos/kubernetes/kubernetes/pulls/25517/commits","review_comments_url":"https://api.github.com/repos/kubernetes/kubernetes/pulls/25517/comments","review_comment_url":"https://api.github.com/repos/kubernetes/kubernetes/pulls/comments{/number}","comments_url":"https://api.github.com/repos/kubernetes/kubernetes/issues/25517/comments","statuses_url":"https://api.github.com/repos/kubernetes/kubernetes/statuses/4fbc3c773c41ffbc07673e4a710aaa3099b7a6f8","head":{"label":"mfanjie:fed-service-controller","ref":"fed-service-controller","sha":"4fbc3c773c41ffbc07673e4a710aaa3099b7a6f8","user":{"login":"mfanjie","id":16643108,"avatar_url":"https://avatars.githubusercontent.com/u/16643108?v=3","gravatar_id":"","url":"https://api.github.com/users/mfanjie","html_url":"https://github.com/mfanjie","followers_url":"https://api.github.com/users/mfanjie/followers","following_url":"https://api.github.com/users/mfanjie/following{/other_user}","gists_url":"https://api.github.com/users/mfanjie/gists{/gist_id}","starred_url":"https://api.github.com/users/mfanjie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mfanjie/subscriptions","organizations_url":"https://api.github.com/users/mfanjie/orgs","repos_url":"https://api.github.com/users/mfanjie/repos","events_url":"https://api.github.com/users/mfanjie/events{/privacy}","received_events_url":"https://api.github.com/users/mfanjie/received_events","type":"User","site_admin":false},"repo":{"id":52251497,"name":"kubernetes","full_name":"mfanjie/kubernetes","owner":{"login":"mfanjie","id":16643108,"avatar_url":"https://avatars.githubusercontent.com/u/16643108?v=3","gravatar_id":"","url":"https://api.github.com/users/mfanjie","html_url":"https://github.com/mfanjie","followers_url":"https://api.github.com/users/mfanjie/followers","following_url":"https://api.github.com/users/mfanjie/following{/other_user}","gists_url":"https://api.github.com/users/mfanjie/gists{/gist_id}","starred_url":"https://api.github.com/users/mfanjie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mfanjie/subscriptions","organizations_url":"https://api.github.com/users/mfanjie/orgs","repos_url":"https://api.github.com/users/mfanjie/repos","events_url":"https://api.github.com/users/mfanjie/events{/privacy}","received_events_url":"https://api.github.com/users/mfanjie/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/mfanjie/kubernetes","description":"Container Cluster Manager from Google","fork":true,"url":"https://api.github.com/repos/mfanjie/kubernetes","forks_url":"https://api.github.com/repos/mfanjie/kubernetes/forks","keys_url":"https://api.github.com/repos/mfanjie/kubernetes/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mfanjie/kubernetes/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mfanjie/kubernetes/teams","hooks_url":"https://api.github.com/repos/mfanjie/kubernetes/hooks","issue_events_url":"https://api.github.com/repos/mfanjie/kubernetes/issues/events{/number}","events_url":"https://api.github.com/repos/mfanjie/kubernetes/events","assignees_url":"https://api.github.com/repos/mfanjie/kubernetes/assignees{/user}","branches_url":"https://api.github.com/repos/mfanjie/kubernetes/branches{/branch}","tags_url":"https://api.github.com/repos/mfanjie/kubernetes/tags","blobs_url":"https://api.github.com/repos/mfanjie/kubernetes/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mfanjie/kubernetes/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mfanjie/kubernetes/git/refs{/sha}","trees_url":"https://api.github.com/repos/mfanjie/kubernetes/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mfanjie/kubernetes/statuses/{sha}","languages_url":"https://api.github.com/repos/mfanjie/kubernetes/languages","stargazers_url":"https://api.github.com/repos/mfanjie/kubernetes/stargazers","contributors_url":"https://api.github.com/repos/mfanjie/kubernetes/contributors","subscribers_url":"https://api.github.com/repos/mfanjie/kubernetes/subscribers","subscription_url":"https://api.github.com/repos/mfanjie/kubernetes/subscription","commits_url":"https://api.github.com/repos/mfanjie/kubernetes/commits{/sha}","git_commits_url":"https://api.github.com/repos/mfanjie/kubernetes/git/commits{/sha}","comments_url":"https://api.github.com/repos/mfanjie/kubernetes/comments{/number}","issue_comment_url":"https://api.github.com/repos/mfanjie/kubernetes/issues/comments{/number}","contents_url":"https://api.github.com/repos/mfanjie/kubernetes/contents/{+path}","compare_url":"https://api.github.com/repos/mfanjie/kubernetes/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mfanjie/kubernetes/merges","archive_url":"https://api.github.com/repos/mfanjie/kubernetes/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mfanjie/kubernetes/downloads","issues_url":"https://api.github.com/repos/mfanjie/kubernetes/issues{/number}","pulls_url":"https://api.github.com/repos/mfanjie/kubernetes/pulls{/number}","milestones_url":"https://api.github.com/repos/mfanjie/kubernetes/milestones{/number}","notifications_url":"https://api.github.com/repos/mfanjie/kubernetes/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mfanjie/kubernetes/labels{/name}","releases_url":"https://api.github.com/repos/mfanjie/kubernetes/releases{/id}","deployments_url":"https://api.github.com/repos/mfanjie/kubernetes/deployments","created_at":"2016-02-22T06:18:44Z","updated_at":"2016-02-22T06:18:55Z","pushed_at":"2016-05-20T09:44:26Z","git_url":"git://github.com/mfanjie/kubernetes.git","ssh_url":"git@github.com:mfanjie/kubernetes.git","clone_url":"https://github.com/mfanjie/kubernetes.git","svn_url":"https://github.com/mfanjie/kubernetes","homepage":"http://kubernetes.io","size":238987,"stargazers_count":0,"watchers_count":0,"language":"Go","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"kubernetes:master","ref":"master","sha":"f538d6019dbe20a161e5475f910f4b64ff8cd569","user":{"login":"kubernetes","id":13629408,"avatar_url":"https://avatars.githubusercontent.com/u/13629408?v=3","gravatar_id":"","url":"https://api.github.com/users/kubernetes","html_url":"https://github.com/kubernetes","followers_url":"https://api.github.com/users/kubernetes/followers","following_url":"https://api.github.com/users/kubernetes/following{/other_user}","gists_url":"https://api.github.com/users/kubernetes/gists{/gist_id}","starred_url":"https://api.github.com/users/kubernetes/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kubernetes/subscriptions","organizations_url":"https://api.github.com/users/kubernetes/orgs","repos_url":"https://api.github.com/users/kubernetes/repos","events_url":"https://api.github.com/users/kubernetes/events{/privacy}","received_events_url":"https://api.github.com/users/kubernetes/received_events","type":"Organization","site_admin":false},"repo":{"id":20580498,"name":"kubernetes","full_name":"kubernetes/kubernetes","owner":{"login":"kubernetes","id":13629408,"avatar_url":"https://avatars.githubusercontent.com/u/13629408?v=3","gravatar_id":"","url":"https://api.github.com/users/kubernetes","html_url":"https://github.com/kubernetes","followers_url":"https://api.github.com/users/kubernetes/followers","following_url":"https://api.github.com/users/kubernetes/following{/other_user}","gists_url":"https://api.github.com/users/kubernetes/gists{/gist_id}","starred_url":"https://api.github.com/users/kubernetes/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kubernetes/subscriptions","organizations_url":"https://api.github.com/users/kubernetes/orgs","repos_url":"https://api.github.com/users/kubernetes/repos","events_url":"https://api.github.com/users/kubernetes/events{/privacy}","received_events_url":"https://api.github.com/users/kubernetes/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/kubernetes/kubernetes","description":"Container Cluster Manager from Google","fork":false,"url":"https://api.github.com/repos/kubernetes/kubernetes","forks_url":"https://api.github.com/repos/kubernetes/kubernetes/forks","keys_url":"https://api.github.com/repos/kubernetes/kubernetes/keys{/key_id}","collaborators_url":"https://api.github.com/repos/kubernetes/kubernetes/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/kubernetes/kubernetes/teams","hooks_url":"https://api.github.com/repos/kubernetes/kubernetes/hooks","issue_events_url":"https://api.github.com/repos/kubernetes/kubernetes/issues/events{/number}","events_url":"https://api.github.com/repos/kubernetes/kubernetes/events","assignees_url":"https://api.github.com/repos/kubernetes/kubernetes/assignees{/user}","branches_url":"https://api.github.com/repos/kubernetes/kubernetes/branches{/branch}","tags_url":"https://api.github.com/repos/kubernetes/kubernetes/tags","blobs_url":"https://api.github.com/repos/kubernetes/kubernetes/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/kubernetes/kubernetes/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/kubernetes/kubernetes/git/refs{/sha}","trees_url":"https://api.github.com/repos/kubernetes/kubernetes/git/trees{/sha}","statuses_url":"https://api.github.com/repos/kubernetes/kubernetes/statuses/{sha}","languages_url":"https://api.github.com/repos/kubernetes/kubernetes/languages","stargazers_url":"https://api.github.com/repos/kubernetes/kubernetes/stargazers","contributors_url":"https://api.github.com/repos/kubernetes/kubernetes/contributors","subscribers_url":"https://api.github.com/repos/kubernetes/kubernetes/subscribers","subscription_url":"https://api.github.com/repos/kubernetes/kubernetes/subscription","commits_url":"https://api.github.com/repos/kubernetes/kubernetes/commits{/sha}","git_commits_url":"https://api.github.com/repos/kubernetes/kubernetes/git/commits{/sha}","comments_url":"https://api.github.com/repos/kubernetes/kubernetes/comments{/number}","issue_comment_url":"https://api.github.com/repos/kubernetes/kubernetes/issues/comments{/number}","contents_url":"https://api.github.com/repos/kubernetes/kubernetes/contents/{+path}","compare_url":"https://api.github.com/repos/kubernetes/kubernetes/compare/{base}...{head}","merges_url":"https://api.github.com/repos/kubernetes/kubernetes/merges","archive_url":"https://api.github.com/repos/kubernetes/kubernetes/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/kubernetes/kubernetes/downloads","issues_url":"https://api.github.com/repos/kubernetes/kubernetes/issues{/number}","pulls_url":"https://api.github.com/repos/kubernetes/kubernetes/pulls{/number}","milestones_url":"https://api.github.com/repos/kubernetes/kubernetes/milestones{/number}","notifications_url":"https://api.github.com/repos/kubernetes/kubernetes/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/kubernetes/kubernetes/labels{/name}","releases_url":"https://api.github.com/repos/kubernetes/kubernetes/releases{/id}","deployments_url":"https://api.github.com/repos/kubernetes/kubernetes/deployments","created_at":"2014-06-06T22:56:04Z","updated_at":"2016-05-21T01:20:21Z","pushed_at":"2016-05-21T01:50:11Z","git_url":"git://github.com/kubernetes/kubernetes.git","ssh_url":"git@github.com:kubernetes/kubernetes.git","clone_url":"https://github.com/kubernetes/kubernetes.git","svn_url":"https://github.com/kubernetes/kubernetes","homepage":"http://kubernetes.io","size":445219,"stargazers_count":14481,"watchers_count":14481,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":4395,"mirror_url":null,"open_issues_count":3612,"forks":4395,"open_issues":3612,"watchers":14481,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/25517"},"html":{"href":"https://github.com/kubernetes/kubernetes/pull/25517"},"issue":{"href":"https://api.github.com/repos/kubernetes/kubernetes/issues/25517"},"comments":{"href":"https://api.github.com/repos/kubernetes/kubernetes/issues/25517/comments"},"review_comments":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/25517/comments"},"review_comment":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/kubernetes/kubernetes/pulls/25517/commits"},"statuses":{"href":"https://api.github.com/repos/kubernetes/kubernetes/statuses/4fbc3c773c41ffbc07673e4a710aaa3099b7a6f8"}}}}
{"actor":{"display_login":"mfanjie"}}