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

Conversation

Bubblemelon
Copy link
Contributor

@Bubblemelon Bubblemelon commented Jun 19, 2018

This references issue #2444.

The ./ignition build works when it is sent to a Packet instance.

However, has trouble logging via ssh when an image with the changes in igntion is booted onto an instance. I roughly know what is causing this problem.

The commits in the PR are numerous but I will eventually squash it into one commit after removing the debug prints some from logger.Info() and fmt.Println(). So I'm ignoring the tailor check for now, until I
have one squashed commit.

@coreosbot
Copy link
Contributor

Can one of the admins verify this patch?

@Bubblemelon
Copy link
Contributor Author

On a Packet Instance, run ./igntion -oem file -clear-cache -log-to-stdout -stage files in the directory where the binary was scp-ed into.

To supply the instance with an ignition config file, use $IGNITION_CONFIG_FILE to lead to the path of the file, i.e. /path/to/config.ign

The Timeline will an error message if the config is invalid. But will not show any messages if no config was given or was correctly booted with. Unless that is desired, but as far as my discussions went with Derek, only show the message on the Timeline if there is an error i.e. when ignition fails, all else remains the same.

output packet

@Bubblemelon Bubblemelon changed the title Status Report for Packet Instance WIP: Status Report for Packet Instance Jun 19, 2018
@Bubblemelon Bubblemelon force-pushed the status-report-packetcloud branch 2 times, most recently from e07d915 to 69d0703 Compare June 26, 2018 22:54
// r := report.Report{
// Entries: []Entry{
// {
// Message: "Ignition Failed",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ajeddeloh Is it necessary to use report.Report?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the report is just for config validation. We should send the actual error to the cloud though (which might be from a report.Report{}, but might not be as well)

@Bubblemelon
Copy link
Contributor Author

Link to blog about metadata.packet.net : https://help.packet.net/technical/hacking/user-state

@Bubblemelon Bubblemelon changed the title WIP: Status Report for Packet Instance Status Report for Packet Instance Jun 28, 2018
@Bubblemelon
Copy link
Contributor Author

@ajeddeloh I've added a more detailed error/status message that posts to the Packet timeline (to the user).

After looking at Kola, it looks like it's using an external package from Packet to form an instance, it's not a simple rest api call if I understand correctly. So let me know how you would like me to tell Packet that ignition failed, right now its a simple os.Exit(1) in /internal/main.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 {
func (e Engine) Run(stageName string) (bool, string) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should just return an error. It should have from the start.

}

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)))
return stages.Get(stageName).Create(e.Logger, e.Root, *e.Fetcher).Run(config.Append(baseConfig, config.Append(systemBaseConfig, cfg))), "Valid Ignition Config"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The stages should also return an error (this is some long overdue refactoring)

internal/main.go Outdated
if !engine.Run(flags.stage.String()) {
if !ignSuccess {
// ignition fails
logger.Info("Ignition Fails")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: s/Fails/Failed

internal/main.go Outdated
os.Exit(1)
} else {
logger.Info("Ignition Passes")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need to log sucess, but I'm not that opinionated. If you do want to, maybe "Ignition finished successfully".

@@ -199,6 +199,7 @@ 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) {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spurious newline

Message: "Ignition: valid config",
}

// Marshall Message
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this part is common to both branches of the if and can be broken out

@@ -61,6 +62,21 @@ 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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there shouldn't be packet specific stuff in oem/oem. Instead the packet version should be registered below.

@@ -40,6 +40,7 @@ type Config struct {
name string
fetch providers.FuncFetchConfig
newFetcher providers.FuncNewFetcher
status providers.FuncStatusFetcher
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe statusFunc?

@@ -219,9 +219,13 @@ 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 {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spurious newline

if f.client == nil {
logger := log.New(true)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this addition?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

f enters FetchfromHTTP as nil, for Looger and client

Copy link
Contributor

@arithx arithx Jun 29, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than instantiating the logger here would it work to change the Fetcher instantiation here to call rf, err := c.NewFetcherFunc()(log.New(true)?

fmt.Println("Error: ", err)
}

type Metadata struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:
Rather than declaring this type, which is only used once inline, can you not just do this when assinging the metadata variable:

metadata := struct {
    PhoneHomeURL string `json:"phone_home_url"`
}{}

If you do want to keep the type I think it should be moved outside of the function.

fmt.Println("Error: ", err)
}
postReq.Header.Set("Content-Type", "application/json")
client := &http.Client{}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ajeddeloh: would it make sense to make a GetHttpClient function of the Fetcher object and/or make a Post call? That way we can get clients with the correct Transport & http.Client customizations easier?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmmm actually I don't think so. Ignition could have failed because of wonky timeouts/CAs. We shouldn't need those customizations here, especially since this is packet specific.

}

if err != nil {
fmt.Println("Error: ", err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drop fmt calls. If you need to log a message use log

@Bubblemelon Bubblemelon force-pushed the status-report-packetcloud branch 4 times, most recently from b730e32 to e1b840a Compare June 30, 2018 01:16
arithx
arithx previously requested changes Jul 2, 2018
@@ -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") }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this being changed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When running ignition and setting -oem as packet, ignition doesn't seem to take the exported env SYSTEM_CONFIG_DIRso I hard coded the path for the mean time.

if e.Fetcher == nil || e.Logger == nil {
fmt.Fprintf(os.Stderr, "engine incorrectly configured\n")
return false
err := errors.ErrEngineConfiguration
return false, err
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can just directly return errors.ErrEngineConfiguration rather than using a temporary variable.

}

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)))
return stages.Get(stageName).Create(e.Logger, e.Root, *e.Fetcher).Run(config.Append(baseConfig, config.Append(systemBaseConfig, cfg))), err
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you tested this change in a situation where you'd receive a ErrNoProvider?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In internal/providers/system/system.go (line 55) when a config is not given at the default path as specified by $SYSTEM_CONFIG_DIR, ErrNoProvider will be called for example, and will show: Ignition error: config provider was not online

// Status takes b as whether ignition passes (true) or fails
// msg is the error or success message
func (c Config) Status(f resource.Fetcher, status error) error {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like your editor is sneaking in a lot of random newlines.

@@ -18,7 +18,12 @@
package packet

import (
"bytes"
"encoding/json"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drop this extra newline

data, err := f.FetchToBuffer(metadataUrl, resource.FetchOptions{
Headers: nil,
})
if data == nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a possible case where a nil is returned for both data & err? If so then this function will appear to pass despite not working. Also this check should probably be combined with the err != nil check.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Data Err return value
!nil !nil err
!nil nil do nothing
nil !nil err
nil nil ErrValidFetchEmptyData

for this:

	if err != nil {
		return err
	}
	if data == nil {
		return errors.ErrValidFetchEmptyData
	}

internal/providers/packet/packet.go ( line 72 )

if f.client == nil {
f.newHttpClient()
}
// loging := log.New(true)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drop the commented code.

@Bubblemelon
Copy link
Contributor Author

Bubblemelon commented Jul 10, 2018

Here's my .sh file to build and test ignition Packet changes on the SDK if anyone wants this.

Also please ^ critique ! 😄

@@ -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) (bool, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bool here is redundant and can be dropped completely. It's always true when the error is nil and false when the error is non-nil.

}

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)))
return stages.Get(stageName).Create(e.Logger, e.Root, *e.Fetcher).Run(config.Append(baseConfig, config.Append(systemBaseConfig, cfg))), err
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The stage should be changed to return an error instead of a bool and that should be returned here.

internal/main.go Outdated
} else {
logger.Info("ERRORRRRR IS NIL")
}
statusErr := engine.OEMConfig.Status(*engine.Fetcher, err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor nit: these twoi lines can be combined into something like:

if statusErr := engine.OEM... ; statusErr != nil {

// msg is the error or success message
func (c Config) Status(f resource.Fetcher, statusErr error) error {
if c.status != nil {
err := c.status(f, statusErr)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can just return this here and let the caller log it.

@@ -241,7 +246,6 @@ func (f *Fetcher) FetchFromHTTP(u url.URL, dest *os.File, opts FetchOptions) err
default:
return ErrFailed
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dropped this

State: "failed",
Message: message,
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra newline


} else {

err = postMessage(errMsg, f, "Ignition: valid config", postMessageURL)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/Ignition: valid config/Ignition stage exited successful
You can have a valid config and still fail.

postMessageURL := phonehomeURL + "/events"
if errMsg != nil {

err = postMessage(errMsg, f, "Ignition error: "+errMsg.Error(), postMessageURL)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably want to include the stage (e.g. files/disks)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the stage name?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah

@Bubblemelon
Copy link
Contributor Author

The script that terminates Packet instances running for more than a day doesn't seem to be working.

@ajeddeloh I believe you've mentioned of such script before.

@arithx
Copy link
Contributor

arithx commented Jul 12, 2018

@Bubblemelon are you referring to the gc jobs? If so was the instance spawned by kola (or does it have a mantle tag), because only machines with that tag are automatically cleaned up.

@@ -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/") }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be changed back before merging

@@ -16,6 +16,7 @@ package exec

import (
"encoding/json"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove extra newline

}

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.Crit("%s did not pass", stageName)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would actually use fmt.Fprintf(os.Stderr, ... since one of the failure modes is that Logger could be nil.

nit: "Stage %s failed" instead of "%s did not pass".

internal/main.go Outdated
if !engine.Run(flags.stage.String()) {
if err = engine.Run(flags.stage.String()); err != nil {
logger.Crit("Ignition failed")
if err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is always true

internal/main.go Outdated
if err != nil {
logger.Err(err.Error())
}
if statusErr := engine.OEMConfig.Status(flags.stage.String(), *engine.Fetcher, err); statusErr != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be good to break this common code out of the if.

@@ -163,3 +165,8 @@ func (s stage) waitOnDevicesAndCreateAliases(devs []string, ctxt string) error {

return nil
}

func newDiskStageError(msg string, err error) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would just use fmt.Errorf in the call sites.

// POST Message to phonehome IP
postMessageURL := phonehomeURL + "/events"

err = postMessage(stageName, errMsg, f, postMessageURL)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just return postMessage(...)

}

// postMessage makes a post request with the supplied message to the url
func postMessage(stageName string, e error, f resource.Fetcher, url string) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think f is unused.

@Bubblemelon Bubblemelon force-pushed the status-report-packetcloud branch 2 times, most recently from 17210d0 to f6a5001 Compare July 17, 2018 20:56
Copy link
Contributor

@ajeddeloh ajeddeloh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly lgtm, except a few nits. Can you rework the commits to have something like:

  • rework run to return an error
  • add the framework for reporting statues
  • add the packet implementation

return err
}
e.Logger.Info("passed")
return err
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: return nil here (since err must be nil) to make it clear that this is the sucessful case

internal/main.go Outdated
os.Exit(1)
} else {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Don't need the else since the other branch exits

err = json.Unmarshal(data, &metadata)
if err != nil {
return err

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: extra newline

@Bubblemelon Bubblemelon force-pushed the status-report-packetcloud branch 2 times, most recently from 777cb1c to e066080 Compare July 23, 2018 21:54
@Bubblemelon Bubblemelon force-pushed the status-report-packetcloud branch 2 times, most recently from 4895b7a to b7856d2 Compare July 30, 2018 23:57
@Bubblemelon Bubblemelon dismissed arithx’s stale review July 31, 2018 23:16

Covered suggested/requested items and had gone over them with Andrew.

@coreos coreos deleted a comment from arithx Jul 31, 2018
Copy link
Contributor

@ajeddeloh ajeddeloh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two minor nits: some wording in a message and an extra newline, other than that LGTM

fmt.Fprintf(os.Stderr, "%s failed", stageName)
return err
}
e.Logger.Info("passed")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add stage name here like in the failed message.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay fixed the new line in packet.go on line 131 and changed "passed" to "%s passed", stageName see here.

fmt.Println("Error: ", err)
}
postReq.Header.Set("Content-Type", "application/json")
client := &http.Client{}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmmm actually I don't think so. Ignition could have failed because of wonky timeouts/CAs. We shouldn't need those customizations here, especially since this is packet specific.

Run() returns an error instead of a bool.
main.go checks if stage Run() is successful.

oem.go implements the a Status() function
that calls the oem specific fucntion to
report the ignition status.

FetchFromHTTP in url.go will check if f.client is nill.
packet.go POST igntion status
messages to Packet.net via Packet's api url.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants