Skip to content

Commit

Permalink
job: add job support for unit kill (#218)
Browse files Browse the repository at this point in the history
  • Loading branch information
morpheu authored Nov 22, 2023
1 parent a667645 commit 6cd206c
Show file tree
Hide file tree
Showing 6 changed files with 917 additions and 792 deletions.
310 changes: 0 additions & 310 deletions tsuru/client/apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -1641,313 +1641,3 @@ func addCName(cnames []string, g cmd.AppNameMixIn, client *cmd.Client) error {
_, err = client.Do(request)
return err
}

type UnitAdd struct {
cmd.AppNameMixIn
fs *gnuflag.FlagSet
process string
version string
}

func (c *UnitAdd) Info() *cmd.Info {
return &cmd.Info{
Name: "unit-add",
Usage: "unit add <# of units> [-a/--app appname] [-p/--process processname] [--version version]",
Desc: `Adds new units to a process of an application. You need to have access to the
app to be able to add new units to it.`,
MinArgs: 1,
}
}

func (c *UnitAdd) Flags() *gnuflag.FlagSet {
if c.fs == nil {
c.fs = c.AppNameMixIn.Flags()
c.fs.StringVar(&c.process, "process", "", "Process name")
c.fs.StringVar(&c.process, "p", "", "Process name")
c.fs.StringVar(&c.version, "version", "", "Version number")
}
return c.fs
}

func (c *UnitAdd) Run(context *cmd.Context, client *cmd.Client) error {
context.RawOutput()
appName, err := c.AppName()
if err != nil {
return err
}
u, err := cmd.GetURL(fmt.Sprintf("/apps/%s/units", appName))
if err != nil {
return err
}
val := url.Values{}
val.Add("units", context.Args[0])
val.Add("process", c.process)
val.Set("version", c.version)
request, err := http.NewRequest("PUT", u, bytes.NewBufferString(val.Encode()))
if err != nil {
return err
}
request.Header.Add("Content-Type", "application/x-www-form-urlencoded")
response, err := client.Do(request)
if err != nil {
return err
}
defer response.Body.Close()
return cmd.StreamJSONResponse(context.Stdout, response)
}

type UnitRemove struct {
cmd.AppNameMixIn
fs *gnuflag.FlagSet
process string
version string
}

func (c *UnitRemove) Info() *cmd.Info {
return &cmd.Info{
Name: "unit-remove",
Usage: "unit remove <# of units> [-a/--app appname] [-p/-process processname] [--version version]",
Desc: `Removes units from a process of an application. You need to have access to the
app to be able to remove units from it.`,
MinArgs: 1,
}
}

func (c *UnitRemove) Flags() *gnuflag.FlagSet {
if c.fs == nil {
c.fs = c.AppNameMixIn.Flags()
c.fs.StringVar(&c.process, "process", "", "Process name")
c.fs.StringVar(&c.process, "p", "", "Process name")
c.fs.StringVar(&c.version, "version", "", "Version number")
}
return c.fs
}

func (c *UnitRemove) Run(context *cmd.Context, client *cmd.Client) error {
context.RawOutput()
appName, err := c.AppName()
if err != nil {
return err
}
val := url.Values{}
val.Add("units", context.Args[0])
val.Add("process", c.process)
val.Set("version", c.version)
url, err := cmd.GetURL(fmt.Sprintf("/apps/%s/units?%s", appName, val.Encode()))
if err != nil {
return err
}
request, err := http.NewRequest(http.MethodDelete, url, nil)
if err != nil {
return err
}
response, err := client.Do(request)
if err != nil {
return err
}
return cmd.StreamJSONResponse(context.Stdout, response)
}

type UnitKill struct {
cmd.AppNameMixIn
fs *gnuflag.FlagSet
force bool
}

func (c *UnitKill) Info() *cmd.Info {
return &cmd.Info{
Name: "unit-kill",
Usage: "unit kill [-a/--app appname] [-f/--force] <unit>",
Desc: `Kills units from a process of an application. You need to have access to the
app to be able to remove unit from it.`,
MinArgs: 1,
}
}

func (c *UnitKill) Flags() *gnuflag.FlagSet {
if c.fs == nil {
c.fs = c.AppNameMixIn.Flags()
c.fs.BoolVar(&c.force, "f", false, "Forces the termination of unit.")
}
return c.fs
}

func (c *UnitKill) Run(context *cmd.Context, client *cmd.Client) error {
context.RawOutput()
appName, err := c.AppName()
if err != nil {
return err
}
unit := context.Args[0]

v := url.Values{}
if c.force {
v.Set("force", "true")
}

url, err := cmd.GetURLVersion("1.12", fmt.Sprintf("/apps/%s/units/%s?%s", appName, unit, v.Encode()))
if err != nil {
return err
}
request, err := http.NewRequest(http.MethodDelete, url, nil)
if err != nil {
return err
}
response, err := client.Do(request)
if err != nil {
return err
}
return cmd.StreamJSONResponse(context.Stdout, response)
}

type UnitSet struct {
cmd.AppNameMixIn
fs *gnuflag.FlagSet
process string
version int
}

func (c *UnitSet) Info() *cmd.Info {
return &cmd.Info{
Name: "unit-set",
Usage: "unit set <# of units> [-a/--app appname] [-p/--process processname] [--version version]",
Desc: `Set the number of units for a process of an application, adding or removing units as needed. You need to have access to the
app to be able to set the number of units for it. The process flag is optional if the app has only 1 process.`,
MinArgs: 1,
}
}

func (c *UnitSet) Flags() *gnuflag.FlagSet {
if c.fs == nil {
c.fs = c.AppNameMixIn.Flags()
processMessage := "Process name"
c.fs.StringVar(&c.process, "process", "", processMessage)
c.fs.StringVar(&c.process, "p", "", processMessage)
c.fs.IntVar(&c.version, "version", 0, "Version number")
}
return c.fs
}

func (c *UnitSet) Run(context *cmd.Context, client *cmd.Client) error {
context.RawOutput()
appName, err := c.AppName()
if err != nil {
return err
}
u, err := cmd.GetURL(fmt.Sprintf("/apps/%s", appName))
if err != nil {
return err
}
request, err := http.NewRequest(http.MethodGet, u, nil)
if err != nil {
return err
}
response, err := client.Do(request)
if err != nil {
return err
}
result, err := io.ReadAll(response.Body)
if err != nil {
return err
}
var a app
err = json.Unmarshal(result, &a)
if err != nil {
return err
}

unitsByProcess := map[string][]unit{}
unitsByVersion := map[int][]unit{}
for _, u := range a.Units {
unitsByProcess[u.ProcessName] = append(unitsByProcess[u.ProcessName], u)
unitsByVersion[u.Version] = append(unitsByVersion[u.Version], u)
}

if len(unitsByProcess) != 1 && c.process == "" {
return errors.New("Please use the -p/--process flag to specify which process you want to set units for.")
}

if len(unitsByVersion) != 1 && c.version == 0 {
return errors.New("Please use the --version flag to specify which version you want to set units for.")
}

if c.process == "" {
for p := range unitsByProcess {
c.process = p
break
}
}

if c.version == 0 {
for v := range unitsByVersion {
c.version = v
break
}
}

existingUnits := 0
for _, unit := range a.Units {
if unit.ProcessName == c.process && unit.Version == c.version {
existingUnits++
}
}

desiredUnits, err := strconv.Atoi(context.Args[0])
if err != nil {
return err
}

if existingUnits < desiredUnits {
u, err := cmd.GetURL(fmt.Sprintf("/apps/%s/units", appName))
if err != nil {
return err
}

unitsToAdd := desiredUnits - existingUnits
val := url.Values{}
val.Add("units", strconv.Itoa(unitsToAdd))
val.Add("process", c.process)
val.Add("version", strconv.Itoa(c.version))
request, err := http.NewRequest(http.MethodPut, u, bytes.NewBufferString(val.Encode()))
if err != nil {
return err
}

request.Header.Add("Content-Type", "application/x-www-form-urlencoded")
response, err := client.Do(request)
if err != nil {
return err
}

defer response.Body.Close()
return cmd.StreamJSONResponse(context.Stdout, response)
}

if existingUnits > desiredUnits {
unitsToRemove := existingUnits - desiredUnits
val := url.Values{}
val.Add("units", strconv.Itoa(unitsToRemove))
val.Add("process", c.process)
val.Add("version", strconv.Itoa(c.version))
u, err := cmd.GetURL(fmt.Sprintf("/apps/%s/units?%s", appName, val.Encode()))
if err != nil {
return err
}

request, err := http.NewRequest(http.MethodDelete, u, nil)
if err != nil {
return err
}

response, err := client.Do(request)
if err != nil {
return err
}

defer response.Body.Close()
return cmd.StreamJSONResponse(context.Stdout, response)
}

fmt.Fprintf(context.Stdout, "The process %s, version %d already has %d units.\n", c.process, c.version, existingUnits)
return nil
}
Loading

0 comments on commit 6cd206c

Please sign in to comment.