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

Status Report for Packet Instance #566

Merged
merged 3 commits into from
Aug 1, 2018
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
11 changes: 6 additions & 5 deletions config/shared/errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,12 @@ 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")
)

// NewNoInstallSectionError produces an error indicating the given unit, named
Expand Down
18 changes: 12 additions & 6 deletions internal/exec/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
func (e Engine) Run(stageName string) error {
if e.Fetcher == nil || e.Logger == nil {
fmt.Fprintf(os.Stderr, "engine incorrectly configured\n")
return false
return errors.ErrEngineConfiguration
}
baseConfig := types.Config{
Ignition: types.Ignition{Version: types.MaxVersion.String()},
Expand All @@ -72,7 +72,7 @@ func (e Engine) Run(stageName string) bool {
e.logReport(r)
if err != nil && err != providers.ErrNoProvider {
e.Logger.Crit("failed to acquire system base config: %v", err)
return false
return err
}

cfg, err := e.acquireConfig()
Expand All @@ -84,17 +84,23 @@ func (e Engine) Run(stageName string) bool {
e.logReport(r)
if err != nil && err != providers.ErrNoProvider {
e.Logger.Crit("failed to acquire default config: %v", err)
return false
return err
}
default:
e.Logger.Crit("failed to acquire config: %v", err)
return false
return 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)))
if err = stages.Get(stageName).Create(e.Logger, e.Root, *e.Fetcher).Run(config.Append(baseConfig, config.Append(systemBaseConfig, cfg))); err != nil {
// e.Logger could be nil
fmt.Fprintf(os.Stderr, "%s failed", stageName)
return err
}
e.Logger.Info("%s passed", stageName)
return nil
}

// acquireConfig returns the configuration, first checking a local cache
Expand Down
18 changes: 7 additions & 11 deletions internal/exec/stages/disks/disks.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,29 +65,26 @@ func (stage) Name() string {
return name
}

func (s stage) Run(config types.Config) bool {
func (s stage) Run(config types.Config) error {
// Interacting with disks/partitions/raids/filesystems in general can cause
// udev races. If we do not need to do anything, we also do not need to
// do the udevadm settle and can just return here.
if len(config.Storage.Disks) == 0 &&
len(config.Storage.Raid) == 0 &&
len(config.Storage.Filesystems) == 0 {
return true
return nil
}

if err := s.createPartitions(config); err != nil {
s.Logger.Crit("create partitions failed: %v", err)
return false
return fmt.Errorf("create partitions failed: %v", err)
}

if err := s.createRaids(config); err != nil {
s.Logger.Crit("failed to create raids: %v", err)
return false
return fmt.Errorf("failed to create raids: %v", err)
}

if err := s.createFilesystems(config); err != nil {
s.Logger.Crit("failed to create filesystems: %v", err)
return false
return fmt.Errorf("failed to create filesystems: %v", err)
}

// udevd registers an IN_CLOSE_WRITE inotify watch on block device
Expand Down Expand Up @@ -118,11 +115,10 @@ func (s stage) Run(config types.Config) bool {
exec.Command(distro.UdevadmCmd(), "settle"),
"waiting for udev to settle",
); err != nil {
s.Logger.Crit("udevadm settle failed: %v", err)
return false
return fmt.Errorf("udevadm settle failed: %v", err)
}

return true
return nil
}

// waitOnDevices waits for the devices enumerated in devs as a logged operation
Expand Down
14 changes: 6 additions & 8 deletions internal/exec/stages/files/files.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package files

import (
"errors"
"fmt"

"github.com/coreos/ignition/internal/config/types"
"github.com/coreos/ignition/internal/exec/stages"
Expand Down Expand Up @@ -60,21 +61,18 @@ func (stage) Name() string {
return name
}

func (s stage) Run(config types.Config) bool {
func (s stage) Run(config types.Config) error {
if err := s.createPasswd(config); err != nil {
s.Logger.Crit("failed to create users/groups: %v", err)
return false
return fmt.Errorf("failed to create users/groups: %v", err)
}

if err := s.createFilesystemsEntries(config); err != nil {
s.Logger.Crit("failed to create files: %v", err)
return false
return fmt.Errorf("failed to create files: %v", err)
}

if err := s.createUnits(config); err != nil {
s.Logger.Crit("failed to create units: %v", err)
return false
return fmt.Errorf("failed to create units: %v", err)
}

return true
return nil
}
2 changes: 1 addition & 1 deletion internal/exec/stages/stages.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (

// Stage is responsible for actually executing a stage of the configuration.
type Stage interface {
Run(config types.Config) bool
Run(config types.Config) error
Name() string
}

Expand Down
8 changes: 7 additions & 1 deletion internal/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,13 @@ func main() {
Fetcher: &fetcher,
}

if !engine.Run(flags.stage.String()) {
err = engine.Run(flags.stage.String())
if statusErr := engine.OEMConfig.Status(flags.stage.String(), *engine.Fetcher, err); statusErr != nil {
logger.Err("POST Status error: ", statusErr.Error())
}
if err != nil {
logger.Crit("Ignition failed: %v", err.Error())
os.Exit(1)
}
logger.Info("Ignition finished successfully")
}
14 changes: 12 additions & 2 deletions internal/oem/oem.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type Config struct {
name string
fetch providers.FuncFetchConfig
newFetcher providers.FuncNewFetcher
status providers.FuncPostStatus
}

func (c Config) Name() string {
Expand All @@ -61,6 +62,14 @@ func (c Config) NewFetcherFunc() providers.FuncNewFetcher {
}
}

// Status takes a Fetcher and the error from Run (from engine)
func (c Config) Status(stageName string, f resource.Fetcher, statusErr error) error {
if c.status != nil {
return c.status(stageName, f, statusErr)
}
return nil
}

var configs = registry.Create("oem configs")

func init() {
Expand Down Expand Up @@ -110,8 +119,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",
Expand Down
85 changes: 85 additions & 0 deletions internal/providers/packet/packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,23 @@
package packet

import (
"bytes"
"encoding/json"
"errors"
"net/http"
"net/url"
"strings"

"github.com/coreos/ignition/config/validate/report"
"github.com/coreos/ignition/internal/config/types"
"github.com/coreos/ignition/internal/providers/util"
"github.com/coreos/ignition/internal/resource"
)

var (
ErrValidFetchEmptyData = errors.New("fetch successful but fetched data empty")
)

var (
userdataUrl = url.URL{
Scheme: "https",
Expand All @@ -34,6 +43,14 @@ var (
}
)

var (
metadataUrl = url.URL{
Scheme: "https",
Host: "metadata.packet.net",
Path: "metadata",
}
)

func FetchConfig(f resource.Fetcher) (types.Config, report.Report, error) {
// Packet's metadata service returns "Not Acceptable" when queried
// with the default Accept header.
Expand All @@ -48,3 +65,71 @@ func FetchConfig(f resource.Fetcher) (types.Config, report.Report, error) {

return util.ParseConfig(f.Logger, data)
}

// PostStatus posts a message that will show on the Packet Instance Timeline
func PostStatus(stageName string, 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{
Headers: nil,
})
if err != nil {
return err
}
if data == nil {
return ErrValidFetchEmptyData
}
metadata := struct {
PhoneHomeURL string `json:"phone_home_url"`
}{}
err = json.Unmarshal(data, &metadata)
if err != nil {
return err
}
phonehomeURL := metadata.PhoneHomeURL
// to get phonehome IPv4
phonehomeURL = strings.TrimSuffix(phonehomeURL, "/phone-home")
// POST Message to phonehome IP
postMessageURL := phonehomeURL + "/events"

return postMessage(stageName, errMsg, postMessageURL)
}

// postMessage makes a post request with the supplied message to the url
func postMessage(stageName string, e error, url string) error {

stageName = "[" + stageName + "]"

type mStruct struct {
State string `json:"state"`
Message string `json:"message"`
}
m := mStruct{}
if e != nil {
m = mStruct{
State: "failed",
Message: stageName + " Ignition error: " + e.Error(),
}
} else {
m = mStruct{
State: "succeeded",
Message: stageName + " Ignition status: finished successfully",
}
}
messageJSON, err := json.Marshal(m)
if err != nil {
return err
}
postReq, err := http.NewRequest("POST", url, bytes.NewBuffer(messageJSON))
if err != nil {
return err
}
postReq.Header.Set("Content-Type", "application/json")
client := &http.Client{}
respPost, err := client.Do(postReq)
if err != nil {
return err
}
defer respPost.Body.Close()
return err
}
1 change: 1 addition & 0 deletions internal/providers/providers.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ var (

type FuncFetchConfig func(f resource.Fetcher) (types.Config, report.Report, error)
type FuncNewFetcher func(logger *log.Logger) (resource.Fetcher, error)
type FuncPostStatus func(stageName string, f resource.Fetcher, e error) error
5 changes: 5 additions & 0 deletions internal/resource/url.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,9 +219,14 @@ 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
f.newHttpClient()
}

dataReader, status, ctxCancel, err := f.client.getReaderWithHeader(u.String(), opts.Headers)
if ctxCancel != nil {
// whatever context getReaderWithHeader created for the request should
Expand Down