Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tiup-publisher): add notify when publish failed #178

Merged
merged 1 commit into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 69 additions & 19 deletions tiup-publisher/handler.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package main

import (
"bytes"
"context"
"crypto/sha256"
"encoding/json"
"fmt"
"io"
"net/http"
Expand All @@ -12,7 +14,7 @@ import (
"strings"

cloudevents "github.com/cloudevents/sdk-go/v2"
"github.com/rs/zerolog/log"
"github.com/rs/zerolog"
"oras.land/oras-go/v2/registry/remote"
"oras.land/oras-go/v2/registry/remote/auth"
"oras.land/oras-go/v2/registry/remote/retry"
Expand All @@ -21,11 +23,20 @@ import (
)

type Handler struct {
mirrorsURL string
mirrorURL string
larkWebhookURL string
logger zerolog.Logger
}

func NewHandler(mirror string) (*Handler, error) {
return &Handler{mirror}, nil
func NewHandler(mirrorURL, larkWebhookURL string, logger *zerolog.Logger) (*Handler, error) {
handler := Handler{mirrorURL: mirrorURL, larkWebhookURL: larkWebhookURL}
if logger == nil {
handler.logger = zerolog.New(os.Stderr).With().Timestamp().Logger()
} else {
handler.logger = *logger
}

return &handler, nil
}

func (h *Handler) SupportEventTypes() []string {
Expand All @@ -50,29 +61,68 @@ func (h *Handler) handleImpl(data *PublishRequestEvent) cloudevents.Result {
// 1. get the the tarball from data.From.
saveTo, err := downloadFile(data)
if err != nil {
log.Err(err).Msg("download file failed")
h.logger.Err(err).Msg("download file failed")
// h.notifyLark(&data.Publish, err)
return cloudevents.NewReceipt(true, "download file failed: %v", err)
}
log.Info().Msg("download file success")
h.logger.Info().Msg("download file success")

// 2. publish the tarball to the mirror.
if err := publish(saveTo, &data.Publish, h.mirrorsURL); err != nil {
log.Err(err).Msg("publish to mirror failed")
if err := h.publish(saveTo, &data.Publish); err != nil {
h.logger.Err(err).Msg("publish to mirror failed")
h.notifyLark(&data.Publish, err)
return cloudevents.NewReceipt(true, "publish to mirror failed: %v", err)
}
log.Info().Msg("publish to mirror success")
h.logger.Info().Msg("publish to mirror success")

// 3. check the package is in the mirror.
// printf 'post_check "$(tiup mirror show)/%s-%s-%s-%s.tar.gz" "%s"\n' \
remoteURL := fmt.Sprintf("%s/%s-%s-%s-%s.tar.gz", h.mirrorsURL, data.Publish.Name, data.Publish.Version, data.Publish.OS, data.Publish.Arch)
remoteURL := fmt.Sprintf("%s/%s-%s-%s-%s.tar.gz", h.mirrorURL, data.Publish.Name, data.Publish.Version, data.Publish.OS, data.Publish.Arch)
if err := postCheck(saveTo, remoteURL); err != nil {
log.Err(err).Str("remote", remoteURL).Msg("post check failed")
h.logger.Err(err).Str("remote", remoteURL).Msg("post check failed")
return cloudevents.NewReceipt(true, "post check failed: %v", err)
}
log.Info().Str("remote", remoteURL).Msg("post check success")

h.logger.Info().Str("remote", remoteURL).Msg("post check success")
return cloudevents.ResultACK
}

func (h *Handler) notifyLark(publishInfo *PublishInfo, err error) {
if h.larkWebhookURL == "" {
return
}

message := fmt.Sprintf("Failed to publish %s-%s @%s/%s platform to mirror %s: %v",
publishInfo.Name,
publishInfo.Version,
publishInfo.OS,
publishInfo.Arch,
h.mirrorURL,
err)

payload := map[string]interface{}{
"msg_type": "text",
"content": map[string]string{
"text": message,
},
}

jsonPayload, err := json.Marshal(payload)
if err != nil {
h.logger.Err(err).Msg("failed to marshal JSON payload")
}

resp, err := http.Post(h.larkWebhookURL, "application/json", bytes.NewBuffer(jsonPayload))
if err != nil {
h.logger.Err(err).Msg("failed to send notification to Lark")
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
h.logger.Error().Msgf("Lark API returned non-OK status: %d", resp.StatusCode)
}
}

func downloadFile(data *PublishRequestEvent) (string, error) {
switch data.From.Type {
case FromTypeOci:
Expand All @@ -81,26 +131,26 @@ func downloadFile(data *PublishRequestEvent) (string, error) {
case FromTypeHTTP:
return downloadHTTPFile(data.From.HTTP.URL)
default:
return "", nil
return "", fmt.Errorf("unknown from type: %v", data.From.Type)
}
}

func publish(file string, info *PublishInfo, to string) error {
func (h *Handler) publish(file string, info *PublishInfo) error {
args := []string{"mirror", "publish", info.Name, info.Version, file, info.EntryPoint, "--os", info.OS, "--arch", info.Arch, "--desc", info.Description}
if info.Standalone {
args = append(args, "--standalone")
}
command := exec.Command("tiup", args...)
command.Env = os.Environ()
command.Env = append(command.Env, "TIUP_MIRRORS="+to)
log.Debug().Any("args", command.Args).Any("env", command.Args).Msg("will execute tiup command")
command.Env = append(command.Env, "TIUP_MIRRORS="+h.mirrorURL)
h.logger.Debug().Any("args", command.Args).Any("env", command.Args).Msg("will execute tiup command")
output, err := command.Output()
if err != nil {
log.Err(err).Msg("failed to execute tiup command")
h.logger.Err(err).Msg("failed to execute tiup command")
return err
}
log.Info().
Str("mirror", to).
h.logger.Info().
Str("mirror", h.mirrorURL).
Str("output", string(output)).
Msg("tiup package publish success")

Expand Down
4 changes: 2 additions & 2 deletions tiup-publisher/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
const testMirrorURL = "http://tiup.pingcap.net:8988"

func TestHandler_Handle(t *testing.T) {
t.Skip("manual test case")
// t.Skip("manual test case")

t.Run("Valid event data", func(tt *testing.T) {
event := event.New()
Expand All @@ -37,7 +37,7 @@ func TestHandler_Handle(t *testing.T) {
},
})

h := &Handler{testMirrorURL}
h, _ := NewHandler(testMirrorURL, "https://open.feishu.cn/open-apis/bot/v2/hook/<replace_me>", nil)
result := h.Handle(event)
assert.True(tt, cloudevents.IsACK(result))
tt.Fail()
Expand Down
2 changes: 1 addition & 1 deletion tiup-publisher/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func main() {
if err := yaml.Unmarshal(configData, &config); err != nil {
log.Fatal().Err(err).Msg("Error parsing config file")
}
handler, err := NewHandler(config.MirrorUrl)
handler, err := NewHandler(config.MirrorUrl, config.LarkWebhookURL, &log.Logger)
if err != nil {
log.Fatal().Err(err).Msg("Error creating handler")
}
Expand Down
28 changes: 9 additions & 19 deletions tiup-publisher/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ const (
)

type Config struct {
Brokers []string `yaml:"brokers"`
Topic string `yaml:"topic"`
Brokers []string `yaml:"brokers" json:"brokers,omitempty"`
Topic string `yaml:"topic" json:"topic,omitempty"`
Credentials struct {
Type string `yaml:"type"`
Username string `yaml:"username"`
Password string `yaml:"password"`
} `yaml:"credentials"`
ConsumerGroup string `yaml:"consumerGroup"`
MirrorUrl string `yaml:"mirrorUrl"`
Type string `yaml:"type" json:"type,omitempty"`
Username string `yaml:"username" json:"username,omitempty"`
Password string `yaml:"password" json:"password,omitempty"`
} `yaml:"credentials" json:"credentials,omitempty"`
ConsumerGroup string `yaml:"consumer_group" json:"consumer_group,omitempty"`
MirrorUrl string `yaml:"mirror_url" json:"mirror_url,omitempty"`
LarkWebhookURL string `yaml:"lark_webhook_url" json:"lark_webhook_url,omitempty"`
}

type PublishRequestEvent struct {
Expand Down Expand Up @@ -48,14 +49,3 @@ type FromOci struct {
type FromHTTP struct {
URL string `json:"url,omitempty"`
}

// 2024-09-23T20:02:29.932583969+08:00 tiup mirror publish ctl v8.4.0-alpha-nightly ctl-v8.4.0-alpha-41-gedb43c053-darwin-amd64.tar.gz ctl --os darwin --arch amd64 --desc "TiDB controller suite"

type TiupMirror struct {
Name string `yaml:"name,omitempty" json:"name,omitempty"`
URL string `yaml:"url,omitempty" json:"url,omitempty"`
Auth struct {
Username string `yaml:"username,omitempty" json:"username,omitempty"`
Password string `yaml:"password,omitempty" json:"password,omitempty"`
} `yaml:"auth,omitempty" json:"auth,omitempty"`
}