package compat import ( "fmt" "io/ioutil" "net/http" "os" goRuntime "runtime" "strings" "time" "github.com/containers/common/pkg/config" "github.com/containers/common/pkg/sysinfo" "github.com/containers/image/v5/pkg/sysregistriesv2" "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/api/handlers" "github.com/containers/podman/v4/pkg/api/handlers/utils" api "github.com/containers/podman/v4/pkg/api/types" "github.com/containers/podman/v4/pkg/rootless" docker "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/registry" "github.com/docker/docker/api/types/swarm" "github.com/google/uuid" log "github.com/sirupsen/logrus" ) func GetInfo(w http.ResponseWriter, r *http.Request) { // 200 ok // 500 internal runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) infoData, err := runtime.Info() if err != nil { utils.Error(w, http.StatusInternalServerError, fmt.Errorf("failed to obtain system memory info: %w", err)) return } configInfo, err := runtime.GetConfig() if err != nil { utils.Error(w, http.StatusInternalServerError, fmt.Errorf("failed to obtain runtime config: %w", err)) return } versionInfo, err := define.GetVersion() if err != nil { utils.Error(w, http.StatusInternalServerError, fmt.Errorf("failed to obtain podman versions: %w", err)) return } stateInfo := getContainersState(runtime) sysInfo := sysinfo.New(true) // FIXME: Need to expose if runtime supports Checkpointing // liveRestoreEnabled := criu.CheckForCriu() && configInfo.RuntimeSupportsCheckpoint() info := &handlers.Info{ Info: docker.Info{ Architecture: goRuntime.GOARCH, BridgeNfIP6tables: !sysInfo.BridgeNFCallIP6TablesDisabled, BridgeNfIptables: !sysInfo.BridgeNFCallIPTablesDisabled, CPUCfsPeriod: sysInfo.CPUCfsPeriod, CPUCfsQuota: sysInfo.CPUCfsQuota, CPUSet: sysInfo.Cpuset, CPUShares: sysInfo.CPUShares, CgroupDriver: configInfo.Engine.CgroupManager, ClusterAdvertise: "", ClusterStore: "", ContainerdCommit: docker.Commit{}, Containers: infoData.Store.ContainerStore.Number, ContainersPaused: stateInfo[define.ContainerStatePaused], ContainersRunning: stateInfo[define.ContainerStateRunning], ContainersStopped: stateInfo[define.ContainerStateStopped] + stateInfo[define.ContainerStateExited], Debug: log.IsLevelEnabled(log.DebugLevel), DefaultRuntime: configInfo.Engine.OCIRuntime, DockerRootDir: infoData.Store.GraphRoot, Driver: infoData.Store.GraphDriverName, DriverStatus: getGraphStatus(infoData.Store.GraphStatus), ExperimentalBuild: true, GenericResources: nil, HTTPProxy: getEnv("http_proxy"), HTTPSProxy: getEnv("https_proxy"), ID: uuid.New().String(), IPv4Forwarding: !sysInfo.IPv4ForwardingDisabled, Images: infoData.Store.ImageStore.Number, IndexServerAddress: "", InitBinary: "", InitCommit: docker.Commit{}, Isolation: "", KernelMemoryTCP: false, KernelVersion: infoData.Host.Kernel, Labels: nil, LiveRestoreEnabled: false, LoggingDriver: "", MemTotal: infoData.Host.MemTotal, MemoryLimit: sysInfo.MemoryLimit, NCPU: goRuntime.NumCPU(), NEventsListener: 0, NFd: getFdCount(), NGoroutines: goRuntime.NumGoroutine(), Name: infoData.Host.Hostname, NoProxy: getEnv("no_proxy"), OSType: goRuntime.GOOS, OSVersion: infoData.Host.Distribution.Version, OomKillDisable: sysInfo.OomKillDisable, OperatingSystem: infoData.Host.Distribution.Distribution, PidsLimit: sysInfo.PidsLimit, Plugins: docker.PluginsInfo{ Volume: infoData.Plugins.Volume, Network: infoData.Plugins.Network, Log: infoData.Plugins.Log, }, ProductLicense: "Apache-2.0", RegistryConfig: getServiceConfig(runtime), RuncCommit: docker.Commit{}, Runtimes: getRuntimes(configInfo), SecurityOptions: getSecOpts(sysInfo), ServerVersion: versionInfo.Version, SwapLimit: sysInfo.SwapLimit, Swarm: swarm.Info{ LocalNodeState: swarm.LocalNodeStateInactive, }, SystemStatus: nil, SystemTime: time.Now().Format(time.RFC3339Nano), Warnings: []string{}, }, BuildahVersion: infoData.Host.BuildahVersion, CPURealtimePeriod: sysInfo.CPURealtimePeriod, CPURealtimeRuntime: sysInfo.CPURealtimeRuntime, CgroupVersion: strings.TrimPrefix(infoData.Host.CgroupsVersion, "v"), Rootless: rootless.IsRootless(), SwapFree: infoData.Host.SwapFree, SwapTotal: infoData.Host.SwapTotal, Uptime: infoData.Host.Uptime, } utils.WriteResponse(w, http.StatusOK, info) } func getServiceConfig(runtime *libpod.Runtime) *registry.ServiceConfig { var indexConfs map[string]*registry.IndexInfo regs, err := sysregistriesv2.GetRegistries(runtime.SystemContext()) if err == nil { indexConfs = make(map[string]*registry.IndexInfo, len(regs)) for _, reg := range regs { mirrors := make([]string, len(reg.Mirrors)) for i, mirror := range reg.Mirrors { mirrors[i] = mirror.Location } indexConfs[reg.Prefix] = ®istry.IndexInfo{ Name: reg.Prefix, Mirrors: mirrors, Secure: !reg.Insecure, } } } else { log.Warnf("failed to get registries configuration: %v", err) indexConfs = make(map[string]*registry.IndexInfo) } return ®istry.ServiceConfig{ AllowNondistributableArtifactsCIDRs: make([]*registry.NetIPNet, 0), AllowNondistributableArtifactsHostnames: make([]string, 0), InsecureRegistryCIDRs: make([]*registry.NetIPNet, 0), IndexConfigs: indexConfs, Mirrors: make([]string, 0), } } func getGraphStatus(storeInfo map[string]string) [][2]string { graphStatus := make([][2]string, 0, len(storeInfo)) for k, v := range storeInfo { graphStatus = append(graphStatus, [2]string{k, v}) } return graphStatus } func getSecOpts(sysInfo *sysinfo.SysInfo) []string { var secOpts []string if sysInfo.AppArmor { secOpts = append(secOpts, "name=apparmor") } if sysInfo.Seccomp { // FIXME: get profile name... secOpts = append(secOpts, fmt.Sprintf("name=seccomp,profile=%s", "default")) } return secOpts } func getRuntimes(configInfo *config.Config) map[string]docker.Runtime { runtimes := map[string]docker.Runtime{} for name, paths := range configInfo.Engine.OCIRuntimes { runtimes[name] = docker.Runtime{ Path: paths[0], Args: nil, } } return runtimes } func getFdCount() (count int) { count = -1 if entries, err := ioutil.ReadDir("/proc/self/fd"); err == nil { count = len(entries) } return } // Just ignoring Container errors here... func getContainersState(r *libpod.Runtime) map[define.ContainerStatus]int { states := map[define.ContainerStatus]int{} ctnrs, err := r.GetAllContainers() if err == nil { for _, ctnr := range ctnrs { state, err := ctnr.State() if err != nil { continue } states[state]++ } } return states } func getEnv(value string) string { if v, exists := os.LookupEnv(strings.ToUpper(value)); exists { return v } if v, exists := os.LookupEnv(strings.ToLower(value)); exists { return v } return "" }