From a6466792b4a78638871a8ed4af65f25555b24520 Mon Sep 17 00:00:00 2001
From: zhongjiawei <zhongjiawei1@huawei.com>
Date: Wed, 26 Jul 2023 16:34:51 +0800
Subject: [PATCH] runc:make runc spec compatible 1.0.0.rc3

---
 spec.go | 181 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 180 insertions(+), 1 deletion(-)

diff --git a/spec.go b/spec.go
index 806d2f1..7170648 100644
--- a/spec.go
+++ b/spec.go
@@ -112,8 +112,95 @@ created by an unprivileged user.
 	},
 }
 
+type compatSpec struct {
+	specs.Spec
+	Linux *linux `json:"linux,omitempty" platform:"linux"`
+}
+
+type linux struct {
+	specs.Linux
+	Resources *linuxResources `json:"resources,omitempty"`
+}
+
+type linuxResources struct {
+	specs.LinuxResources
+	// DisableOOMKiller disables the OOM killer for out of memory conditions
+	DisableOOMKiller *bool `json:"disableOOMKiller,omitempty"`
+	// Specify an oom_score_adj for the container.
+	OOMScoreAdj *int `json:"oomScoreAdj,omitempty"`
+	// BlockIO restriction configuration
+	BlockIO *LinuxBlockIO `json:"blockIO,omitempty"`
+}
+
+// LinuxBlockIO for Linux cgroup 'blkio' resource management
+type LinuxBlockIO struct {
+	// Specifies per cgroup weight, range is from 10 to 1000
+	Weight *uint16 `json:"blkioWeight,omitempty"`
+	// Specifies tasks' weight in the given cgroup while competing with the cgroup's child cgroups, range is from 10 to 1000, CFQ scheduler only
+	LeafWeight *uint16 `json:"blkioLeafWeight,omitempty"`
+	// Weight per cgroup per device, can override BlkioWeight
+	WeightDevice []LinuxWeightDevice `json:"blkioWeightDevice,omitempty"`
+	// IO read rate limit per cgroup per device, bytes per second
+	ThrottleReadBpsDevice []LinuxThrottleDevice `json:"blkioThrottleReadBpsDevice,omitempty"`
+	// IO write rate limit per cgroup per device, bytes per second
+	ThrottleWriteBpsDevice []LinuxThrottleDevice `json:"blkioThrottleWriteBpsDevice,omitempty"`
+	// IO read rate limit per cgroup per device, IO per second
+	ThrottleReadIOPSDevice []LinuxThrottleDevice `json:"blkioThrottleReadIOPSDevice,omitempty"`
+	// IO write rate limit per cgroup per device, IO per second
+	ThrottleWriteIOPSDevice []LinuxThrottleDevice `json:"blkioThrottleWriteIOPSDevice,omitempty"`
+}
+
+// linuxBlockIODevice holds major:minor format supported in blkio cgroup
+type linuxBlockIODevice struct {
+	// Major is the device's major number.
+	Major int64 `json:"major"`
+	// Minor is the device's minor number.
+	Minor int64 `json:"minor"`
+}
+
+// LinuxWeightDevice struct holds a `major:minor weight` pair for blkioWeightDevice
+type LinuxWeightDevice struct {
+	linuxBlockIODevice
+	// Weight is the bandwidth rate for the device, range is from 10 to 1000
+	Weight *uint16 `json:"weight,omitempty"`
+	// LeafWeight is the bandwidth rate for the device while competing with the cgroup's child cgroups, range is from 10 to 1000, CFQ scheduler only
+	LeafWeight *uint16 `json:"leafWeight,omitempty"`
+}
+
+// LinuxThrottleDevice struct holds a `major:minor rate_per_second` pair
+type LinuxThrottleDevice struct {
+	linuxBlockIODevice
+	// Rate is the IO rate limit per cgroup per device
+	Rate uint64 `json:"rate"`
+}
+
+func versionRc6Plus(ver string) bool {
+	if len(ver) < 5 { // version should be a.b.c[-rcn][x]
+		return false
+	}
+
+	// docker-18.09		1.0.1
+	if ver[:5] < "1.0.1" {
+		return true
+	}
+
+	// < 1.0.0-rc6: include 1.0.0-rc5xxx
+	return false
+}
+
 // loadSpec loads the specification from the provided path.
 func loadSpec(cPath string) (spec *specs.Spec, err error) {
+	spec, err = loadOriginSpec(cPath)
+	if err != nil || versionRc6Plus(spec.Version) {
+		return loadCompactSpec(cPath)
+	}
+
+	return spec, validateProcessSpec(spec.Process)
+
+}
+
+// loadSpec loads the specification from the provided path.
+func loadOriginSpec(cPath string) (spec *specs.Spec, err error) {
 	cf, err := os.Open(cPath)
 	if err != nil {
 		if os.IsNotExist(err) {
@@ -126,7 +213,99 @@ func loadSpec(cPath string) (spec *specs.Spec, err error) {
 	if err = json.NewDecoder(cf).Decode(&spec); err != nil {
 		return nil, err
 	}
-	return spec, validateProcessSpec(spec.Process)
+	return spec, nil
+}
+
+func loadCompactSpec(cPath string) (*specs.Spec, error) {
+	var compatSpec compatSpec
+	cf, err := os.Open(cPath)
+	if err != nil {
+		if os.IsNotExist(err) {
+			return nil, fmt.Errorf("JSON specification file %s not found", cPath)
+		}
+		return nil, err
+	}
+	defer cf.Close()
+
+	if err = json.NewDecoder(cf).Decode(&compatSpec); err != nil {
+		return nil, fmt.Errorf("config.json %q error :%v", cPath, err)
+	}
+
+	var spec *specs.Spec
+	if spec, err = updateCompactSpec(&compatSpec); err != nil {
+		return nil, err
+	}
+
+	return spec, nil
+}
+
+func updateCompactSpec(compatSpec *compatSpec) (*specs.Spec, error) {
+	compatjson, _ := json.Marshal(compatSpec)
+	var spec specs.Spec
+	err := json.Unmarshal(compatjson, &spec)
+	if err != nil {
+		return nil, fmt.Errorf("update config failed %v", err)
+	}
+
+	if compatSpec != nil && compatSpec.Linux != nil &&
+		compatSpec.Linux.Resources != nil &&
+		compatSpec.Linux.Resources.DisableOOMKiller != nil {
+		if spec.Linux.Resources.Memory == nil {
+			memory := &specs.LinuxMemory{
+				DisableOOMKiller: compatSpec.Linux.Resources.DisableOOMKiller,
+			}
+			spec.Linux.Resources.Memory = memory
+		} else {
+			spec.Linux.Resources.Memory.DisableOOMKiller = compatSpec.Linux.Resources.DisableOOMKiller
+		}
+	}
+
+	if compatSpec != nil && compatSpec.Linux != nil &&
+		compatSpec.Linux.Resources != nil &&
+		compatSpec.Linux.Resources.OOMScoreAdj != nil {
+		if spec.Process == nil {
+			process := &specs.Process{
+				OOMScoreAdj: compatSpec.Linux.Resources.OOMScoreAdj,
+			}
+			spec.Process = process
+		} else {
+			spec.Process.OOMScoreAdj = compatSpec.Linux.Resources.OOMScoreAdj
+		}
+	}
+
+	if compatSpec.Linux.Resources.BlockIO != nil {
+		spec.Linux.Resources.BlockIO.Weight = compatSpec.Linux.Resources.BlockIO.Weight
+		spec.Linux.Resources.BlockIO.LeafWeight = compatSpec.Linux.Resources.BlockIO.LeafWeight
+		if compatSpec.Linux.Resources.BlockIO.WeightDevice != nil {
+			for _, wd := range compatSpec.Linux.Resources.BlockIO.WeightDevice {
+				wdSpec := specs.LinuxWeightDevice{
+					Weight:     wd.Weight,
+					LeafWeight: wd.LeafWeight,
+				}
+				wdSpec.Major = wd.Major
+				wdSpec.Minor = wd.Minor
+				spec.Linux.Resources.BlockIO.WeightDevice = append(spec.Linux.Resources.BlockIO.WeightDevice, wdSpec)
+			}
+		}
+		procLinuxThrottleDevice := func(src []LinuxThrottleDevice, dest *[]specs.LinuxThrottleDevice) {
+			if src != nil {
+				for _, ltd := range src {
+					ltdSpec := specs.LinuxThrottleDevice{
+						Rate: ltd.Rate,
+					}
+					ltdSpec.Major = ltd.Major
+					ltdSpec.Minor = ltd.Minor
+					*dest = append(*dest, ltdSpec)
+				}
+			}
+		}
+		procLinuxThrottleDevice(compatSpec.Linux.Resources.BlockIO.ThrottleReadBpsDevice, &spec.Linux.Resources.BlockIO.ThrottleReadBpsDevice)
+		procLinuxThrottleDevice(compatSpec.Linux.Resources.BlockIO.ThrottleWriteBpsDevice, &spec.Linux.Resources.BlockIO.ThrottleWriteBpsDevice)
+		procLinuxThrottleDevice(compatSpec.Linux.Resources.BlockIO.ThrottleReadIOPSDevice, &spec.Linux.Resources.BlockIO.ThrottleReadIOPSDevice)
+		procLinuxThrottleDevice(compatSpec.Linux.Resources.BlockIO.ThrottleWriteIOPSDevice, &spec.Linux.Resources.BlockIO.ThrottleWriteIOPSDevice)
+	}
+
+	return &spec, nil
 }
 
 func createLibContainerRlimit(rlimit specs.POSIXRlimit) (configs.Rlimit, error) {
-- 
2.33.0

