Expose logs when container started but exited with errors (#527)
* expose the error message of start failure * disable container auto-remove, only remove it when it fails to start * call /containers/id/logs endpoint to get the logs
This commit is contained in:
@@ -1,14 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// DisableContainerAutoremove to tell if to run container with --rm
|
||||
var DisableContainerAutoremove = false
|
||||
|
||||
func init() {
|
||||
if os.Getenv("DISABLE_CONTAINER_AUTOREMOVE") == "true" {
|
||||
DisableContainerAutoremove = true
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var _ = func() (_ struct{}) {
|
||||
os.Setenv("DISABLE_CONTAINER_AUTOREMOVE", "true")
|
||||
return
|
||||
}()
|
||||
|
||||
func TestEnvLoad(t *testing.T) {
|
||||
if !DisableContainerAutoremove {
|
||||
t.Fatalf("should be true after set")
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/google/go-querystring/query"
|
||||
"github.com/google/uuid"
|
||||
fxConfig "github.com/metrue/fx/config"
|
||||
containerruntimes "github.com/metrue/fx/container_runtimes"
|
||||
"github.com/metrue/fx/types"
|
||||
"github.com/metrue/fx/utils"
|
||||
@@ -425,7 +424,7 @@ func (api *API) StartContainer(ctx context.Context, name string, image string, b
|
||||
}
|
||||
|
||||
hostConfig := &container.HostConfig{
|
||||
AutoRemove: !fxConfig.DisableContainerAutoremove,
|
||||
AutoRemove: false,
|
||||
PortBindings: portMap,
|
||||
}
|
||||
|
||||
@@ -475,11 +474,82 @@ func (api *API) StartContainer(ctx context.Context, name string, image string, b
|
||||
return errors.New(msg)
|
||||
}
|
||||
|
||||
if _, err = api.inspect(createRes.ID); err != nil {
|
||||
msg := fmt.Sprintf("inspect container %s error", name)
|
||||
return errors.Wrap(err, msg)
|
||||
// wait seconds for container starting
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
info, err := api.inspect(createRes.ID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to inspect container "+createRes.ID)
|
||||
}
|
||||
|
||||
if !info.State.Running {
|
||||
logs, err := api.logs(createRes.ID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get logs of container "+createRes.ID)
|
||||
}
|
||||
|
||||
if err := api.RemoveContainer(createRes.ID); err != nil {
|
||||
msg := fmt.Sprintf("remove container %s failed, and container started with logs: %s", createRes.ID, string(logs))
|
||||
return errors.Wrap(err, msg)
|
||||
}
|
||||
|
||||
return fmt.Errorf("container start failure: %s", logs)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (api *API) logs(id string) ([]byte, error) {
|
||||
query := url.Values{}
|
||||
query.Set("stdout", "true")
|
||||
query.Set("stderr", "true")
|
||||
path := fmt.Sprintf("/containers/%s/logs?%s", id, query.Encode())
|
||||
url := fmt.Sprintf("%s%s", api.endpoint, path)
|
||||
request, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client := &http.Client{Timeout: 20 * time.Second}
|
||||
resp, err := client.Do(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == 200 {
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
return nil, fmt.Errorf("get logs of container %s failed: %d", id, resp.StatusCode)
|
||||
}
|
||||
|
||||
// RemoveContainer remove a container
|
||||
func (api *API) RemoveContainer(id string) error {
|
||||
query := url.Values{}
|
||||
query.Set("v", "true")
|
||||
path := fmt.Sprintf("/containers/%s?%s", id, query.Encode())
|
||||
url := fmt.Sprintf("%s%s", api.endpoint, path)
|
||||
request, err := http.NewRequest("DELETE", url, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client := &http.Client{Timeout: 20 * time.Second}
|
||||
resp, err := client.Do(request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
output, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "read response body of remove container request failed")
|
||||
}
|
||||
if resp.StatusCode != 204 {
|
||||
return fmt.Errorf("could not remove container %s: %s", id, string(output))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/google/uuid"
|
||||
fxConfig "github.com/metrue/fx/config"
|
||||
containerruntimes "github.com/metrue/fx/container_runtimes"
|
||||
"github.com/metrue/fx/types"
|
||||
"github.com/metrue/fx/utils"
|
||||
@@ -162,7 +161,7 @@ func (d *Docker) StartContainer(ctx context.Context, name string, image string,
|
||||
}
|
||||
|
||||
hostConfig := &dockerTypesContainer.HostConfig{
|
||||
AutoRemove: !fxConfig.DisableContainerAutoremove,
|
||||
AutoRemove: false,
|
||||
PortBindings: portMap,
|
||||
}
|
||||
resp, err := d.ContainerCreate(ctx, config, hostConfig, nil, name)
|
||||
|
||||
Reference in New Issue
Block a user