diff --git a/pkg/app/version.go b/pkg/app/version.go index 114ea313e..52071662d 100644 --- a/pkg/app/version.go +++ b/pkg/app/version.go @@ -33,12 +33,20 @@ var CommandVersion = &cli.Command{ Value: false, Aliases: []string{"s"}, }, + &cli.BoolFlag{ + Name: "detail", + Usage: "Print details about the envd environment", + Value: false, + Aliases: []string{"d"}, + }, }, } func printVersion(ctx *cli.Context) error { short := ctx.Bool("short") + detail := ctx.Bool("detail") ver := version.GetVersion() + detailVer, err := version.GetDetailedVersion(ctx) fmt.Printf("envd: %s\n", ver) if short { return nil @@ -52,5 +60,19 @@ func printVersion(ctx *cli.Context) error { fmt.Printf(" GoVersion: %s\n", ver.GoVersion) fmt.Printf(" Compiler: %s\n", ver.Compiler) fmt.Printf(" Platform: %s\n", ver.Platform) + if detail { + if err != nil { + fmt.Printf("Error in getting details from Docker Server: %s\n", err) + } else { + fmt.Printf(" OSType: %s\n", detailVer.OSType) + if detailVer.OSVersion != "" { + fmt.Printf(" OSVersion: %s\n", detailVer.OSVersion) + } + fmt.Printf(" KernelVersion: %s\n", detailVer.KernelVersion) + fmt.Printf(" DockerHostVersion: %s\n", detailVer.DockerVersion) + fmt.Printf(" ContainerRuntimes: %s\n", detailVer.ContainerRuntimes) + fmt.Printf(" DefaultRuntime: %s\n", detailVer.DefaultRuntime) + } + } return nil } diff --git a/pkg/docker/docker.go b/pkg/docker/docker.go index 6755a040c..725a678da 100644 --- a/pkg/docker/docker.go +++ b/pkg/docker/docker.go @@ -71,6 +71,8 @@ type Client interface { ListImage(ctx context.Context) ([]types.ImageSummary, error) GetImage(ctx context.Context, image string) (types.ImageSummary, error) + GetInfo(ctx context.Context) (types.Info, error) + // GPUEnabled returns true if nvidia container runtime exists in docker daemon. GPUEnabled(ctx context.Context) (bool, error) } @@ -215,6 +217,10 @@ func (c generalClient) GetContainer(ctx context.Context, cname string) (types.Co return c.ContainerInspect(ctx, cname) } +func (c generalClient) GetInfo(ctx context.Context) (types.Info, error) { + return c.Info(ctx) +} + func (c generalClient) Destroy(ctx context.Context, name string) (string, error) { logger := logrus.WithField("container", name) // Refer to https://docs.docker.com/engine/reference/commandline/container_kill/ diff --git a/pkg/envd/engine.go b/pkg/envd/engine.go index 1f27c42ce..049f308b0 100644 --- a/pkg/envd/engine.go +++ b/pkg/envd/engine.go @@ -30,6 +30,7 @@ type Engine interface { ResumeEnvironment(ctx context.Context, env string) (string, error) ListEnvironment(ctx context.Context) ([]types.EnvdEnvironment, error) ListEnvDependency(ctx context.Context, env string) (*types.Dependency, error) + GetInfo(ctx context.Context) (*types.EnvdInfo, error) } type generalEngine struct { @@ -139,3 +140,14 @@ func (e generalEngine) ListEnvDependency( } return dep, nil } + +func (e generalEngine) GetInfo(ctx context.Context) (*types.EnvdInfo, error) { + info, err := e.dockerCli.GetInfo(ctx) + if err != nil { + return nil, errors.Wrap(err, "failed to get docker client info") + } + return &types.EnvdInfo{ + Info: info, + }, nil + +} diff --git a/pkg/types/envd.go b/pkg/types/envd.go index c27d873e3..422d166a1 100644 --- a/pkg/types/envd.go +++ b/pkg/types/envd.go @@ -42,6 +42,10 @@ type EnvdManifest struct { Dependency `json:",inline,omitempty"` } +type EnvdInfo struct { + types.Info +} + type Dependency struct { APTPackages []string `json:"apt_packages,omitempty"` PyPIPackages []string `json:"pypi_packages,omitempty"` diff --git a/pkg/version/version.go b/pkg/version/version.go index 51324c59f..5b61624e3 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -24,6 +24,11 @@ import ( "runtime" "strings" "sync" + + "github.com/cockroachdb/errors" + "github.com/tensorchord/envd/pkg/envd" + "github.com/tensorchord/envd/pkg/types" + "github.com/urfave/cli/v2" ) var ( @@ -53,12 +58,22 @@ type Version struct { Platform string } +type DetailedVersion struct { + OSVersion string + OSType string + KernelVersion string + Architecture string + DockerVersion string + ContainerRuntimes string + DefaultRuntime string +} + func (v Version) String() string { return v.Version } -// GetVersion returns the version information -func GetVersion() Version { +// Get Envd version information +func GetEnvdVersion() string { var versionStr string if gitCommit != "" && gitTag != "" && gitTreeState == "clean" { @@ -82,8 +97,22 @@ func GetVersion() Version { versionStr += "+unknown" } } + return versionStr +} + +func GetRuntimes(info *types.EnvdInfo) string { + runtimesMap := info.Runtimes + keys := make([]string, 0, len(runtimesMap)) + for k := range runtimesMap { + keys = append(keys, k) + } + return "[" + strings.Join(keys, ",") + "]" +} + +// GetVersion returns the version information +func GetVersion() Version { return Version{ - Version: versionStr, + Version: GetEnvdVersion(), BuildDate: buildDate, GitCommit: gitCommit, GitTag: gitTag, @@ -94,6 +123,32 @@ func GetVersion() Version { } } +func GetDetailedVersion(clicontext *cli.Context) (DetailedVersion, error) { + engine, err := envd.New(clicontext.Context) + if err != nil { + return DetailedVersion{}, errors.Wrap( + err, "failed to create engine for docker server", + ) + } + + info, err := engine.GetInfo(clicontext.Context) + if err != nil { + return DetailedVersion{}, errors.Wrap( + err, "failed to get detailed version info from docker server", + ) + } + + return DetailedVersion{ + OSVersion: info.OSVersion, + OSType: info.OSType, + KernelVersion: info.KernelVersion, + DockerVersion: info.ServerVersion, + Architecture: info.Architecture, + DefaultRuntime: info.DefaultRuntime, + ContainerRuntimes: GetRuntimes(info), + }, nil +} + var ( reRelease *regexp.Regexp reDev *regexp.Regexp