diff --git a/runtime/kubernetes/kubernetes.go b/runtime/kubernetes/kubernetes.go index 0eba844e..348b28b6 100644 --- a/runtime/kubernetes/kubernetes.go +++ b/runtime/kubernetes/kubernetes.go @@ -184,16 +184,15 @@ func (k *kubernetes) getService(labels map[string]string, opts ...client.GetOpti // parse out deployment status and inject into service metadata if len(kdep.Status.Conditions) > 0 { - svc.Status(kdep.Status.Conditions[0].Type, nil) + status := transformStatus(kdep.Status.Conditions[0].Type) + svc.Status(status, nil) svc.Metadata["started"] = kdep.Status.Conditions[0].LastUpdateTime } else { - svc.Status("n/a", nil) + svc.Status(runtime.Unknown, nil) } // get the real status for _, item := range podList.Items { - var status string - // check the name if item.Metadata.Labels["name"] != name { continue @@ -203,12 +202,7 @@ func (k *kubernetes) getService(labels map[string]string, opts ...client.GetOpti continue } - switch item.Status.Phase { - case "Failed": - status = item.Status.Reason - default: - status = item.Status.Phase - } + status := transformStatus(item.Status.Phase) // skip if we can't get the container if len(item.Status.Containers) == 0 { @@ -225,11 +219,9 @@ func (k *kubernetes) getService(labels map[string]string, opts ...client.GetOpti // set status from waiting if v := state.Waiting; v != nil { - if len(v.Reason) > 0 { - status = v.Reason - } + status = runtime.Pending } - // TODO: set from terminated + svc.Status(status, nil) } @@ -744,3 +736,34 @@ func (k *kubernetes) DeleteNamespace(ns string) error { } return err } + +// transformStatus takes a deployment status (deploymentcondition.type) and transforms it into a +// runtime service status, e.g. containercreating => starting +func transformStatus(depStatus string) runtime.ServiceStatus { + switch strings.ToLower(depStatus) { + case "pending": + return runtime.Pending + case "containercreating": + return runtime.Starting + case "imagepullbackoff": + return runtime.Error + case "crashloopbackoff": + return runtime.Error + case "error": + return runtime.Error + case "running": + return runtime.Running + case "available": + return runtime.Running + case "succeeded": + return runtime.Stopped + case "failed": + return runtime.Error + case "waiting": + return runtime.Pending + case "terminated": + return runtime.Stopped + default: + return runtime.Unknown + } +} diff --git a/runtime/kubernetes/service.go b/runtime/kubernetes/service.go index 508d55f9..07ae0d03 100644 --- a/runtime/kubernetes/service.go +++ b/runtime/kubernetes/service.go @@ -149,7 +149,7 @@ func (s *service) Start(k client.Client, opts ...client.CreateOption) error { if logger.V(logger.DebugLevel, logger.DefaultLogger) { logger.Debugf("Runtime failed to create deployment: %v", err) } - s.Status("error", err) + s.Status(runtime.Error, err) v := parseError(err) if v.Reason == "AlreadyExists" { return runtime.ErrAlreadyExists @@ -161,7 +161,7 @@ func (s *service) Start(k client.Client, opts ...client.CreateOption) error { if logger.V(logger.DebugLevel, logger.DefaultLogger) { logger.Debugf("Runtime failed to create service: %v", err) } - s.Status("error", err) + s.Status(runtime.Error, err) v := parseError(err) if v.Reason == "AlreadyExists" { return runtime.ErrAlreadyExists @@ -169,7 +169,7 @@ func (s *service) Start(k client.Client, opts ...client.CreateOption) error { return err } - s.Status("started", nil) + s.Status(runtime.Running, nil) return nil } @@ -180,7 +180,7 @@ func (s *service) Stop(k client.Client, opts ...client.DeleteOption) error { if logger.V(logger.DebugLevel, logger.DefaultLogger) { logger.Debugf("Runtime failed to delete service: %v", err) } - s.Status("error", err) + s.Status(runtime.Error, err) return err } // delete deployment once the service has been deleted @@ -188,11 +188,11 @@ func (s *service) Stop(k client.Client, opts ...client.DeleteOption) error { if logger.V(logger.DebugLevel, logger.DefaultLogger) { logger.Debugf("Runtime failed to delete deployment: %v", err) } - s.Status("error", err) + s.Status(runtime.Error, err) return err } - s.Status("stopped", nil) + s.Status(runtime.Stopped, nil) return nil } @@ -202,7 +202,7 @@ func (s *service) Update(k client.Client, opts ...client.UpdateOption) error { if logger.V(logger.DebugLevel, logger.DefaultLogger) { logger.Debugf("Runtime failed to update deployment: %v", err) } - s.Status("error", err) + s.Status(runtime.Error, err) return err } if err := k.Update(serviceResource(s.kservice), opts...); err != nil { @@ -215,13 +215,13 @@ func (s *service) Update(k client.Client, opts ...client.UpdateOption) error { return nil } -func (s *service) Status(status string, err error) { +func (s *service) Status(status runtime.ServiceStatus, err error) { + s.Service.Status = status s.Metadata["lastStatusUpdate"] = time.Now().Format(time.RFC3339) + if err == nil { - s.Metadata["status"] = status delete(s.Metadata, "error") return } - s.Metadata["status"] = "error" s.Metadata["error"] = err.Error() } diff --git a/runtime/local/service.go b/runtime/local/service.go index 08819eff..824ce488 100644 --- a/runtime/local/service.go +++ b/runtime/local/service.go @@ -133,7 +133,7 @@ func (s *service) Start() error { if s.Metadata == nil { s.Metadata = make(map[string]string) } - s.Status("starting", nil) + s.Status(runtime.Starting, nil) // TODO: pull source & build binary if logger.V(logger.DebugLevel, logger.DefaultLogger) { @@ -142,7 +142,7 @@ func (s *service) Start() error { p, err := s.Process.Fork(s.Exec) if err != nil { - s.Status("error", err) + s.Status(runtime.Error, err) return err } // set the pid @@ -150,7 +150,7 @@ func (s *service) Start() error { // set to running s.running = true // set status - s.Status("running", nil) + s.Status(runtime.Running, nil) // set started s.Metadata["started"] = time.Now().Format(time.RFC3339) @@ -165,9 +165,9 @@ func (s *service) Start() error { } // Status updates the status of the service. Assumes it's called under a lock as it mutates state -func (s *service) Status(status string, err error) { +func (s *service) Status(status runtime.ServiceStatus, err error) { + s.Service.Status = status s.Metadata["lastStatusUpdate"] = time.Now().Format(time.RFC3339) - s.Metadata["status"] = status if err == nil { delete(s.Metadata, "error") return @@ -193,7 +193,7 @@ func (s *service) Stop() error { } // set status - s.Status("stopping", nil) + s.Status(runtime.Stopping, nil) // kill the process err := s.Process.Kill(s.PID) @@ -203,7 +203,7 @@ func (s *service) Stop() error { } // set status - s.Status("stopped", err) + s.Status(runtime.Stopped, err) // return the kill error return err @@ -240,12 +240,12 @@ func (s *service) Wait() { logger.Errorf("Service %s terminated with error %s", s.Name, err) } s.retries++ - s.Status("error", err) + s.Status(runtime.Error, err) s.Metadata["retries"] = strconv.Itoa(s.retries) s.err = err } else { - s.Status("done", nil) + s.Status(runtime.Stopped, nil) } // no longer running diff --git a/runtime/runtime.go b/runtime/runtime.go index b06012e2..e8c6f914 100644 --- a/runtime/runtime.go +++ b/runtime/runtime.go @@ -97,6 +97,29 @@ type Event struct { Options *CreateOptions } +// ServiceStatus defines service statuses +type ServiceStatus int + +const ( + // Unknown indicates the status of the service is not known + Unknown ServiceStatus = iota + // Pending is the initial status of a service + Pending + // Building is the status when the service is being built + Building + // Starting is the status when the service has been started but is not yet ready to accept traffic + Starting + // Running is the status when the service is active and accepting traffic + Running + // Stopping is the status when a service is stopping + Stopping + // Stopped is the status when a service has been stopped or has completed + Stopped + // Error is the status when an error occured, this could be a build error or a run error. The error + // details can be found within the service's metadata + Error +) + // Service is runtime service type Service struct { // Name of the service @@ -107,6 +130,8 @@ type Service struct { Source string // Metadata stores metadata Metadata map[string]string + // Status of the service + Status ServiceStatus } // Resources which are allocated to a serivce