Skip to content

Commit

Permalink
Merge pull request #23 from ritsec/3-action-deploy-of-obiii-merges-to…
Browse files Browse the repository at this point in the history
…-main

OBIII Scheduled Updates and `/update` Command
  • Loading branch information
1nv8rzim authored Sep 7, 2023
2 parents 92266c5 + 0702114 commit 35c980b
Show file tree
Hide file tree
Showing 8 changed files with 321 additions and 6 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
config.yml
private/
data.sqlite
OBIII
OBIII
OBIII.env
1 change: 1 addition & 0 deletions OBIII.service
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ After=multi-user.target

[Service]
Type=simple
EnvironmentFile=/root/ops-bot-iii/OBIII.env
WorkingDirectory=/root/ops-bot-iii/
Restart=always
RestartSec=30
Expand Down
2 changes: 2 additions & 0 deletions commands/enabled.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func populateSlashCommands(ctx ddtrace.SpanContext) {
SlashCommands["signin"] = slash.Signin()
SlashCommands["vote"] = slash.Vote()
SlashCommands["feedback"] = slash.Feedback()
SlashCommands["update"] = slash.Update()
}

// populateHandlers populates the Handlers map with all of the handlers
Expand Down Expand Up @@ -63,4 +64,5 @@ func populateScheduledEvents(ctx ddtrace.SpanContext) {
ScheduledEvents["goodfood"] = scheduled.GoodFood()
ScheduledEvents["heartbeat"] = scheduled.Heartbeat()
ScheduledEvents["status"] = scheduled.Status()
ScheduledEvents["update"] = scheduled.Update()
}
83 changes: 83 additions & 0 deletions commands/scheduled/update.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package scheduled

import (
"time"

"github.com/bwmarrin/discordgo"
"github.com/robfig/cron"
"gitlab.ritsec.cloud/1nv8rZim/ops-bot-iii/helpers"
"gitlab.ritsec.cloud/1nv8rZim/ops-bot-iii/logging"
"gitlab.ritsec.cloud/1nv8rZim/ops-bot-iii/structs"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
)

// updates repo, build binary, and exists if update is available
func updateOBIII(s *discordgo.Session, ctx ddtrace.SpanContext) {
span := tracer.StartSpan(
"commands.scheduled.update:updateOBIII",
tracer.ResourceName("Scheduled.Update:updateOBIII"),
tracer.ChildOf(ctx),
)
defer span.Finish()

logging.Debug(s, "Checking for update", nil, span)

update, err := helpers.UpdateMainBranch()
if err != nil {
logging.Error(s, err.Error(), nil, span)
return
}

if update {
logging.Critical(s, "Update available; updating", nil, span)

err = helpers.BuildOBIII()
if err != nil {
logging.Error(s, err.Error(), nil, span)
return
}

err = helpers.Exit()
if err != nil {
logging.Error(s, err.Error(), nil, span)
return
}
} else {
logging.Debug(s, "No update available", nil, span)
}
}

// checks for update every day at 2am and runs if available
func Update() *structs.ScheduledEvent {
return structs.NewScheduledTask(
func(s *discordgo.Session, quit chan interface{}) error {
span := tracer.StartSpan(
"commands.scheduled.update:Update",
tracer.ResourceName("Scheduled.Update"),
)
defer span.Finish()

est, err := time.LoadLocation("America/New_York")
if err != nil {
logging.Error(s, err.Error(), nil, span)
return err
}

c := cron.NewWithLocation(est)

// every day at 2am
err = c.AddFunc("0 0 2 * * *", func() { updateOBIII(s, span.Context()) })
if err != nil {
return err
}

c.Start()
<-quit
c.Stop()

return nil
},
)

}
143 changes: 143 additions & 0 deletions commands/slash/update.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package slash

import (
"fmt"

"github.com/bwmarrin/discordgo"
"github.com/sirupsen/logrus"
"gitlab.ritsec.cloud/1nv8rZim/ops-bot-iii/commands/slash/permission"
"gitlab.ritsec.cloud/1nv8rZim/ops-bot-iii/helpers"
"gitlab.ritsec.cloud/1nv8rZim/ops-bot-iii/logging"
"gitlab.ritsec.cloud/1nv8rZim/ops-bot-iii/structs"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
)

func Update() *structs.SlashCommand {
return &structs.SlashCommand{
Command: &discordgo.ApplicationCommand{
Name: "update",
Description: "Update the bot",
DefaultMemberPermissions: &permission.Admin,
Options: []*discordgo.ApplicationCommandOption{
{
Name: "force",
Description: "Force the bot to reboot",
Type: discordgo.ApplicationCommandOptionBoolean,
Required: false,
},
},
},
Handler: func(s *discordgo.Session, i *discordgo.InteractionCreate) {
span := tracer.StartSpan(
"commands.slash.update:Update",
tracer.ResourceName("/update"),
)
defer span.Finish()

logging.Debug(s, "Update command received", i.Member.User, span)

force := false
if len(i.ApplicationCommandData().Options) != 0 {
force = i.ApplicationCommandData().Options[0].BoolValue()
}

update, err := helpers.UpdateMainBranch()
if err != nil {
logging.Error(s, "Error updating main branch", i.Member.User, span, logrus.Fields{"err": err.Error()})
return
}

if !update {
logging.Debug(s, "No update available", i.Member.User, span)

if !force {
err = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Content: "No update available\nIf you want to force an update, use `/update force`",
Flags: discordgo.MessageFlagsEphemeral,
},
})
if err != nil {
logging.Error(s, "Error responding to interaction", i.Member.User, span, logrus.Fields{"err": err.Error()})
return
}

return
} else {
logging.Debug(s, "Forcing update", i.Member.User, span)

err = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Content: "No update available; forcing update\nBot will be up temporarily once done updating",
Flags: discordgo.MessageFlagsEphemeral,
},
})
if err != nil {
logging.Error(s, "Error responding to interaction", i.Member.User, span, logrus.Fields{"err": err.Error()})
return
}

err = helpers.BuildOBIII()
if err != nil {
logging.Error(s, "Error building OBIII", i.Member.User, span, logrus.Fields{"err": err.Error()})

content := fmt.Sprintf("Error building OBIII\n\nError:\n%s", err.Error())
_, err = s.InteractionResponseEdit(
i.Interaction,
&discordgo.WebhookEdit{
Content: &content,
},
)
if err != nil {
logging.Error(s, "Error editing interaction response", i.Member.User, span, logrus.Fields{"err": err.Error()})
}

return
}

err = helpers.Exit()
if err != nil {
logging.Error(s, "Error exiting", i.Member.User, span, logrus.Fields{"err": err.Error()})
return
}

return
}
}

err = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Content: "Update available; restarting now\nBot will be up temporarily once done updating",
Flags: discordgo.MessageFlagsEphemeral,
},
})
if err != nil {
logging.Error(s, "Error responding to interaction", i.Member.User, span, logrus.Fields{"err": err.Error()})
return
}

err = helpers.BuildOBIII()
if err != nil {
content := fmt.Sprintf("Error building OBIII\n\nError:\n%s", err.Error())
_, err = s.InteractionResponseEdit(
i.Interaction,
&discordgo.WebhookEdit{
Content: &content,
},
)

logging.Error(s, "Error building OBIII", i.Member.User, span, logrus.Fields{"err": err.Error()})
return
}

err = helpers.Exit()
if err != nil {
logging.Error(s, "Error exiting", i.Member.User, span, logrus.Fields{"err": err.Error()})
return
}
},
}
}
4 changes: 0 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,6 @@ github.com/mailgun/mailgun-go v2.0.0+incompatible h1:0FoRHWwMUctnd8KIR3vtZbqdfjp
github.com/mailgun/mailgun-go v2.0.0+incompatible/go.mod h1:NWTyU+O4aczg/nsGhQnvHL6v2n5Gy6Sv5tNDVvC6FbU=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
Expand All @@ -284,7 +283,6 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
Expand Down Expand Up @@ -331,7 +329,6 @@ github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk=
github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
Expand Down Expand Up @@ -634,7 +631,6 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
89 changes: 89 additions & 0 deletions helpers/update.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package helpers

import (
"bytes"
"fmt"
"os/exec"
)

// UpdateMainBranch switches to the main branch, fetches from origin, pulls from origin, and returns true if an update was pulled
func UpdateMainBranch() (bool, error) {
switchCmd := exec.Command("git", "switch", "main")

stderr := &bytes.Buffer{}
switchCmd.Stderr = stderr

err := switchCmd.Run()
if err != nil {
return false, fmt.Errorf("error switching to main branch: %s", stderr.String())
}

fetchCmd := exec.Command("git", "fetch", "origin", "main")

stderr = &bytes.Buffer{}
fetchCmd.Stderr = stderr

err = fetchCmd.Run()
if err != nil {
return false, fmt.Errorf("error fetching from origin: %s", stderr.String())
}

commitCount := exec.Command("git", "rev-list", "--count", "HEAD...origin/main")

stdout := &bytes.Buffer{}
stderr = &bytes.Buffer{}

commitCount.Stdout = stdout
commitCount.Stderr = stderr

err = commitCount.Run()
if err != nil {
return false, fmt.Errorf("error getting commit count: %s", stderr.String())
}

if stdout.String() == "0\n" {
return false, nil
}

pullCmd := exec.Command("git", "pull", "origin", "main")

stderr = &bytes.Buffer{}
pullCmd.Stderr = stderr

err = pullCmd.Run()
if err != nil {
return false, fmt.Errorf("error pulling from origin: %s", stderr.String())
}

return true, nil
}

// BuildOBIII builds the OBIII binary
func BuildOBIII() error {
buildCmd := exec.Command("/usr/local/go/bin/go", "build", "-o", "OBIII", "main.go")

stderr := &bytes.Buffer{}
buildCmd.Stderr = stderr

err := buildCmd.Run()
if err != nil {
return fmt.Errorf("error building obiii:\n%s\n%s", err.Error(), stderr.String())
}

return nil
}

// Exit restarts the OBIII service
func Exit() error {
exitCmd := exec.Command("systemctl", "restart", "OBIII")

stderr := &bytes.Buffer{}
exitCmd.Stderr = stderr

err := exitCmd.Run()
if err != nil {
return fmt.Errorf("error restarting obiii: %s", stderr.String())
}

return nil
}
2 changes: 1 addition & 1 deletion install.sh
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/bin/bash

go env > OBIII.env
cp OBIII.service /etc/systemd/system/OBIII.service
systemctl daemon-reload
systemctl stop OBIII
Expand Down

0 comments on commit 35c980b

Please sign in to comment.