Signed-off-by: Boaz Shuster <ripcurld.github@gmail.com>
| ... | ... |
@@ -43,6 +43,7 @@ func (v VersionMiddleware) WrapHandler(handler func(ctx context.Context, w http. |
| 43 | 43 |
header := fmt.Sprintf("Docker/%s (%s)", v.serverVersion, runtime.GOOS)
|
| 44 | 44 |
w.Header().Set("Server", header)
|
| 45 | 45 |
w.Header().Set("API-Version", v.defaultVersion)
|
| 46 |
+ w.Header().Set("OSType", runtime.GOOS)
|
|
| 46 | 47 |
ctx = context.WithValue(ctx, "api-version", apiVersion) |
| 47 | 48 |
return handler(ctx, w, r, vars) |
| 48 | 49 |
} |
| ... | ... |
@@ -51,6 +51,7 @@ type DockerCli struct {
|
| 51 | 51 |
keyFile string |
| 52 | 52 |
client client.APIClient |
| 53 | 53 |
hasExperimental bool |
| 54 |
+ osType string |
|
| 54 | 55 |
defaultVersion string |
| 55 | 56 |
} |
| 56 | 57 |
|
| ... | ... |
@@ -59,6 +60,11 @@ func (cli *DockerCli) HasExperimental() bool {
|
| 59 | 59 |
return cli.hasExperimental |
| 60 | 60 |
} |
| 61 | 61 |
|
| 62 |
+// OSType returns the operating system the daemon is running on. |
|
| 63 |
+func (cli *DockerCli) OSType() string {
|
|
| 64 |
+ return cli.osType |
|
| 65 |
+} |
|
| 66 |
+ |
|
| 62 | 67 |
// DefaultVersion returns api.defaultVersion of DOCKER_API_VERSION if specified. |
| 63 | 68 |
func (cli *DockerCli) DefaultVersion() string {
|
| 64 | 69 |
return cli.defaultVersion |
| ... | ... |
@@ -166,6 +172,7 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions) error {
|
| 166 | 166 |
|
| 167 | 167 |
if ping, err := cli.client.Ping(context.Background()); err == nil {
|
| 168 | 168 |
cli.hasExperimental = ping.Experimental |
| 169 |
+ cli.osType = ping.OSType |
|
| 169 | 170 |
|
| 170 | 171 |
// since the new header was added in 1.25, assume server is 1.24 if header is not present. |
| 171 | 172 |
if ping.APIVersion == "" {
|
| ... | ... |
@@ -189,6 +189,7 @@ func addFlags(flags *pflag.FlagSet) *containerOptions {
|
| 189 | 189 |
flags.Var(&copts.securityOpt, "security-opt", "Security Options") |
| 190 | 190 |
flags.StringVar(&copts.usernsMode, "userns", "", "User namespace to use") |
| 191 | 191 |
flags.StringVar(&copts.credentialSpec, "credentialspec", "", "Credential spec for managed service account (Windows only)") |
| 192 |
+ flags.SetAnnotation("credentialspec", "ostype", []string{"windows"})
|
|
| 192 | 193 |
|
| 193 | 194 |
// Network and port publishing flag |
| 194 | 195 |
flags.Var(&copts.extraHosts, "add-host", "Add a custom host-to-IP mapping (host:ip)") |
| ... | ... |
@@ -239,7 +240,9 @@ func addFlags(flags *pflag.FlagSet) *containerOptions {
|
| 239 | 239 |
flags.StringVar(&copts.cpusetCpus, "cpuset-cpus", "", "CPUs in which to allow execution (0-3, 0,1)") |
| 240 | 240 |
flags.StringVar(&copts.cpusetMems, "cpuset-mems", "", "MEMs in which to allow execution (0-3, 0,1)") |
| 241 | 241 |
flags.Int64Var(&copts.cpuCount, "cpu-count", 0, "CPU count (Windows only)") |
| 242 |
+ flags.SetAnnotation("cpu-count", "ostype", []string{"windows"})
|
|
| 242 | 243 |
flags.Int64Var(&copts.cpuPercent, "cpu-percent", 0, "CPU percent (Windows only)") |
| 244 |
+ flags.SetAnnotation("cpu-percent", "ostype", []string{"windows"})
|
|
| 243 | 245 |
flags.Int64Var(&copts.cpuPeriod, "cpu-period", 0, "Limit CPU CFS (Completely Fair Scheduler) period") |
| 244 | 246 |
flags.Int64Var(&copts.cpuQuota, "cpu-quota", 0, "Limit CPU CFS (Completely Fair Scheduler) quota") |
| 245 | 247 |
flags.Int64Var(&copts.cpuRealtimePeriod, "cpu-rt-period", 0, "Limit CPU real-time period in microseconds") |
| ... | ... |
@@ -254,7 +257,9 @@ func addFlags(flags *pflag.FlagSet) *containerOptions {
|
| 254 | 254 |
flags.Var(&copts.deviceWriteBps, "device-write-bps", "Limit write rate (bytes per second) to a device") |
| 255 | 255 |
flags.Var(&copts.deviceWriteIOps, "device-write-iops", "Limit write rate (IO per second) to a device") |
| 256 | 256 |
flags.Var(&copts.ioMaxBandwidth, "io-maxbandwidth", "Maximum IO bandwidth limit for the system drive (Windows only)") |
| 257 |
+ flags.SetAnnotation("io-maxbandwidth", "ostype", []string{"windows"})
|
|
| 257 | 258 |
flags.Uint64Var(&copts.ioMaxIOps, "io-maxiops", 0, "Maximum IOps limit for the system drive (Windows only)") |
| 259 |
+ flags.SetAnnotation("io-maxiops", "ostype", []string{"windows"})
|
|
| 258 | 260 |
flags.Var(&copts.kernelMemory, "kernel-memory", "Kernel memory limit") |
| 259 | 261 |
flags.VarP(&copts.memory, "memory", "m", "Memory limit") |
| 260 | 262 |
flags.Var(&copts.memoryReservation, "memory-reservation", "Memory soft limit") |
| ... | ... |
@@ -7,7 +7,7 @@ import ( |
| 7 | 7 |
"golang.org/x/net/context" |
| 8 | 8 |
) |
| 9 | 9 |
|
| 10 |
-// Ping pings the server and returns the value of the "Docker-Experimental" & "API-Version" headers |
|
| 10 |
+// Ping pings the server and returns the value of the "Docker-Experimental", "OS-Type" & "API-Version" headers |
|
| 11 | 11 |
func (cli *Client) Ping(ctx context.Context) (types.Ping, error) {
|
| 12 | 12 |
var ping types.Ping |
| 13 | 13 |
req, err := cli.buildRequest("GET", fmt.Sprintf("%s/_ping", cli.basePath), nil, nil)
|
| ... | ... |
@@ -26,5 +26,7 @@ func (cli *Client) Ping(ctx context.Context) (types.Ping, error) {
|
| 26 | 26 |
ping.Experimental = true |
| 27 | 27 |
} |
| 28 | 28 |
|
| 29 |
+ ping.OSType = serverResp.header.Get("OSType")
|
|
| 30 |
+ |
|
| 29 | 31 |
return ping, nil |
| 30 | 32 |
} |
| ... | ... |
@@ -49,7 +49,7 @@ func newDockerCommand(dockerCli *command.DockerCli) *cobra.Command {
|
| 49 | 49 |
if err := dockerCli.Initialize(opts); err != nil {
|
| 50 | 50 |
return err |
| 51 | 51 |
} |
| 52 |
- return isSupported(cmd, dockerCli.Client().ClientVersion(), dockerCli.HasExperimental()) |
|
| 52 |
+ return isSupported(cmd, dockerCli.Client().ClientVersion(), dockerCli.OSType(), dockerCli.HasExperimental()) |
|
| 53 | 53 |
}, |
| 54 | 54 |
} |
| 55 | 55 |
cli.SetupRootCommand(cmd) |
| ... | ... |
@@ -80,7 +80,7 @@ func setFlagErrorFunc(dockerCli *command.DockerCli, cmd *cobra.Command, flags *p |
| 80 | 80 |
flagErrorFunc := cmd.FlagErrorFunc() |
| 81 | 81 |
cmd.SetFlagErrorFunc(func(cmd *cobra.Command, err error) error {
|
| 82 | 82 |
initializeDockerCli(dockerCli, flags, opts) |
| 83 |
- if err := isSupported(cmd, dockerCli.Client().ClientVersion(), dockerCli.HasExperimental()); err != nil {
|
|
| 83 |
+ if err := isSupported(cmd, dockerCli.Client().ClientVersion(), dockerCli.OSType(), dockerCli.HasExperimental()); err != nil {
|
|
| 84 | 84 |
return err |
| 85 | 85 |
} |
| 86 | 86 |
return flagErrorFunc(cmd, err) |
| ... | ... |
@@ -90,12 +90,12 @@ func setFlagErrorFunc(dockerCli *command.DockerCli, cmd *cobra.Command, flags *p |
| 90 | 90 |
func setHelpFunc(dockerCli *command.DockerCli, cmd *cobra.Command, flags *pflag.FlagSet, opts *cliflags.ClientOptions) {
|
| 91 | 91 |
cmd.SetHelpFunc(func(ccmd *cobra.Command, args []string) {
|
| 92 | 92 |
initializeDockerCli(dockerCli, flags, opts) |
| 93 |
- if err := isSupported(ccmd, dockerCli.Client().ClientVersion(), dockerCli.HasExperimental()); err != nil {
|
|
| 93 |
+ if err := isSupported(ccmd, dockerCli.Client().ClientVersion(), dockerCli.OSType(), dockerCli.HasExperimental()); err != nil {
|
|
| 94 | 94 |
ccmd.Println(err) |
| 95 | 95 |
return |
| 96 | 96 |
} |
| 97 | 97 |
|
| 98 |
- hideUnsupportedFeatures(ccmd, dockerCli.Client().ClientVersion(), dockerCli.HasExperimental()) |
|
| 98 |
+ hideUnsupportedFeatures(ccmd, dockerCli.Client().ClientVersion(), dockerCli.OSType(), dockerCli.HasExperimental()) |
|
| 99 | 99 |
|
| 100 | 100 |
if err := ccmd.Help(); err != nil {
|
| 101 | 101 |
ccmd.Println(err) |
| ... | ... |
@@ -122,7 +122,7 @@ func setValidateArgs(dockerCli *command.DockerCli, cmd *cobra.Command, flags *pf |
| 122 | 122 |
cmdArgs := ccmd.Args |
| 123 | 123 |
ccmd.Args = func(cmd *cobra.Command, args []string) error {
|
| 124 | 124 |
initializeDockerCli(dockerCli, flags, opts) |
| 125 |
- if err := isSupported(cmd, dockerCli.Client().ClientVersion(), dockerCli.HasExperimental()); err != nil {
|
|
| 125 |
+ if err := isSupported(cmd, dockerCli.Client().ClientVersion(), dockerCli.OSType(), dockerCli.HasExperimental()); err != nil {
|
|
| 126 | 126 |
return err |
| 127 | 127 |
} |
| 128 | 128 |
return cmdArgs(cmd, args) |
| ... | ... |
@@ -198,7 +198,7 @@ func dockerPreRun(opts *cliflags.ClientOptions) {
|
| 198 | 198 |
} |
| 199 | 199 |
} |
| 200 | 200 |
|
| 201 |
-func hideUnsupportedFeatures(cmd *cobra.Command, clientVersion string, hasExperimental bool) {
|
|
| 201 |
+func hideUnsupportedFeatures(cmd *cobra.Command, clientVersion, osType string, hasExperimental bool) {
|
|
| 202 | 202 |
cmd.Flags().VisitAll(func(f *pflag.Flag) {
|
| 203 | 203 |
// hide experimental flags |
| 204 | 204 |
if !hasExperimental {
|
| ... | ... |
@@ -208,10 +208,9 @@ func hideUnsupportedFeatures(cmd *cobra.Command, clientVersion string, hasExperi |
| 208 | 208 |
} |
| 209 | 209 |
|
| 210 | 210 |
// hide flags not supported by the server |
| 211 |
- if !isFlagSupported(f, clientVersion) {
|
|
| 211 |
+ if !isOSTypeSupported(f, osType) || !isVersionSupported(f, clientVersion) {
|
|
| 212 | 212 |
f.Hidden = true |
| 213 | 213 |
} |
| 214 |
- |
|
| 215 | 214 |
}) |
| 216 | 215 |
|
| 217 | 216 |
for _, subcmd := range cmd.Commands() {
|
| ... | ... |
@@ -229,7 +228,7 @@ func hideUnsupportedFeatures(cmd *cobra.Command, clientVersion string, hasExperi |
| 229 | 229 |
} |
| 230 | 230 |
} |
| 231 | 231 |
|
| 232 |
-func isSupported(cmd *cobra.Command, clientVersion string, hasExperimental bool) error {
|
|
| 232 |
+func isSupported(cmd *cobra.Command, clientVersion, osType string, hasExperimental bool) error {
|
|
| 233 | 233 |
// We check recursively so that, e.g., `docker stack ls` will return the same output as `docker stack` |
| 234 | 234 |
if !hasExperimental {
|
| 235 | 235 |
for curr := cmd; curr != nil; curr = curr.Parent() {
|
| ... | ... |
@@ -247,8 +246,12 @@ func isSupported(cmd *cobra.Command, clientVersion string, hasExperimental bool) |
| 247 | 247 |
|
| 248 | 248 |
cmd.Flags().VisitAll(func(f *pflag.Flag) {
|
| 249 | 249 |
if f.Changed {
|
| 250 |
- if !isFlagSupported(f, clientVersion) {
|
|
| 251 |
- errs = append(errs, fmt.Sprintf("\"--%s\" requires API version %s, but the Docker daemon API version is %s", f.Name, getFlagVersion(f), clientVersion))
|
|
| 250 |
+ if !isVersionSupported(f, clientVersion) {
|
|
| 251 |
+ errs = append(errs, fmt.Sprintf("\"--%s\" requires API version %s, but the Docker daemon API version is %s", f.Name, getFlagAnnotation(f, "version"), clientVersion))
|
|
| 252 |
+ return |
|
| 253 |
+ } |
|
| 254 |
+ if !isOSTypeSupported(f, osType) {
|
|
| 255 |
+ errs = append(errs, fmt.Sprintf("\"--%s\" requires the Docker daemon to run on %s, but the Docker daemon is running on %s", f.Name, getFlagAnnotation(f, "ostype"), osType))
|
|
| 252 | 256 |
return |
| 253 | 257 |
} |
| 254 | 258 |
if _, ok := f.Annotations["experimental"]; ok && !hasExperimental {
|
| ... | ... |
@@ -263,20 +266,27 @@ func isSupported(cmd *cobra.Command, clientVersion string, hasExperimental bool) |
| 263 | 263 |
return nil |
| 264 | 264 |
} |
| 265 | 265 |
|
| 266 |
-func getFlagVersion(f *pflag.Flag) string {
|
|
| 267 |
- if flagVersion, ok := f.Annotations["version"]; ok && len(flagVersion) == 1 {
|
|
| 268 |
- return flagVersion[0] |
|
| 266 |
+func getFlagAnnotation(f *pflag.Flag, annotation string) string {
|
|
| 267 |
+ if value, ok := f.Annotations[annotation]; ok && len(value) == 1 {
|
|
| 268 |
+ return value[0] |
|
| 269 | 269 |
} |
| 270 | 270 |
return "" |
| 271 | 271 |
} |
| 272 | 272 |
|
| 273 |
-func isFlagSupported(f *pflag.Flag, clientVersion string) bool {
|
|
| 274 |
- if v := getFlagVersion(f); v != "" {
|
|
| 273 |
+func isVersionSupported(f *pflag.Flag, clientVersion string) bool {
|
|
| 274 |
+ if v := getFlagAnnotation(f, "version"); v != "" {
|
|
| 275 | 275 |
return versions.GreaterThanOrEqualTo(clientVersion, v) |
| 276 | 276 |
} |
| 277 | 277 |
return true |
| 278 | 278 |
} |
| 279 | 279 |
|
| 280 |
+func isOSTypeSupported(f *pflag.Flag, osType string) bool {
|
|
| 281 |
+ if v := getFlagAnnotation(f, "ostype"); v != "" && osType != "" {
|
|
| 282 |
+ return osType == v |
|
| 283 |
+ } |
|
| 284 |
+ return true |
|
| 285 |
+} |
|
| 286 |
+ |
|
| 280 | 287 |
// hasTags return true if any of the command's parents has tags |
| 281 | 288 |
func hasTags(cmd *cobra.Command) bool {
|
| 282 | 289 |
for curr := cmd; curr != nil; curr = curr.Parent() {
|