From 70c3c0ef630804828c19f80e07b8be07f0aedb51 Mon Sep 17 00:00:00 2001 From: bubblemelon <12985181+Bubblemelon@users.noreply.github.com> Date: Fri, 29 Jun 2018 17:46:43 -0700 Subject: [PATCH] packet: reports status to packet api Posts state and status of ignition to metadata.packet.net api, and engine.Run() now returns an error. --- config/shared/errors/errors.go | 12 ++-- internal/distro/distro.go | 2 +- internal/exec/engine.go | 12 ++-- internal/main.go | 24 +++++-- internal/oem/oem.go | 25 ++++--- internal/providers/packet/packet.go | 108 ++++++++++++---------------- internal/providers/providers.go | 2 +- internal/resource/http.go | 1 - internal/resource/url.go | 4 +- 9 files changed, 92 insertions(+), 98 deletions(-) diff --git a/config/shared/errors/errors.go b/config/shared/errors/errors.go index f2d1ddf286..087181a0f8 100644 --- a/config/shared/errors/errors.go +++ b/config/shared/errors/errors.go @@ -85,11 +85,13 @@ var ( ErrInvalidNetworkdDropinExt = errors.New("invalid networkd drop-in extension") // Misc errors - ErrInvalidScheme = errors.New("invalid url scheme") - ErrInvalidUrl = errors.New("unable to parse url") - ErrHashMalformed = errors.New("malformed hash specifier") - ErrHashWrongSize = errors.New("incorrect size for hash sum") - ErrHashUnrecognized = errors.New("unrecognized hash function") + ErrInvalidScheme = errors.New("invalid url scheme") + ErrInvalidUrl = errors.New("unable to parse url") + ErrHashMalformed = errors.New("malformed hash specifier") + ErrHashWrongSize = errors.New("incorrect size for hash sum") + ErrHashUnrecognized = errors.New("unrecognized hash function") + ErrEngineConfiguration = errors.New("engine incorrectly configured") + ErrValidFetchEmptyData = errors.New("fetch successful but fetched data empty") ) // NewNoInstallSectionError produces an error indicating the given unit, named diff --git a/internal/distro/distro.go b/internal/distro/distro.go index d46135e180..1bc01b6958 100644 --- a/internal/distro/distro.go +++ b/internal/distro/distro.go @@ -59,7 +59,7 @@ func DiskByPartUUIDDir() string { return diskByPartUUIDDir } func OEMDevicePath() string { return fromEnv("OEM_DEVICE", oemDevicePath) } func KernelCmdlinePath() string { return kernelCmdlinePath } -func SystemConfigDir() string { return fromEnv("SYSTEM_CONFIG_DIR", systemConfigDir) } +func SystemConfigDir() string { return fromEnv("SYSTEM_CONFIG_DIR", "/home/core/") } func OEMLookasideDir() string { return fromEnv("OEM_LOOKASIDE_DIR", oemLookasideDir) } func ChrootCmd() string { return chrootCmd } diff --git a/internal/exec/engine.go b/internal/exec/engine.go index e72d67066e..1465a1f038 100644 --- a/internal/exec/engine.go +++ b/internal/exec/engine.go @@ -53,10 +53,10 @@ type Engine struct { // Run executes the stage of the given name. It returns true if the stage // successfully ran and false if there were any errors. -func (e Engine) Run(stageName string) (bool, string) { +func (e Engine) Run(stageName string) (bool, error) { if e.Fetcher == nil || e.Logger == nil { fmt.Fprintf(os.Stderr, "engine incorrectly configured\n") - return false, "engine incorrectly configured" + return false, errors.ErrEngineConfiguration } baseConfig := types.Config{ Ignition: types.Ignition{Version: types.MaxVersion.String()}, @@ -72,7 +72,7 @@ func (e Engine) Run(stageName string) (bool, string) { e.logReport(r) if err != nil && err != providers.ErrNoProvider { e.Logger.Crit("failed to acquire system base config: %v", err) - return false, "failed to acquire system base config: " + err.Error() + return false, err } cfg, err := e.acquireConfig() @@ -84,17 +84,17 @@ func (e Engine) Run(stageName string) (bool, string) { e.logReport(r) if err != nil && err != providers.ErrNoProvider { e.Logger.Crit("failed to acquire default config: %v", err) - return false, "failed to acquire default config: " + err.Error() + return false, err } default: e.Logger.Crit("failed to acquire config: %v", err) - return false, "failed to acquire config: " + err.Error() + return false, err } e.Logger.PushPrefix(stageName) defer e.Logger.PopPrefix() - return stages.Get(stageName).Create(e.Logger, e.Root, *e.Fetcher).Run(config.Append(baseConfig, config.Append(systemBaseConfig, cfg))), "Valid Ignition Config" + return stages.Get(stageName).Create(e.Logger, e.Root, *e.Fetcher).Run(config.Append(baseConfig, config.Append(systemBaseConfig, cfg))), err } // acquireConfig returns the configuration, first checking a local cache diff --git a/internal/main.go b/internal/main.go index 62969c4309..b257aa33e6 100644 --- a/internal/main.go +++ b/internal/main.go @@ -92,15 +92,27 @@ func main() { OEMConfig: oemConfig, Fetcher: &fetcher, } - ignSuccess, message := engine.Run(flags.stage.String()) + ignSuccess, err := engine.Run(flags.stage.String()) if !ignSuccess { - // ignition fails - logger.Info("Ignition Fails") - oemConfig.Status(ignSuccess, message) + logger.Warning("Ignition failed") + if err != nil { + logger.Err(err.Error()) + } else { + logger.Info("ERRORRRRR IS NIL") + } + statusErr := engine.OEMConfig.Status(*engine.Fetcher, err) + if statusErr != nil { + logger.Err("Status error: ", statusErr.Error()) + } os.Exit(1) + } else { - logger.Info("Ignition Passes") - oemConfig.Status(ignSuccess, message) + logger.Info("Ignition finished successfully") + // err should be nil + statusErr := engine.OEMConfig.Status(*engine.Fetcher, err) + if statusErr != nil { + logger.Err("Status error: ", statusErr.Error()) + } } } diff --git a/internal/oem/oem.go b/internal/oem/oem.go index 8ac34fe9ae..41b90bdb55 100644 --- a/internal/oem/oem.go +++ b/internal/oem/oem.go @@ -40,7 +40,7 @@ type Config struct { name string fetch providers.FuncFetchConfig newFetcher providers.FuncNewFetcher - status providers.FuncStatusFetcher + status providers.FuncPostStatus } func (c Config) Name() string { @@ -64,17 +64,15 @@ func (c Config) NewFetcherFunc() providers.FuncNewFetcher { // Status takes b as whether ignition passes (true) or fails // msg is the error or success message -func (c Config) Status(b bool, msg string) providers.FuncStatusFetcher { - rf := resource.Fetcher{} - if !b { - packet.FetchStatus(rf, msg) - - return c.status +func (c Config) Status(f resource.Fetcher, statusErr error) error { + if c.status != nil { + err := c.status(f, statusErr) + if err != nil { + f.Logger.Err("Error: ", err) + } + return err } - packet.FetchStatus(rf, msg) - - return c.status - + return nil } var configs = registry.Create("oem configs") @@ -126,8 +124,9 @@ func init() { fetch: noop.FetchConfig, }) configs.Register(Config{ - name: "packet", - fetch: packet.FetchConfig, + name: "packet", + fetch: packet.FetchConfig, + status: packet.PostStatus, }) configs.Register(Config{ name: "pxe", diff --git a/internal/providers/packet/packet.go b/internal/providers/packet/packet.go index 9540788db4..1f6a622faf 100644 --- a/internal/providers/packet/packet.go +++ b/internal/providers/packet/packet.go @@ -20,12 +20,11 @@ package packet import ( "bytes" "encoding/json" - "fmt" - "log" "net/http" "net/url" "strings" + "github.com/coreos/ignition/config/shared/errors" "github.com/coreos/ignition/config/validate/report" "github.com/coreos/ignition/internal/config/types" "github.com/coreos/ignition/internal/providers/util" @@ -41,7 +40,7 @@ var ( ) var ( - MetadataUrl = url.URL{ + metadataUrl = url.URL{ Scheme: "https", Host: "metadata.packet.net", Path: "metadata", @@ -63,101 +62,84 @@ func FetchConfig(f resource.Fetcher) (types.Config, report.Report, error) { return util.ParseConfig(f.Logger, data) } -// FetchStatus is invoked by oem.Status() when igintion fails -// POSTS if s==fail, a message to Packet's provision setup Timeline -func FetchStatus(f resource.Fetcher, s string) error { - - type Message struct { - Message string `json:"message"` - } - +// PostStatus posts a message that will show on the Packet Instance Timeline +func PostStatus(f resource.Fetcher, errMsg error) error { + f.Logger.Info("POST message to Packet Timeline") // fetch JSON from https://metadata.packet.net/metadata - data, err := f.FetchToBuffer(MetadataUrl, resource.FetchOptions{ + data, err := f.FetchToBuffer(metadataUrl, resource.FetchOptions{ Headers: nil, }) - if data == nil { - log.Fatalf("data is nil") - } - if err != nil { - fmt.Println("Error: ", err) + return err } - - type Metadata struct { - PhoneHomeURL string `json:"phone_home_url"` + if data == nil { + return errors.ErrValidFetchEmptyData } - metadata := Metadata{} - + metadata := struct { + PhoneHomeURL string `json:"phone_home_url"` + }{} err = json.Unmarshal(data, &metadata) if err != nil { - fmt.Println("Error: ", err) - } + return err + } phonehomeURL := metadata.PhoneHomeURL - // to get phonehome IP + // to get phonehome IPv4 phonehomeURL = strings.TrimSuffix(phonehomeURL, "/phone-home") - // POST Message to phonehome IP postMessageURL := phonehomeURL + "/events" + if errMsg != nil { - // Place message into report struct - if s != "Valid Ignition Config" { - m := Message{ - Message: "Ignition error: " + s, - } - // Marshall Message - messageJSON, err := json.Marshal(m) - if err != nil { - fmt.Println("Error: ", err) - } - // POST Message - err = postMessage(messageJSON, postMessageURL) + err = postMessage(errMsg, f, "Ignition error: "+errMsg.Error(), postMessageURL) if err != nil { - fmt.Println("Error: ", err) + return err } } else { - fmt.Println("IGNTION STATUS PASS") - - m := Message{ - Message: "Ignition: valid config", - } - - // Marshall Message - messageJSON, err := json.Marshal(m) - if err != nil { - fmt.Println("Error: ", err) - } - // POST Message - err = postMessage(messageJSON, postMessageURL) + err = postMessage(errMsg, f, "Ignition: valid config", postMessageURL) if err != nil { - fmt.Println("Error: ", err) + return err } } - return err } -// postMessage(): POST message in JSON to API URL -func postMessage(message []byte, url string) error { +// postMessage makes a post request with the supplied message to the url +func postMessage(e error, f resource.Fetcher, message string, url string) error { + type mStruct struct { + State string `json:"state"` + Message string `json:"message"` + } + m := mStruct{} + if e != nil { + m = mStruct{ + State: "failed", + Message: message, + } - postReq, err := http.NewRequest("POST", url, bytes.NewBuffer(message)) + } else { + m = mStruct{ + State: "succeeded", + Message: message, + } + } + messageJSON, err := json.Marshal(m) + if err != nil { + return err + } + postReq, err := http.NewRequest("POST", url, bytes.NewBuffer(messageJSON)) if err != nil { - fmt.Println("Error: ", err) + return err } postReq.Header.Set("Content-Type", "application/json") client := &http.Client{} - respPost, err := client.Do(postReq) if err != nil { - fmt.Println("Error: ", err) - panic(err) + return err } defer respPost.Body.Close() - return err - } diff --git a/internal/providers/providers.go b/internal/providers/providers.go index 73c4cb33ad..eeb87cde9c 100644 --- a/internal/providers/providers.go +++ b/internal/providers/providers.go @@ -29,4 +29,4 @@ var ( type FuncFetchConfig func(f resource.Fetcher) (types.Config, report.Report, error) type FuncNewFetcher func(logger *log.Logger) (resource.Fetcher, error) -type FuncStatusFetcher func(f resource.Fetcher, s string) (report.Report, error) +type FuncPostStatus func(f resource.Fetcher, e error) error diff --git a/internal/resource/http.go b/internal/resource/http.go index 3f3caec855..985415aa99 100644 --- a/internal/resource/http.go +++ b/internal/resource/http.go @@ -199,7 +199,6 @@ func (f *Fetcher) newHttpClient() { // code, a cancel function for the result's context, and error (if any). By // default, User-Agent is added to the header but this can be overridden. func (c HttpClient) getReaderWithHeader(url string, header http.Header) (io.ReadCloser, int, context.CancelFunc, error) { - req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, 0, nil, err diff --git a/internal/resource/url.go b/internal/resource/url.go index d9511a37ea..7af49a02c5 100644 --- a/internal/resource/url.go +++ b/internal/resource/url.go @@ -219,7 +219,8 @@ func (f *Fetcher) FetchFromTFTP(u url.URL, dest *os.File, opts FetchOptions) err // FetchFromHTTP fetches a resource from u via HTTP(S) into dest, returning an // error if one is encountered. func (f *Fetcher) FetchFromHTTP(u url.URL, dest *os.File, opts FetchOptions) error { - + // for the case when "config is not valid" + // this if necessary if not spawned through kola (e.g. Packet Dashboard) if f.client == nil { logger := log.New(true) f.Logger = &logger @@ -245,7 +246,6 @@ func (f *Fetcher) FetchFromHTTP(u url.URL, dest *os.File, opts FetchOptions) err default: return ErrFailed } - return f.decompressCopyHashAndVerify(dest, dataReader, opts) }