This fix fixes issue raised in 29492 where it was not
possible to specify a default `--default-shm-size` in daemon
configuration for each `docker run``.
The flag `--default-shm-size` which is reloadable, has been
added to the daemon configuation.
Related docs has been updated.
This fix fixes 29492.
Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
| ... | ... |
@@ -11,7 +11,6 @@ import ( |
| 11 | 11 |
"github.com/docker/docker/opts" |
| 12 | 12 |
runconfigopts "github.com/docker/docker/runconfig/opts" |
| 13 | 13 |
"github.com/docker/go-connections/nat" |
| 14 |
- units "github.com/docker/go-units" |
|
| 15 | 14 |
"github.com/spf13/cobra" |
| 16 | 15 |
) |
| 17 | 16 |
|
| ... | ... |
@@ -19,26 +18,6 @@ type int64Value interface {
|
| 19 | 19 |
Value() int64 |
| 20 | 20 |
} |
| 21 | 21 |
|
| 22 |
-type memBytes int64 |
|
| 23 |
- |
|
| 24 |
-func (m *memBytes) String() string {
|
|
| 25 |
- return units.BytesSize(float64(m.Value())) |
|
| 26 |
-} |
|
| 27 |
- |
|
| 28 |
-func (m *memBytes) Set(value string) error {
|
|
| 29 |
- val, err := units.RAMInBytes(value) |
|
| 30 |
- *m = memBytes(val) |
|
| 31 |
- return err |
|
| 32 |
-} |
|
| 33 |
- |
|
| 34 |
-func (m *memBytes) Type() string {
|
|
| 35 |
- return "bytes" |
|
| 36 |
-} |
|
| 37 |
- |
|
| 38 |
-func (m *memBytes) Value() int64 {
|
|
| 39 |
- return int64(*m) |
|
| 40 |
-} |
|
| 41 |
- |
|
| 42 | 22 |
// PositiveDurationOpt is an option type for time.Duration that uses a pointer. |
| 43 | 23 |
// It bahave similarly to DurationOpt but only allows positive duration values. |
| 44 | 24 |
type PositiveDurationOpt struct {
|
| ... | ... |
@@ -149,9 +128,9 @@ type updateOptions struct {
|
| 149 | 149 |
|
| 150 | 150 |
type resourceOptions struct {
|
| 151 | 151 |
limitCPU opts.NanoCPUs |
| 152 |
- limitMemBytes memBytes |
|
| 152 |
+ limitMemBytes opts.MemBytes |
|
| 153 | 153 |
resCPU opts.NanoCPUs |
| 154 |
- resMemBytes memBytes |
|
| 154 |
+ resMemBytes opts.MemBytes |
|
| 155 | 155 |
} |
| 156 | 156 |
|
| 157 | 157 |
func (r *resourceOptions) ToResourceRequirements() *swarm.ResourceRequirements {
|
| ... | ... |
@@ -11,12 +11,12 @@ import ( |
| 11 | 11 |
) |
| 12 | 12 |
|
| 13 | 13 |
func TestMemBytesString(t *testing.T) {
|
| 14 |
- var mem memBytes = 1048576 |
|
| 14 |
+ var mem opts.MemBytes = 1048576 |
|
| 15 | 15 |
assert.Equal(t, mem.String(), "1 MiB") |
| 16 | 16 |
} |
| 17 | 17 |
|
| 18 | 18 |
func TestMemBytesSetAndValue(t *testing.T) {
|
| 19 |
- var mem memBytes |
|
| 19 |
+ var mem opts.MemBytes |
|
| 20 | 20 |
assert.NilError(t, mem.Set("5kb"))
|
| 21 | 21 |
assert.Equal(t, mem.Value(), int64(5120)) |
| 22 | 22 |
} |
| ... | ... |
@@ -22,9 +22,7 @@ import ( |
| 22 | 22 |
) |
| 23 | 23 |
|
| 24 | 24 |
const ( |
| 25 |
- // DefaultSHMSize is the default size (64MB) of the SHM which will be mounted in the container |
|
| 26 |
- DefaultSHMSize int64 = 67108864 |
|
| 27 |
- containerSecretMountPath = "/run/secrets" |
|
| 25 |
+ containerSecretMountPath = "/run/secrets" |
|
| 28 | 26 |
) |
| 29 | 27 |
|
| 30 | 28 |
// Container holds the fields specific to unixen implementations. |
| ... | ... |
@@ -14,6 +14,7 @@ var ( |
| 14 | 14 |
defaultPidFile = "/var/run/docker.pid" |
| 15 | 15 |
defaultGraph = "/var/lib/docker" |
| 16 | 16 |
defaultExecRoot = "/var/run/docker" |
| 17 |
+ defaultShmSize = int64(67108864) |
|
| 17 | 18 |
) |
| 18 | 19 |
|
| 19 | 20 |
// Config defines the configuration of a docker daemon. |
| ... | ... |
@@ -36,6 +37,7 @@ type Config struct {
|
| 36 | 36 |
Init bool `json:"init,omitempty"` |
| 37 | 37 |
InitPath string `json:"init-path,omitempty"` |
| 38 | 38 |
SeccompProfile string `json:"seccomp-profile,omitempty"` |
| 39 |
+ ShmSize opts.MemBytes `json:"default-shm-size,omitempty"` |
|
| 39 | 40 |
} |
| 40 | 41 |
|
| 41 | 42 |
// bridgeConfig stores all the bridge driver specific |
| ... | ... |
@@ -66,6 +68,9 @@ func (config *Config) InstallFlags(flags *pflag.FlagSet) {
|
| 66 | 66 |
|
| 67 | 67 |
config.Ulimits = make(map[string]*units.Ulimit) |
| 68 | 68 |
|
| 69 |
+ // Set default value for `--default-shm-size` |
|
| 70 |
+ config.ShmSize = opts.MemBytes(defaultShmSize) |
|
| 71 |
+ |
|
| 69 | 72 |
// Then platform-specific install flags |
| 70 | 73 |
flags.BoolVar(&config.EnableSelinuxSupport, "selinux-enabled", false, "Enable selinux support") |
| 71 | 74 |
flags.Var(opts.NewUlimitOpt(&config.Ulimits), "default-ulimit", "Default ulimits for containers") |
| ... | ... |
@@ -89,6 +94,7 @@ func (config *Config) InstallFlags(flags *pflag.FlagSet) {
|
| 89 | 89 |
flags.Int64Var(&config.CPURealtimePeriod, "cpu-rt-period", 0, "Limit the CPU real-time period in microseconds") |
| 90 | 90 |
flags.Int64Var(&config.CPURealtimeRuntime, "cpu-rt-runtime", 0, "Limit the CPU real-time runtime in microseconds") |
| 91 | 91 |
flags.StringVar(&config.SeccompProfile, "seccomp-profile", "", "Path to seccomp profile") |
| 92 |
+ flags.Var(&config.ShmSize, "default-shm-size", "Default shm size for containers") |
|
| 92 | 93 |
|
| 93 | 94 |
config.attachExperimentalFlags(flags) |
| 94 | 95 |
} |
| ... | ... |
@@ -4,7 +4,12 @@ package daemon |
| 4 | 4 |
|
| 5 | 5 |
import ( |
| 6 | 6 |
"io/ioutil" |
| 7 |
+ "runtime" |
|
| 8 |
+ |
|
| 7 | 9 |
"testing" |
| 10 |
+ |
|
| 11 |
+ "github.com/docker/docker/pkg/testutil/assert" |
|
| 12 |
+ "github.com/spf13/pflag" |
|
| 8 | 13 |
) |
| 9 | 14 |
|
| 10 | 15 |
func TestDaemonConfigurationMerge(t *testing.T) {
|
| ... | ... |
@@ -78,3 +83,52 @@ func TestDaemonConfigurationMerge(t *testing.T) {
|
| 78 | 78 |
} |
| 79 | 79 |
} |
| 80 | 80 |
} |
| 81 |
+ |
|
| 82 |
+func TestDaemonParseShmSize(t *testing.T) {
|
|
| 83 |
+ if runtime.GOOS == "solaris" {
|
|
| 84 |
+ t.Skip("ShmSize not supported on Solaris\n")
|
|
| 85 |
+ } |
|
| 86 |
+ flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
|
|
| 87 |
+ |
|
| 88 |
+ config := &Config{}
|
|
| 89 |
+ config.InstallFlags(flags) |
|
| 90 |
+ // By default `--default-shm-size=64M` |
|
| 91 |
+ expectedValue := 64 * 1024 * 1024 |
|
| 92 |
+ if config.ShmSize.Value() != int64(expectedValue) {
|
|
| 93 |
+ t.Fatalf("expected default shm size %d, got %d", expectedValue, config.ShmSize.Value())
|
|
| 94 |
+ } |
|
| 95 |
+ assert.NilError(t, flags.Set("default-shm-size", "128M"))
|
|
| 96 |
+ expectedValue = 128 * 1024 * 1024 |
|
| 97 |
+ if config.ShmSize.Value() != int64(expectedValue) {
|
|
| 98 |
+ t.Fatalf("expected default shm size %d, got %d", expectedValue, config.ShmSize.Value())
|
|
| 99 |
+ } |
|
| 100 |
+} |
|
| 101 |
+ |
|
| 102 |
+func TestDaemonConfigurationMergeShmSize(t *testing.T) {
|
|
| 103 |
+ if runtime.GOOS == "solaris" {
|
|
| 104 |
+ t.Skip("ShmSize not supported on Solaris\n")
|
|
| 105 |
+ } |
|
| 106 |
+ f, err := ioutil.TempFile("", "docker-config-")
|
|
| 107 |
+ if err != nil {
|
|
| 108 |
+ t.Fatal(err) |
|
| 109 |
+ } |
|
| 110 |
+ |
|
| 111 |
+ configFile := f.Name() |
|
| 112 |
+ |
|
| 113 |
+ f.Write([]byte(` |
|
| 114 |
+ {
|
|
| 115 |
+ "default-shm-size": "1g" |
|
| 116 |
+ }`)) |
|
| 117 |
+ |
|
| 118 |
+ f.Close() |
|
| 119 |
+ |
|
| 120 |
+ c := &Config{}
|
|
| 121 |
+ cc, err := MergeDaemonConfigurations(c, nil, configFile) |
|
| 122 |
+ if err != nil {
|
|
| 123 |
+ t.Fatal(err) |
|
| 124 |
+ } |
|
| 125 |
+ expectedValue := 1 * 1024 * 1024 * 1024 |
|
| 126 |
+ if cc.ShmSize.Value() != int64(expectedValue) {
|
|
| 127 |
+ t.Fatalf("expected default shm size %d, got %d", expectedValue, cc.ShmSize.Value())
|
|
| 128 |
+ } |
|
| 129 |
+} |
| ... | ... |
@@ -117,7 +117,7 @@ func (daemon *Daemon) setupIpcDirs(c *container.Container) error {
|
| 117 | 117 |
return err |
| 118 | 118 |
} |
| 119 | 119 |
|
| 120 |
- shmSize := container.DefaultSHMSize |
|
| 120 |
+ shmSize := int64(daemon.configStore.ShmSize) |
|
| 121 | 121 |
if c.HostConfig.ShmSize != 0 {
|
| 122 | 122 |
shmSize = c.HostConfig.ShmSize |
| 123 | 123 |
} |
| ... | ... |
@@ -255,7 +255,10 @@ func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConf |
| 255 | 255 |
hostConfig.MemorySwap = hostConfig.Memory * 2 |
| 256 | 256 |
} |
| 257 | 257 |
if hostConfig.ShmSize == 0 {
|
| 258 |
- hostConfig.ShmSize = container.DefaultSHMSize |
|
| 258 |
+ hostConfig.ShmSize = defaultShmSize |
|
| 259 |
+ if daemon.configStore != nil {
|
|
| 260 |
+ hostConfig.ShmSize = int64(daemon.configStore.ShmSize) |
|
| 261 |
+ } |
|
| 259 | 262 |
} |
| 260 | 263 |
var err error |
| 261 | 264 |
opts, err := daemon.generateSecurityOpt(hostConfig.IpcMode, hostConfig.PidMode, hostConfig.Privileged) |
| ... | ... |
@@ -575,6 +578,10 @@ func (daemon *Daemon) platformReload(config *Config) map[string]string {
|
| 575 | 575 |
daemon.configStore.DefaultRuntime = config.DefaultRuntime |
| 576 | 576 |
} |
| 577 | 577 |
|
| 578 |
+ if config.IsValueSet("default-shm-size") {
|
|
| 579 |
+ daemon.configStore.ShmSize = config.ShmSize |
|
| 580 |
+ } |
|
| 581 |
+ |
|
| 578 | 582 |
// Update attributes |
| 579 | 583 |
var runtimeList bytes.Buffer |
| 580 | 584 |
for name, rt := range daemon.configStore.Runtimes {
|
| ... | ... |
@@ -585,8 +592,9 @@ func (daemon *Daemon) platformReload(config *Config) map[string]string {
|
| 585 | 585 |
} |
| 586 | 586 |
|
| 587 | 587 |
return map[string]string{
|
| 588 |
- "runtimes": runtimeList.String(), |
|
| 589 |
- "default-runtime": daemon.configStore.DefaultRuntime, |
|
| 588 |
+ "runtimes": runtimeList.String(), |
|
| 589 |
+ "default-runtime": daemon.configStore.DefaultRuntime, |
|
| 590 |
+ "default-shm-size": fmt.Sprintf("%d", daemon.configStore.ShmSize),
|
|
| 590 | 591 |
} |
| 591 | 592 |
} |
| 592 | 593 |
|
| ... | ... |
@@ -37,6 +37,7 @@ Options: |
| 37 | 37 |
--default-gateway value Container default gateway IPv4 address |
| 38 | 38 |
--default-gateway-v6 value Container default gateway IPv6 address |
| 39 | 39 |
--default-runtime string Default OCI runtime for containers (default "runc") |
| 40 |
+ --default-shm-size bytes Set the default shm size for containers (default 64 MiB) |
|
| 40 | 41 |
--default-ulimit value Default ulimits for containers (default []) |
| 41 | 42 |
--disable-legacy-registry Disable contacting legacy registries |
| 42 | 43 |
--dns value DNS server to use (default []) |
| ... | ... |
@@ -1161,6 +1162,7 @@ This is a full example of the allowed configuration options on Linux: |
| 1161 | 1161 |
"cluster-advertise": "", |
| 1162 | 1162 |
"max-concurrent-downloads": 3, |
| 1163 | 1163 |
"max-concurrent-uploads": 5, |
| 1164 |
+ "default-shm-size": "64M", |
|
| 1164 | 1165 |
"shutdown-timeout": 15, |
| 1165 | 1166 |
"debug": true, |
| 1166 | 1167 |
"hosts": [], |
| ... | ... |
@@ -2879,3 +2879,60 @@ func (s *DockerDaemonSuite) TestRestartPolicyWithLiveRestore(c *check.C) {
|
| 2879 | 2879 |
out, err = s.d.Cmd("stop", id)
|
| 2880 | 2880 |
c.Assert(err, check.IsNil, check.Commentf("output: %s", out))
|
| 2881 | 2881 |
} |
| 2882 |
+ |
|
| 2883 |
+func (s *DockerDaemonSuite) TestShmSize(c *check.C) {
|
|
| 2884 |
+ testRequires(c, DaemonIsLinux) |
|
| 2885 |
+ |
|
| 2886 |
+ size := 67108864 * 2 |
|
| 2887 |
+ pattern := regexp.MustCompile(fmt.Sprintf("shm on /dev/shm type tmpfs(.*)size=%dk", size/1024))
|
|
| 2888 |
+ |
|
| 2889 |
+ s.d.StartWithBusybox(c, "--default-shm-size", fmt.Sprintf("%v", size))
|
|
| 2890 |
+ |
|
| 2891 |
+ name := "shm1" |
|
| 2892 |
+ out, err := s.d.Cmd("run", "--name", name, "busybox", "mount")
|
|
| 2893 |
+ c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
|
|
| 2894 |
+ c.Assert(pattern.MatchString(out), checker.True) |
|
| 2895 |
+ out, err = s.d.Cmd("inspect", "--format", "{{.HostConfig.ShmSize}}", name)
|
|
| 2896 |
+ c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
|
|
| 2897 |
+ c.Assert(strings.TrimSpace(out), check.Equals, fmt.Sprintf("%v", size))
|
|
| 2898 |
+} |
|
| 2899 |
+ |
|
| 2900 |
+func (s *DockerDaemonSuite) TestShmSizeReload(c *check.C) {
|
|
| 2901 |
+ testRequires(c, DaemonIsLinux) |
|
| 2902 |
+ |
|
| 2903 |
+ configPath, err := ioutil.TempDir("", "test-daemon-shm-size-reload-config")
|
|
| 2904 |
+ c.Assert(err, checker.IsNil, check.Commentf("could not create temp file for config reload"))
|
|
| 2905 |
+ defer os.RemoveAll(configPath) // clean up |
|
| 2906 |
+ configFile := filepath.Join(configPath, "config.json") |
|
| 2907 |
+ |
|
| 2908 |
+ size := 67108864 * 2 |
|
| 2909 |
+ configData := []byte(fmt.Sprintf(`{"default-shm-size": "%dM"}`, size/1024/1024))
|
|
| 2910 |
+ c.Assert(ioutil.WriteFile(configFile, configData, 0666), checker.IsNil, check.Commentf("could not write temp file for config reload"))
|
|
| 2911 |
+ pattern := regexp.MustCompile(fmt.Sprintf("shm on /dev/shm type tmpfs(.*)size=%dk", size/1024))
|
|
| 2912 |
+ |
|
| 2913 |
+ s.d.StartWithBusybox(c, "--config-file", configFile) |
|
| 2914 |
+ |
|
| 2915 |
+ name := "shm1" |
|
| 2916 |
+ out, err := s.d.Cmd("run", "--name", name, "busybox", "mount")
|
|
| 2917 |
+ c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
|
|
| 2918 |
+ c.Assert(pattern.MatchString(out), checker.True) |
|
| 2919 |
+ out, err = s.d.Cmd("inspect", "--format", "{{.HostConfig.ShmSize}}", name)
|
|
| 2920 |
+ c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
|
|
| 2921 |
+ c.Assert(strings.TrimSpace(out), check.Equals, fmt.Sprintf("%v", size))
|
|
| 2922 |
+ |
|
| 2923 |
+ size = 67108864 * 3 |
|
| 2924 |
+ configData = []byte(fmt.Sprintf(`{"default-shm-size": "%dM"}`, size/1024/1024))
|
|
| 2925 |
+ c.Assert(ioutil.WriteFile(configFile, configData, 0666), checker.IsNil, check.Commentf("could not write temp file for config reload"))
|
|
| 2926 |
+ pattern = regexp.MustCompile(fmt.Sprintf("shm on /dev/shm type tmpfs(.*)size=%dk", size/1024))
|
|
| 2927 |
+ |
|
| 2928 |
+ err = s.d.ReloadConfig() |
|
| 2929 |
+ c.Assert(err, checker.IsNil, check.Commentf("error reloading daemon config"))
|
|
| 2930 |
+ |
|
| 2931 |
+ name = "shm2" |
|
| 2932 |
+ out, err = s.d.Cmd("run", "--name", name, "busybox", "mount")
|
|
| 2933 |
+ c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
|
|
| 2934 |
+ c.Assert(pattern.MatchString(out), checker.True) |
|
| 2935 |
+ out, err = s.d.Cmd("inspect", "--format", "{{.HostConfig.ShmSize}}", name)
|
|
| 2936 |
+ c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
|
|
| 2937 |
+ c.Assert(strings.TrimSpace(out), check.Equals, fmt.Sprintf("%v", size))
|
|
| 2938 |
+} |
| ... | ... |
@@ -427,7 +427,7 @@ func (s *DockerDaemonSuite) TestDaemonEvents(c *check.C) {
|
| 427 | 427 |
out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c))
|
| 428 | 428 |
c.Assert(err, checker.IsNil) |
| 429 | 429 |
|
| 430 |
- c.Assert(out, checker.Contains, fmt.Sprintf("daemon reload %s (cluster-advertise=, cluster-store=, cluster-store-opts={}, debug=true, default-runtime=runc, insecure-registries=[], labels=[\"bar=foo\"], live-restore=false, max-concurrent-downloads=1, max-concurrent-uploads=5, name=%s, registry-mirrors=[], runtimes=runc:{docker-runc []}, shutdown-timeout=10)", daemonID, daemonName))
|
|
| 430 |
+ c.Assert(out, checker.Contains, fmt.Sprintf("daemon reload %s (cluster-advertise=, cluster-store=, cluster-store-opts={}, debug=true, default-runtime=runc, default-shm-size=67108864, insecure-registries=[], labels=[\"bar=foo\"], live-restore=false, max-concurrent-downloads=1, max-concurrent-uploads=5, name=%s, runtimes=runc:{docker-runc []}, shutdown-timeout=10)", daemonID, daemonName))
|
|
| 431 | 431 |
} |
| 432 | 432 |
|
| 433 | 433 |
func (s *DockerDaemonSuite) TestDaemonEventsWithFilters(c *check.C) {
|
| ... | ... |
@@ -21,6 +21,7 @@ dockerd - Enable daemon mode |
| 21 | 21 |
[**--default-gateway**[=*DEFAULT-GATEWAY*]] |
| 22 | 22 |
[**--default-gateway-v6**[=*DEFAULT-GATEWAY-V6*]] |
| 23 | 23 |
[**--default-runtime**[=*runc*]] |
| 24 |
+[**--default-shm-size**[=*64MiB*]] |
|
| 24 | 25 |
[**--default-ulimit**[=*[]*]] |
| 25 | 26 |
[**--disable-legacy-registry**] |
| 26 | 27 |
[**--dns**[=*[]*]] |
| ... | ... |
@@ -164,6 +165,9 @@ $ sudo dockerd --add-runtime runc=runc --add-runtime custom=/usr/local/bin/my-ru |
| 164 | 164 |
**--default-runtime**="runc" |
| 165 | 165 |
Set default runtime if there're more than one specified by `--add-runtime`. |
| 166 | 166 |
|
| 167 |
+**--default-shm-size**=*64MiB* |
|
| 168 |
+ Set the daemon-wide default shm size for containers. Default is `64MiB`. |
|
| 169 |
+ |
|
| 167 | 170 |
**--default-ulimit**=[] |
| 168 | 171 |
Default ulimits for containers. |
| 169 | 172 |
|
| ... | ... |
@@ -9,6 +9,7 @@ import ( |
| 9 | 9 |
"strings" |
| 10 | 10 |
|
| 11 | 11 |
"github.com/docker/docker/api/types/filters" |
| 12 |
+ units "github.com/docker/go-units" |
|
| 12 | 13 |
) |
| 13 | 14 |
|
| 14 | 15 |
var ( |
| ... | ... |
@@ -402,3 +403,38 @@ func ValidateLink(val string) (string, error) {
|
| 402 | 402 |
_, _, err := ParseLink(val) |
| 403 | 403 |
return val, err |
| 404 | 404 |
} |
| 405 |
+ |
|
| 406 |
+// MemBytes is a type for human readable memory bytes (like 128M, 2g, etc) |
|
| 407 |
+type MemBytes int64 |
|
| 408 |
+ |
|
| 409 |
+// String returns the string format of the human readable memory bytes |
|
| 410 |
+func (m *MemBytes) String() string {
|
|
| 411 |
+ return units.BytesSize(float64(m.Value())) |
|
| 412 |
+} |
|
| 413 |
+ |
|
| 414 |
+// Set sets the value of the MemBytes by passing a string |
|
| 415 |
+func (m *MemBytes) Set(value string) error {
|
|
| 416 |
+ val, err := units.RAMInBytes(value) |
|
| 417 |
+ *m = MemBytes(val) |
|
| 418 |
+ return err |
|
| 419 |
+} |
|
| 420 |
+ |
|
| 421 |
+// Type returns the type |
|
| 422 |
+func (m *MemBytes) Type() string {
|
|
| 423 |
+ return "bytes" |
|
| 424 |
+} |
|
| 425 |
+ |
|
| 426 |
+// Value returns the value in int64 |
|
| 427 |
+func (m *MemBytes) Value() int64 {
|
|
| 428 |
+ return int64(*m) |
|
| 429 |
+} |
|
| 430 |
+ |
|
| 431 |
+// UnmarshalJSON is the customized unmarshaler for MemBytes |
|
| 432 |
+func (m *MemBytes) UnmarshalJSON(s []byte) error {
|
|
| 433 |
+ if len(s) <= 2 || s[0] != '"' || s[len(s)-1] != '"' {
|
|
| 434 |
+ return fmt.Errorf("invalid size: %q", s)
|
|
| 435 |
+ } |
|
| 436 |
+ val, err := units.RAMInBytes(string(s[1 : len(s)-1])) |
|
| 437 |
+ *m = MemBytes(val) |
|
| 438 |
+ return err |
|
| 439 |
+} |