github.com/containerd/nerdctl@v1.7.7/pkg/infoutil/infoutil.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package infoutil 18 19 import ( 20 "context" 21 "fmt" 22 "os" 23 "os/exec" 24 "runtime" 25 "strings" 26 "time" 27 28 "github.com/Masterminds/semver/v3" 29 "github.com/containerd/containerd" 30 ptypes "github.com/containerd/containerd/protobuf/types" 31 "github.com/containerd/containerd/services/introspection" 32 "github.com/containerd/log" 33 "github.com/containerd/nerdctl/pkg/buildkitutil" 34 "github.com/containerd/nerdctl/pkg/inspecttypes/dockercompat" 35 "github.com/containerd/nerdctl/pkg/inspecttypes/native" 36 "github.com/containerd/nerdctl/pkg/logging" 37 "github.com/containerd/nerdctl/pkg/version" 38 ) 39 40 func NativeDaemonInfo(ctx context.Context, client *containerd.Client) (*native.DaemonInfo, error) { 41 introService := client.IntrospectionService() 42 plugins, err := introService.Plugins(ctx, nil) 43 if err != nil { 44 return nil, err 45 } 46 server, err := introService.Server(ctx, &ptypes.Empty{}) 47 if err != nil { 48 return nil, err 49 } 50 versionService := client.VersionService() 51 version, err := versionService.Version(ctx, &ptypes.Empty{}) 52 if err != nil { 53 return nil, err 54 } 55 56 daemonInfo := &native.DaemonInfo{ 57 Plugins: plugins, 58 Server: server, 59 Version: version, 60 } 61 return daemonInfo, nil 62 } 63 64 func Info(ctx context.Context, client *containerd.Client, snapshotter, cgroupManager string) (*dockercompat.Info, error) { 65 daemonVersion, err := client.Version(ctx) 66 if err != nil { 67 return nil, err 68 } 69 introService := client.IntrospectionService() 70 daemonIntro, err := introService.Server(ctx, &ptypes.Empty{}) 71 if err != nil { 72 return nil, err 73 } 74 snapshotterPlugins, err := GetSnapshotterNames(ctx, introService) 75 if err != nil { 76 return nil, err 77 } 78 79 var info dockercompat.Info 80 info.ID = daemonIntro.UUID 81 // Storage drivers and logging drivers are not really Server concept for nerdctl, but mimics `docker info` output 82 info.Driver = snapshotter 83 info.Plugins.Log = logging.Drivers() 84 info.Plugins.Storage = snapshotterPlugins 85 info.SystemTime = time.Now().Format(time.RFC3339Nano) 86 info.LoggingDriver = "json-file" // hard-coded 87 info.CgroupDriver = cgroupManager 88 info.CgroupVersion = CgroupsVersion() 89 info.KernelVersion = UnameR() 90 info.OperatingSystem = DistroName() 91 info.OSType = runtime.GOOS 92 info.Architecture = UnameM() 93 info.Name, err = os.Hostname() 94 if err != nil { 95 return nil, err 96 } 97 info.ServerVersion = daemonVersion.Version 98 fulfillPlatformInfo(&info) 99 return &info, nil 100 } 101 102 func GetSnapshotterNames(ctx context.Context, introService introspection.Service) ([]string, error) { 103 var names []string 104 plugins, err := introService.Plugins(ctx, nil) 105 if err != nil { 106 return nil, err 107 } 108 for _, p := range plugins.Plugins { 109 if strings.HasPrefix(p.Type, "io.containerd.snapshotter.") && p.InitErr == nil { 110 names = append(names, p.ID) 111 } 112 } 113 return names, nil 114 } 115 116 func ClientVersion() dockercompat.ClientVersion { 117 return dockercompat.ClientVersion{ 118 Version: version.GetVersion(), 119 GitCommit: version.GetRevision(), 120 GoVersion: runtime.Version(), 121 Os: runtime.GOOS, 122 Arch: runtime.GOARCH, 123 Components: []dockercompat.ComponentVersion{ 124 buildctlVersion(), 125 }, 126 } 127 } 128 129 func ServerVersion(ctx context.Context, client *containerd.Client) (*dockercompat.ServerVersion, error) { 130 daemonVersion, err := client.Version(ctx) 131 if err != nil { 132 return nil, err 133 } 134 135 v := &dockercompat.ServerVersion{ 136 Components: []dockercompat.ComponentVersion{ 137 { 138 Name: "containerd", 139 Version: daemonVersion.Version, 140 Details: map[string]string{"GitCommit": daemonVersion.Revision}, 141 }, 142 runcVersion(), 143 }, 144 } 145 return v, nil 146 } 147 148 func ServerSemVer(ctx context.Context, client *containerd.Client) (*semver.Version, error) { 149 v, err := client.Version(ctx) 150 if err != nil { 151 return nil, err 152 } 153 sv, err := semver.NewVersion(v.Version) 154 if err != nil { 155 return nil, fmt.Errorf("failed to parse the containerd version %q: %w", v.Version, err) 156 } 157 return sv, nil 158 } 159 160 func buildctlVersion() dockercompat.ComponentVersion { 161 buildctlBinary, err := buildkitutil.BuildctlBinary() 162 if err != nil { 163 log.L.Warnf("unable to determine buildctl version: %s", err.Error()) 164 return dockercompat.ComponentVersion{Name: "buildctl"} 165 } 166 167 stdout, err := exec.Command(buildctlBinary, "--version").Output() 168 if err != nil { 169 log.L.Warnf("unable to determine buildctl version: %s", err.Error()) 170 return dockercompat.ComponentVersion{Name: "buildctl"} 171 } 172 173 v, err := parseBuildctlVersion(stdout) 174 if err != nil { 175 log.L.Warn(err) 176 return dockercompat.ComponentVersion{Name: "buildctl"} 177 } 178 return *v 179 } 180 181 func parseBuildctlVersion(buildctlVersionStdout []byte) (*dockercompat.ComponentVersion, error) { 182 fields := strings.Fields(strings.TrimSpace(string(buildctlVersionStdout))) 183 var v *dockercompat.ComponentVersion 184 switch len(fields) { 185 case 4: 186 v = &dockercompat.ComponentVersion{ 187 Name: fields[0], 188 Version: fields[2], 189 Details: map[string]string{"GitCommit": fields[3]}, 190 } 191 case 3: 192 v = &dockercompat.ComponentVersion{ 193 Name: fields[0], 194 Version: fields[2], 195 } 196 default: 197 return nil, fmt.Errorf("unable to determine buildctl version, got %q", string(buildctlVersionStdout)) 198 } 199 if v.Name != "buildctl" { 200 return nil, fmt.Errorf("unable to determine buildctl version, got %q", string(buildctlVersionStdout)) 201 } 202 return v, nil 203 } 204 205 func runcVersion() dockercompat.ComponentVersion { 206 stdout, err := exec.Command("runc", "--version").Output() 207 if err != nil { 208 log.L.Warnf("unable to determine runc version: %s", err.Error()) 209 return dockercompat.ComponentVersion{Name: "runc"} 210 } 211 v, err := parseRuncVersion(stdout) 212 if err != nil { 213 log.L.Warn(err) 214 return dockercompat.ComponentVersion{Name: "runc"} 215 } 216 return *v 217 } 218 219 func parseRuncVersion(runcVersionStdout []byte) (*dockercompat.ComponentVersion, error) { 220 var versionList = strings.Split(strings.TrimSpace(string(runcVersionStdout)), "\n") 221 firstLine := strings.Fields(versionList[0]) 222 if len(firstLine) != 3 || firstLine[0] != "runc" { 223 return nil, fmt.Errorf("unable to determine runc version, got: %s", string(runcVersionStdout)) 224 } 225 version := firstLine[2] 226 227 details := map[string]string{} 228 for _, detailsLine := range versionList[1:] { 229 detail := strings.SplitN(detailsLine, ":", 2) 230 if len(detail) != 2 { 231 log.L.Warnf("unable to determine one of runc details, got: %s, %d", detail, len(detail)) 232 continue 233 } 234 switch strings.TrimSpace(detail[0]) { 235 case "commit": 236 details["GitCommit"] = strings.TrimSpace(detail[1]) 237 } 238 } 239 240 return &dockercompat.ComponentVersion{ 241 Name: "runc", 242 Version: version, 243 Details: details, 244 }, nil 245 } 246 247 // BlockIOWeight return whether Block IO weight is supported or not 248 func BlockIOWeight(cgroupManager string) bool { 249 var info dockercompat.Info 250 info.CgroupVersion = CgroupsVersion() 251 info.CgroupDriver = cgroupManager 252 mobySysInfo := mobySysInfo(&info) 253 // blkio weight is not available on cgroup v1 since kernel 5.0. 254 // On cgroup v2, blkio weight is implemented using io.weight 255 return mobySysInfo.BlkioWeight 256 }