diff --git a/pkg/api/controllers.go b/pkg/api/controllers.go index 60153c3..d8bdbcc 100644 --- a/pkg/api/controllers.go +++ b/pkg/api/controllers.go @@ -1093,6 +1093,22 @@ func (s *Server) ServiceList(c *stdapi.Context) error { return c.RenderJSON(v) } +func (s *Server) ServiceRestart(c *stdapi.Context) error { + if err := s.hook("ServiceRestartValidate", c); err != nil { + return err + } + + app := c.Var("app") + name := c.Var("name") + + err := s.provider(c).WithContext(c.Context()).ServiceRestart(app, name) + if err != nil { + return err + } + + return c.RenderOK() +} + func (s *Server) ServiceUpdate(c *stdapi.Context) error { if err := s.hook("ServiceUpdateValidate", c); err != nil { return err diff --git a/pkg/api/routes.go b/pkg/api/routes.go index 464fcd0..5ab3e46 100644 --- a/pkg/api/routes.go +++ b/pkg/api/routes.go @@ -55,6 +55,7 @@ func (s *Server) setupRoutes(r stdapi.Router) { r.Route("GET", "/apps/{app}/resources/{name}", s.ResourceGet) r.Route("GET", "/apps/{app}/resources", s.ResourceList) r.Route("GET", "/apps/{app}/services", s.ServiceList) + r.Route("POST", "/apps/{app}/services/{name}/restart", s.ServiceRestart) r.Route("PUT", "/apps/{app}/services/{name}", s.ServiceUpdate) r.Route("", "", s.Start) r.Route("GET", "/system", s.SystemGet) diff --git a/pkg/structs/mock_Provider.go b/pkg/structs/mock_Provider.go index 20a93e9..cd9fc69 100644 --- a/pkg/structs/mock_Provider.go +++ b/pkg/structs/mock_Provider.go @@ -1051,6 +1051,20 @@ func (_m *MockProvider) ServiceList(app string) (Services, error) { return r0, r1 } +// ServiceRestart provides a mock function with given fields: app, name +func (_m *MockProvider) ServiceRestart(app string, name string) error { + ret := _m.Called(app, name) + + var r0 error + if rf, ok := ret.Get(0).(func(string, string) error); ok { + r0 = rf(app, name) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // ServiceUpdate provides a mock function with given fields: app, name, opts func (_m *MockProvider) ServiceUpdate(app string, name string, opts ServiceUpdateOptions) error { ret := _m.Called(app, name, opts) diff --git a/pkg/structs/provider.go b/pkg/structs/provider.go index 54ebf1c..45c89a5 100644 --- a/pkg/structs/provider.go +++ b/pkg/structs/provider.go @@ -73,6 +73,7 @@ type Provider interface { ResourceList(app string) (Resources, error) ServiceList(app string) (Services, error) + ServiceRestart(app, name string) error ServiceUpdate(app, name string, opts ServiceUpdateOptions) error SystemGet() (*System, error) diff --git a/pkg/structs/routes.go b/pkg/structs/routes.go index f10aaea..e89cff0 100644 --- a/pkg/structs/routes.go +++ b/pkg/structs/routes.go @@ -56,6 +56,7 @@ func init() { routes["ResourceGet"] = "GET /apps/{app}/resources/{name}" routes["ResourceList"] = "GET /apps/{app}/resources" routes["ServiceList"] = "GET /apps/{app}/services" + routes["ServiceRestart"] = "POST /apps/{app}/services/{name}/restart" routes["ServiceUpdate"] = "PUT /apps/{app}/services/{name}" routes["SystemGet"] = "GET /system" routes["SystemLogs"] = "SOCKET /system/logs" diff --git a/provider/k8s/service.go b/provider/k8s/service.go index f77b4b9..cd639d7 100644 --- a/provider/k8s/service.go +++ b/provider/k8s/service.go @@ -2,6 +2,8 @@ package k8s import ( "fmt" + "strconv" + "time" "github.com/convox/convox/pkg/common" "github.com/convox/convox/pkg/structs" @@ -79,6 +81,66 @@ func (p *Provider) ServiceList(app string) (structs.Services, error) { return ss, nil } +func (p *Provider) ServiceRestart(app, name string) error { + m, _, err := common.AppManifest(p, app) + if err != nil { + return err + } + + s, err := m.Service(name) + if err != nil { + return err + } + + if s.Agent.Enabled { + return p.serviceRestartDaemonset(app, name) + } + + return p.serviceRestartDeployment(app, name) +} + +func (p *Provider) serviceRestartDaemonset(app, name string) error { + ds := p.Cluster.ExtensionsV1beta1().DaemonSets(p.AppNamespace(app)) + + s, err := ds.Get(name, am.GetOptions{}) + if err != nil { + return err + } + + if s.Spec.Template.Annotations == nil { + s.Spec.Template.Annotations = map[string]string{} + } + + s.Spec.Template.Annotations["convox.com/restart"] = strconv.FormatInt(time.Now().UTC().UnixNano(), 10) + + if _, err := ds.Update(s); err != nil { + return err + } + + return nil +} + +func (p *Provider) serviceRestartDeployment(app, name string) error { + ds := p.Cluster.ExtensionsV1beta1().Deployments(p.AppNamespace(app)) + + s, err := ds.Get(name, am.GetOptions{}) + if err != nil { + return err + } + + if s.Spec.Template.Annotations == nil { + s.Spec.Template.Annotations = map[string]string{} + } + + s.Spec.Template.Annotations["convox.com/restart"] = strconv.FormatInt(time.Now().UTC().UnixNano(), 10) + + if _, err := ds.Update(s); err != nil { + return err + } + + return nil +} + func (p *Provider) ServiceUpdate(app, name string, opts structs.ServiceUpdateOptions) error { d, err := p.Cluster.AppsV1().Deployments(p.AppNamespace(app)).Get(name, am.GetOptions{}) if err != nil { diff --git a/sdk/methods.go b/sdk/methods.go index 8344961..0599fab 100644 --- a/sdk/methods.go +++ b/sdk/methods.go @@ -737,6 +737,16 @@ func (c *Client) ServiceList(app string) (structs.Services, error) { return v, err } +func (c *Client) ServiceRestart(app string, name string) error { + var err error + + ro := stdsdk.RequestOptions{Headers: stdsdk.Headers{}, Params: stdsdk.Params{}, Query: stdsdk.Query{}} + + err = c.Post(fmt.Sprintf("/apps/%s/services/%s/restart", app, name), ro, nil) + + return err +} + func (c *Client) ServiceUpdate(app string, name string, opts structs.ServiceUpdateOptions) error { var err error