diff --git a/_proto/hapi/services/tiller.proto b/_proto/hapi/services/tiller.proto index f16d68238..53ff6c276 100644 --- a/_proto/hapi/services/tiller.proto +++ b/_proto/hapi/services/tiller.proto @@ -154,6 +154,10 @@ message GetReleaseStatusRequest { string name = 1; // Version is the version of the release int32 version = 2; + // Kind is the kind of the resource to display + string kind = 3; + // Instance is the instance of the resource to display + string instance = 4; } // GetReleaseStatusResponse is the response indicating the status of the named release. diff --git a/cmd/helm/status.go b/cmd/helm/status.go index b635e6186..c26edd74e 100644 --- a/cmd/helm/status.go +++ b/cmd/helm/status.go @@ -47,7 +47,9 @@ type statusCmd struct { release string out io.Writer client helm.Interface - version int32 + kind string + instance string + version int32 } func newStatusCmd(client helm.Interface, out io.Writer) *cobra.Command { @@ -73,13 +75,18 @@ func newStatusCmd(client helm.Interface, out io.Writer) *cobra.Command { }, } + cmd.PersistentFlags().StringVar(&status.instance, "instance","","if set, display status of an instance in assigned kind.") + cmd.PersistentFlags().StringVar(&status.kind, "kind","","if set, display status of an assigned kind resource") cmd.PersistentFlags().Int32Var(&status.version, "revision", 0, "if set, display the status of the named release with revision") return cmd } func (s *statusCmd) run() error { - res, err := s.client.ReleaseStatus(s.release, helm.StatusReleaseVersion(s.version)) + + fmt.Printf("Kind=%s,instnace=%s",s.kind,s.instance) + + res, err := s.client.ReleaseStatus(s.release, helm.StatusReleaseVersion(s.version,s.kind,s.instance)) if err != nil { return prettyError(err) } diff --git a/docs/helm/helm_status.md b/docs/helm/helm_status.md index 893c3325a..45ff700ef 100644 --- a/docs/helm/helm_status.md +++ b/docs/helm/helm_status.md @@ -29,6 +29,9 @@ helm status [flags] RELEASE_NAME --tls-cert string path to TLS certificate file (default "$HELM_HOME/cert.pem") --tls-key string path to TLS key file (default "$HELM_HOME/key.pem") --tls-verify enable TLS for request and verify remote + --kind string if set, display status of an assigned kind resource (The kind of resorce is kubeneters + resource such as : service,ingress,deployment,pod,replicaset and so on) + --instance string if set, display status of an instance in assigned kind.(The paramater must along with "--kind") ``` ### Options inherited from parent commands diff --git a/pkg/helm/option.go b/pkg/helm/option.go index 0604e244a..d04d6d91b 100644 --- a/pkg/helm/option.go +++ b/pkg/helm/option.go @@ -371,9 +371,11 @@ type StatusOption func(*options) // StatusReleaseVersion will instruct Tiller to retrieve the status // of a particular version of a release. -func StatusReleaseVersion(version int32) StatusOption { +func StatusReleaseVersion(version int32,kind string,instance string) StatusOption { return func(opts *options) { opts.statusReq.Version = version + opts.statusReq.Kind = kind + opts.statusReq.Instance = instance } } diff --git a/pkg/kube/client.go b/pkg/kube/client.go index 333e3ab37..875444b01 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -145,32 +145,65 @@ func (c *Client) Build(namespace string, reader io.Reader) (Result, error) { // Get gets kubernetes resources as pretty printed string // // Namespace will set the namespace -func (c *Client) Get(namespace string, reader io.Reader) (string, error) { +func (c *Client) Get(namespace string, reader io.Reader, filter FilterStruct) (string, error) { // Since we don't know what order the objects come in, let's group them by the types, so // that when we print them, they come looking good (headers apply to subgroups, etc.) objs := make(map[string][]runtime.Object) - infos, err := c.BuildUnstructured(namespace, reader) + + infoAllKinds, err := c.BuildUnstructured(namespace, reader) if err != nil { return "", err } + + infos := infoAllKinds.Filter(filter.Filter) + + var objPods map[string][]api.Pod + if (!filter.IsFilter()) || filter.IsPodType() { + objPods = c.getRelationPods(infoAllKinds) + } + + log.Printf("objPods %+v\n", objPods) + missing := []string{} - err = perform(infos, func(info *resource.Info) error { - log.Printf("Doing get for %s: %q", info.Mapping.GroupVersionKind.Kind, info.Name) - if err := info.Get(); err != nil { - log.Printf("WARNING: Failed Get for resource %q: %s", info.Name, err) - missing = append(missing, fmt.Sprintf("%v\t\t%s", info.Mapping.Resource, info.Name)) + if len(infos) !=0 { + err = perform(infos, func(info *resource.Info) error { + + if err := info.Get(); err != nil { + log.Printf("WARNING: Failed Get for resource %q: %s", info.Name, err) + missing = append(missing, fmt.Sprintf("%v\t\t%s", info.Mapping.Resource, info.Name)) + return nil + } + + // Use APIVersion/Kind as grouping mechanism. I'm not sure if you can have multiple + // versions per cluster, but this certainly won't hurt anything, so let's be safe. + gvk := info.ResourceMapping().GroupVersionKind + vk := gvk.Version + "/" + gvk.Kind + + if gvk.Kind != "Pod" { + objs[vk] = append(objs[vk], info.Object) + } else { + if !IsFoundPodInfo(objPods[vk], info) { + objs[vk] = append(objs[vk], info.Object) + } + } return nil + }) + if err != nil { + return "", err + } + }else{ + if !filter.IsFilter(){ + return "",ErrNoObjectsVisited + }else if (!filter.IsPodType()) || (len(objPods) == 0) { + return "",nil } + } - // Use APIVersion/Kind as grouping mechanism. I'm not sure if you can have multiple - // versions per cluster, but this certainly won't hurt anything, so let's be safe. - gvk := info.ResourceMapping().GroupVersionKind - vk := gvk.Version + "/" + gvk.Kind - objs[vk] = append(objs[vk], info.Object) - return nil - }) - if err != nil { - return "", err + //here, we will add the objPods to the objs + for key,podItems := range objPods{ + for _,pod := range podItems { + objs[key] = append(objs[key],&pod) + } } // Ok, now we have all the objects grouped by types (say, by v1/Pod, v1/Service, etc.), so @@ -595,3 +628,89 @@ func watchPodUntilComplete(timeout time.Duration, info *resource.Info) error { return err } + +func (c *Client) getRelationPods(infos []*resource.Info)(map[string][]api.Pod){ + var objPods = make(map[string][]api.Pod) + + for _, value := range infos { + objPods , _ = c.getSelectRelationPod(value,objPods) + } + return objPods +} + +func (c *Client) getSelectRelationPod(info *resource.Info,objPods map[string][]api.Pod)(map[string][]api.Pod,error){ + log.Printf("1222getSelectRelationPod Info: %+v",info) + log.Printf("222222getSelectRelationPod object: %+v",info.Object) + + err := info.Get() + if err != nil { + return objPods, err + } + + log.Printf("getSelectRelationPod object: %+v",info.Object) + + versioned, err := c.AsVersionedObject(info.Object) + if runtime.IsNotRegisteredError(err) { + return objPods,nil + } + if err != nil { + return objPods,err + } + + selector, err := getSelectorFromObject(versioned) + + log.Printf("getSelectRelationPod selector: %+v",selector) + if err != nil { + return objPods,err + } + client, _ := c.ClientSet() + + pods, err := client.Core().Pods(info.Namespace).List(metav1.ListOptions{ + FieldSelector: fields.Everything().String(), + LabelSelector: labels.Set(selector).AsSelector().String(), + }) + if err != nil { + return objPods,err + } + + for _, pod := range pods.Items { + + log.Printf("get select relation pod: %v/%v", pod.Namespace, pod.Name) + + if pod.APIVersion == "" { + pod.APIVersion = "v1" + } + + if pod.Kind == "" { + pod.Kind = "Pod" + } + + vk := pod.GroupVersionKind().Version + "/" + pod.GroupVersionKind().Kind + + if !IsFoundPod(objPods[vk], pod) { + objPods[vk] = append(objPods[vk], pod) + } + } + + return objPods,nil +} + +func IsFoundPod(podItem []api.Pod,pod api.Pod) bool { + for _,value := range podItem { + if (value.Namespace == pod.Namespace) && (value.Name==pod.Name) { + return true + } + } + + return false +} +func IsFoundPodInfo(podItem []api.Pod,podInfo *resource.Info) bool { + for _, value := range podItem { + if (value.Namespace == podInfo.Namespace) && (value.Name == podInfo.Name) { + return true + } + } + return false +} + + diff --git a/pkg/kube/result.go b/pkg/kube/result.go index 9f143feb5..8be71b3f9 100644 --- a/pkg/kube/result.go +++ b/pkg/kube/result.go @@ -21,6 +21,35 @@ import "k8s.io/kubernetes/pkg/kubectl/resource" // Result provides convenience methods for comparing collections of Infos. type Result []*resource.Info +type FilterStruct struct { + Kind string + Instance string +} + +func (fs *FilterStruct) Filter (info *resource.Info) bool{ + if info != nil { + if (fs.Kind == "") || (fs.Kind == info.Mapping.GroupVersionKind.Kind) { + if (fs.Instance == "") || (fs.Instance == info.Name) { + return true + } + } + } + return false +} +func (fs *FilterStruct) IsFilter() bool{ + if (fs.Kind == "") && (fs.Instance == "") { + return false + } + return true +} + +func (fs *FilterStruct) IsPodType() bool { + if (fs.Kind == "Pod") { + return true + } + return false +} + // Append adds an Info to the Result. func (r *Result) Append(val *resource.Info) { *r = append(*r, val) diff --git a/pkg/tiller/environment/environment.go b/pkg/tiller/environment/environment.go index 26516474b..b277b4e9e 100644 --- a/pkg/tiller/environment/environment.go +++ b/pkg/tiller/environment/environment.go @@ -123,7 +123,7 @@ type KubeClient interface { // // reader must contain a YAML stream (one or more YAML documents separated // by "\n---\n"). - Get(namespace string, reader io.Reader) (string, error) + Get(namespace string, reader io.Reader, filter kube.FilterStruct) (string, error) // Delete destroys one or more resources. // diff --git a/pkg/tiller/release_modules.go b/pkg/tiller/release_modules.go index e13b26de9..319a335e1 100644 --- a/pkg/tiller/release_modules.go +++ b/pkg/tiller/release_modules.go @@ -70,8 +70,8 @@ func (m *LocalReleaseModule) Rollback(current, target *release.Release, req *ser } // Status returns kubectl-like formatted status of release objects -func (m *LocalReleaseModule) Status(r *release.Release, req *services.GetReleaseStatusRequest, env *environment.Environment) (string, error) { - return env.KubeClient.Get(r.Namespace, bytes.NewBufferString(r.Manifest)) +func (m *LocalReleaseModule) Status(r *release.Release, req *services.GetReleaseStatusRequest, env *environment.Environment, filter kube.FilterStruct) (string, error) { + return env.KubeClient.Get(r.Namespace, bytes.NewBufferString(r.Manifest),filter) } // Delete deletes the release and returns manifests that were kept in the deletion process @@ -120,7 +120,7 @@ func (m *RemoteReleaseModule) Rollback(current, target *release.Release, req *se } // Status returns status retrieved from rudder.ReleaseStatus -func (m *RemoteReleaseModule) Status(r *release.Release, req *services.GetReleaseStatusRequest, env *environment.Environment) (string, error) { +func (m *RemoteReleaseModule) Status(r *release.Release, req *services.GetReleaseStatusRequest, env *environment.Environment, filter kube.FilterStruct) (string, error) { statusRequest := &rudderAPI.ReleaseStatusRequest{Release: r} resp, err := rudder.ReleaseStatus(statusRequest) return resp.Info.Status.Resources, err diff --git a/pkg/tiller/release_server.go b/pkg/tiller/release_server.go index adc4ddf3c..5be7343ea 100644 --- a/pkg/tiller/release_server.go +++ b/pkg/tiller/release_server.go @@ -41,6 +41,7 @@ import ( "k8s.io/helm/pkg/tiller/environment" "k8s.io/helm/pkg/timeconv" "k8s.io/helm/pkg/version" + "k8s.io/helm/pkg/kube" ) // releaseNameMaxLen is the maximum length of a release name. @@ -261,9 +262,13 @@ func (s *ReleaseServer) GetReleaseStatus(c ctx.Context, req *services.GetRelease Info: rel.Info, } + + var filter kube.FilterStruct = kube.FilterStruct{Kind:req.Kind,Instance:req.Instance} + // Ok, we got the status of the release as we had jotted down, now we need to match the // manifest we stashed away with reality from the cluster. - resp, err := s.ReleaseModule.Status(rel, req, s.env) + resp, err := s.ReleaseModule.Status(rel, req, s.env,filter) + if sc == release.Status_DELETED || sc == release.Status_FAILED { // Skip errors if this is already deleted or failed. return statusResp, nil