github.com/containers/podman/v5@v5.1.0-rc1/test/apiv2/20-containers.at (about)

     1  # -*- sh -*-
     2  #
     3  # test container-related endpoints
     4  #
     5  
     6  # WORKDIR=/data
     7  ENV_WORKDIR_IMG=quay.io/libpod/testimage:20240123
     8  MultiTagName=localhost/test/testformultitag:tag
     9  
    10  podman pull $IMAGE &>/dev/null
    11  podman tag $IMAGE $MultiTagName
    12  podman pull $ENV_WORKDIR_IMG &>/dev/null
    13  # Unimplemented
    14  #t POST libpod/containers/create '' 201 'sdf'
    15  
    16  # Ensure clean slate
    17  podman rm -a -f &>/dev/null
    18  
    19  t GET "libpod/containers/json (at start: clean slate)" 200 \
    20    "[]" \
    21    length=0
    22  # check content type: https://github.com/containers/podman/issues/14647
    23  response_headers=$(cat "$WORKDIR/curl.headers.out")
    24  like "$response_headers" ".*Content-Type: application/json.*" "header does not contain application/json"
    25  
    26  # Regression test for #12904 (race condition in logging code)
    27  mytext="hi-there-$(random_string 15)"
    28  podman run --rm -d --replace --name foo $IMAGE sh -c "echo $mytext;sleep 42"
    29  # Logs output is prepended by ^A^Y (stdout = 1, length = 25 (with newline))
    30  # Looks like it is missing the required 0 bytes from the message, why?
    31  t POST "containers/foo/attach?logs=true&stream=false" 200 \
    32    $'\001\031'$mytext
    33  
    34  # check old docker header
    35  response_headers=$(cat "$WORKDIR/curl.headers.out")
    36  like "$response_headers" ".*Content-Type: application/vnd\.docker\.raw-stream.*" "vnd.docker.raw-stream docker v1.40"
    37  # check new vnd.docker.multiplexed-stream header
    38  t POST "/v1.42/containers/foo/attach?logs=true&stream=false" 200
    39  response_headers=$(cat "$WORKDIR/curl.headers.out")
    40  like "$response_headers" ".*Content-Type: application/vnd\.docker\.multiplexed-stream.*" "vnd.docker.multiplexed-stream docker v1.42"
    41  t POST "/v4.6.0/libpod/containers/foo/attach?logs=true&stream=false" 200
    42  response_headers=$(cat "$WORKDIR/curl.headers.out")
    43  like "$response_headers" ".*Content-Type: application/vnd\.docker\.raw-stream.*" "vnd.docker.raw-stream libpod v4.6.0"
    44  t POST "/v4.7.0/libpod/containers/foo/attach?logs=true&stream=false" 200
    45  response_headers=$(cat "$WORKDIR/curl.headers.out")
    46  like "$response_headers" ".*Content-Type: application/vnd\.docker\.multiplexed-stream.*" "vnd.docker.multiplexed-stream libpod v4.7.0"
    47  
    48  t POST "containers/foo/attach?logs=true&stream=false" 101
    49  response_headers=$(cat "$WORKDIR/curl.headers.out")
    50  like "$response_headers" ".*Content-Type: application/vnd\.docker\.raw-stream.*" "hijacked connection header: Content-type: application/vnd.docker.raw-stream"
    51  like "$response_headers" ".*Upgrade: tcp.*" "hijacked connection header: Upgrade: tcp"
    52  
    53  t POST "containers/foo/kill" 204
    54  
    55  podman run --replace --name=foo -v /tmp:/tmp $IMAGE true
    56  # cannot kill non-running container
    57  t POST "containers/foo/kill" 409
    58  t POST "libpod/containers/foo/kill" 409
    59  
    60  t GET libpod/containers/json 200 length=0
    61  
    62  # bad all input
    63  t GET libpod/containers/json?all='garb1age' 500 \
    64      .cause="schema: error converting value for \"all\""
    65  
    66  t GET libpod/containers/json?all=true 200 \
    67    length=1 \
    68    .[0].Id~[0-9a-f]\\{64\\} \
    69    .[0].Image=$IMAGE \
    70    .[0].Command[0]="true" \
    71    .[0].State~\\\(exited\\\|stopped\\\) \
    72    .[0].ExitCode=0 \
    73    .[0].Mounts~.*/tmp \
    74    .[0].IsInfra=false
    75  
    76  # Test compat API for Network Settings (.Network is N/A when rootless)
    77  network_expect="Networks.pasta.NetworkID=pasta"
    78  if root; then
    79      network_expect="Networks.podman.NetworkID=podman"
    80  fi
    81  t GET /containers/json?all=true 200 \
    82    length=1 \
    83    .[0].Id~[0-9a-f]\\{64\\} \
    84    .[0].Image=$IMAGE \
    85    .[0].Mounts~.*/tmp \
    86    .[0].NetworkSettings.$network_expect
    87  
    88  # compat API imageid with sha256: prefix
    89  t GET containers/json?limit=1 200 \
    90    .[0].ImageID~sha256:[0-9a-f]\\{64\\}
    91  
    92  # Make sure `limit` works.
    93  t GET libpod/containers/json?limit=1 200 \
    94    length=1 \
    95    .[0].Id~[0-9a-f]\\{64\\} \
    96    .[0].Image=$IMAGE \
    97    .[0].Command[0]="true" \
    98    .[0].State~\\\(exited\\\|stopped\\\) \
    99    .[0].ExitCode=0 \
   100    .[0].IsInfra=false
   101  
   102  # Make sure `last` works, which is an alias for `limit`.
   103  # See https://github.com/containers/podman/issues/6413.
   104  t GET libpod/containers/json?last=1 200 \
   105    length=1 \
   106    .[0].Id~[0-9a-f]\\{64\\} \
   107    .[0].Image=$IMAGE \
   108    .[0].Command[0]="true" \
   109    .[0].State~\\\(exited\\\|stopped\\\) \
   110    .[0].ExitCode=0 \
   111    .[0].IsInfra=false
   112  
   113  cid=$(jq -r '.[0].Id' <<<"$output")
   114  
   115  if root; then
   116      t GET libpod/containers/stats?containers='[$cid]' 200
   117  else
   118      if have_cgroupsv2; then
   119          t GET libpod/containers/stats?containers='[$cid]' 200
   120      else
   121          t GET libpod/containers/stats?containers='[$cid]' 409
   122      fi
   123  fi
   124  
   125  # max_usage is not set for cgroupv2
   126  if have_cgroupsv2; then
   127      t GET libpod/containers/stats?containers='[$cid]' 200 \
   128      .memory_stats.max_usage=null
   129  fi
   130  
   131  t DELETE libpod/containers/$cid 200 .[0].Id=$cid
   132  
   133  # Issue #14676: make sure the stats show the memory limit specified for the container
   134  if root; then
   135      CTRNAME=ctr-with-limit
   136      podman run --name $CTRNAME -d -m 512m -v /tmp:/tmp $IMAGE top
   137  
   138      t GET libpod/containers/$CTRNAME/stats?stream=false 200 \
   139      .memory_stats.limit=536870912 \
   140      .Id~[0-9a-f]\\{64\\}
   141  
   142      # Make sure docker compat endpoint shows "id" lowercase
   143      t GET containers/$CTRNAME/stats?stream=false 200 \
   144      .memory_stats.limit=536870912 \
   145      .id~[0-9a-f]\\{64\\}
   146  
   147      t GET containers/$CTRNAME/top?stream=false 200 \
   148        .Titles='[
   149    "UID",
   150    "PID",
   151    "PPID",
   152    "C",
   153    "STIME",
   154    "TTY",
   155    "TIME",
   156    "CMD"
   157  ]'
   158  
   159      podman rm -f $CTRNAME
   160  fi
   161  
   162  CTRNAME=test123
   163  podman run --name $CTRNAME -d $IMAGE top
   164  t GET libpod/containers/$CTRNAME/top?ps_args=--invalid 500 \
   165    .cause~".*unknown gnu long option.*"
   166  t GET containers/$CTRNAME/top?ps_args=--invalid 500 \
   167    .cause~".*unknown gnu long option.*"
   168  
   169  podman rm -f $CTRNAME
   170  
   171  # Issue #15765: make sure the memory limit is capped
   172  if root; then
   173      CTRNAME=ctr-with-limit
   174      podman run --name $CTRNAME -d -m 512m -v /tmp:/tmp $IMAGE top
   175  
   176      t GET libpod/containers/$CTRNAME/stats?stream=false 200 \
   177      .memory_stats.limit!=18446744073709552000
   178  
   179      podman rm -f $CTRNAME
   180  fi
   181  
   182  # Container create without existing image should return 404
   183  t POST libpod/containers/create Image="foo" 404 \
   184    .cause="image not known"
   185  
   186  # Issue #6799: it should be possible to start a container, even w/o args.
   187  t POST libpod/containers/create?name=test_noargs Image=${IMAGE} 201 \
   188    .Id~[0-9a-f]\\{64\\}
   189  cid=$(jq -r '.Id' <<<"$output")
   190  # Prior to the fix in #6835, this would fail 500 "args must not be empty"
   191  t POST   libpod/containers/${cid}/start 204
   192  # Container should exit almost immediately. Wait for it, confirm successful run
   193  t POST   "libpod/containers/${cid}/wait?condition=stopped&condition=exited"  200 '0'
   194  t GET    libpod/containers/${cid}/json 200 \
   195    .Id=$cid \
   196    .State.Status~\\\(exited\\\|stopped\\\) \
   197    .State.Running=false \
   198    .State.ExitCode=0 \
   199    .Config.Umask=0022 # regression check for #15036
   200  t DELETE libpod/containers/$cid 200 .[0].Id=$cid
   201  
   202  CNAME=myfoo
   203  podman run -d --name $CNAME $IMAGE top
   204  t GET libpod/containers/json?all=true 200 \
   205    .[0].Id~[0-9a-f]\\{64\\}
   206  cid=$(jq -r '.[0].Id' <<<"$output")
   207  
   208  # No such container
   209  t POST "libpod/commit?container=nonesuch" 404
   210  
   211  # Comment can only be used with docker format, not OCI
   212  cparam="repo=newrepo&comment=foo&author=bob"
   213  t POST "libpod/commit?container=$CNAME&$cparam"  500 \
   214    .cause="messages are only compatible with the docker image format (-f docker)"
   215  
   216  # Commit a new image from the container
   217  t POST "libpod/commit?container=$CNAME" 200 \
   218    .Id~[0-9a-f]\\{64\\}
   219  iid=$(jq -r '.Id' <<<"$output")
   220  t GET libpod/images/$iid/json 200 \
   221    .RepoTags[0]=null \
   222    .Author="" \
   223    .Comment=""
   224  
   225  # Commit a new image w/o tag
   226  cparam="repo=newrepo&comment=foo&author=bob&format=docker"
   227  t POST "libpod/commit?container=$CNAME&$cparam" 200
   228  t GET libpod/images/newrepo:latest/json 200 \
   229    .RepoTags[0]=localhost/newrepo:latest	\
   230    .Author=bob \
   231    .Comment=foo
   232  
   233  # Commit a new image w/ specified tag and author
   234  cparam="repo=newrepo&tag=v1&author=alice"
   235  t POST "libpod/commit?container=$cid&$cparam&pause=false" 200
   236  t GET libpod/images/newrepo:v1/json 200 \
   237    .RepoTags[0]=localhost/newrepo:v1     \
   238    .Author=alice
   239  
   240  # Commit a new image w/ full parameters
   241  cparam="repo=newrepo&tag=v2&comment=bar&author=eric"
   242  cparam="$cparam&format=docker&changes=CMD=/bin/foo"
   243  
   244  if root || have_cgroupsv2; then
   245      t POST "libpod/commit?container=${cid:0:12}&$cparam&pause=true" 200
   246      t GET libpod/images/newrepo:v2/json 200 \
   247        .RepoTags[0]=localhost/newrepo:v2	\
   248        .Author=eric \
   249        .Comment=bar \
   250        .Config.Cmd[-1]="/bin/foo"
   251      t DELETE images/localhost/newrepo:v2?force=true 200
   252  else
   253      # cgroupsv1 rootless : pause is not supported in cgroups v1 rootless
   254      t POST "libpod/commit?container=${cid:0:12}&$cparam&pause=true" 500 \
   255        .cause="this container does not have a cgroup" \
   256        .message~".*pause containers on rootless containers with cgroup V1"
   257  fi
   258  
   259  # Create a container for testing the container initializing later
   260  podman create -t -i --name myctr $IMAGE ls
   261  
   262  # Check configuration before initializing
   263  t GET libpod/containers/myctr/json 200 \
   264    .Id~[0-9a-f]\\{64\\} \
   265    .State.Status="created" \
   266    .State.Pid=0 \
   267    .ResolvConfPath="" \
   268    .HostnamePath="" \
   269    .HostsPath="" \
   270    .NetworkSettings.SandboxKey=""
   271  
   272  cpid_file=$(jq -r '.ConmonPidFile' <<<"$output")
   273  userdata_path=$(dirname $cpid_file)
   274  
   275  # Initializing the container
   276  t POST libpod/containers/myctr/init 204
   277  
   278  # Check configuration after initializing
   279  t GET libpod/containers/myctr/json 200 \
   280    .Id~[0-9a-f]\\{64\\} \
   281    .State.Status="initialized" \
   282    .State.Pid~[0-9]\\{1\,8\\} \
   283    .ResolvConfPath=$userdata_path/resolv.conf \
   284    .HostnamePath=$userdata_path/hostname \
   285    .HostsPath=$userdata_path/hosts \
   286    .NetworkSettings.SandboxKey~.*/netns/netns- \
   287    .OCIConfigPath~.*config\.json \
   288    .GraphDriver.Data.MergedDir~.*merged
   289  
   290  # Test TS are in UTC
   291  t GET containers/myctr/json 200 \
   292    .Created~.*Z \
   293    .State.StartedAt~.*Z \
   294    .State.FinishedAt~.*Z
   295  
   296  t DELETE images/localhost/newrepo:latest?force=true 200
   297  t DELETE images/localhost/newrepo:v1?force=true 200
   298  t DELETE libpod/containers/$cid?force=true 200 .[0].Id=$cid
   299  t DELETE libpod/containers/myctr 200
   300  t DELETE libpod/containers/bogus 404
   301  
   302  
   303  # test apiv2 create container with correct entrypoint and cmd
   304  # --data '{"Image":"quay.io/libpod/alpine_labels:latest","Entrypoint":["echo"],"Cmd":["param1","param2"]}'
   305  t POST containers/create \
   306    Image=$IMAGE \
   307    Entrypoint='["echo"]' \
   308    Cmd='["param1","param2"]' \
   309    201 \
   310    .Id~[0-9a-f]\\{64\\}
   311  cid=$(jq -r '.Id' <<<"$output")
   312  t GET containers/$cid/json 200 \
   313    .Config.Entrypoint[0]="echo" \
   314    .Config.Cmd[0]="param1" \
   315    .Config.Cmd[1]="param2" \
   316    .Path="echo" \
   317    .Args[0]="param1" \
   318    .Args[1]="param2"
   319  t DELETE containers/$cid 204
   320  
   321  # test only set the entrypoint, Cmd should be []
   322  t POST containers/create \
   323    Image=$IMAGE \
   324    Entrypoint='["echo","param1"]' \
   325    201 \
   326    .Id~[0-9a-f]\\{64\\}
   327  cid=$(jq -r '.Id' <<<"$output")
   328  t GET containers/$cid/json 200 \
   329    .Config.Entrypoint[0]="echo" \
   330    .Config.Entrypoint[1]="param1" \
   331    .Config.Cmd='[]' \
   332    .Path="echo" \
   333    .Args[0]="param1"
   334  
   335  # create a running container for after
   336  t POST containers/create Image=$IMAGE Entrypoint='["top"]' 201 \
   337    .Id~[0-9a-f]\\{64\\}
   338  cid_top=$(jq -r '.Id' <<<"$output")
   339  
   340  t GET containers/${cid_top}/json 200 \
   341    .Config.Entrypoint[0]="top" \
   342    .Config.Cmd='[]' \
   343    .Config.StopTimeout="10" \
   344    .Path="top" \
   345    .NetworkSettings.Networks.podman.NetworkID=podman
   346  t POST  containers/${cid_top}/start 204
   347  # make sure the container is running
   348  t GET containers/${cid_top}/json 200 \
   349    .State.Status="running"
   350  
   351  # 0 means unlimited, need same with docker
   352  t GET containers/json?limit=0 200 \
   353    .[0].Id~[0-9a-f]\\{64\\}
   354  
   355  t GET 'containers/json?limit=0&all=1' 200 \
   356    .[0].Id~[0-9a-f]\\{64\\} \
   357    .[1].Id~[0-9a-f]\\{64\\}
   358  
   359  t GET containers/json?limit=2 200 length=2
   360  
   361  # Filter with two ids should return both container
   362  t GET containers/json?filters='{"id":["'${cid}'","'${cid_top}'"]}&all=1' 200 length=2
   363  # Filter with two ids and status running should return only 1 container
   364  t GET containers/json?filters='{"id":["'${cid}'","'${cid_top}'"],"status":["running"]}&all=1' 200 \
   365    length=1 \
   366    .[0].Id=${cid_top}
   367  
   368  t POST containers/${cid_top}/stop 204
   369  
   370  t DELETE containers/$cid 204
   371  t DELETE containers/$cid_top 204
   372  
   373  # test the WORKDIR and StopSignal
   374  t POST containers/create \
   375    Image=$ENV_WORKDIR_IMG \
   376    WorkingDir=/dataDir \
   377    StopSignal=\"9\" \
   378    201 \
   379    .Id~[0-9a-f]\\{64\\}
   380  cid=$(jq -r '.Id' <<<"$output")
   381  t GET containers/$cid/json 200 \
   382    .Config.WorkingDir="/dataDir" \
   383    .Config.StopSignal="9"
   384  
   385  t DELETE containers/$cid 204
   386  
   387  # when the image had multi tags, the container's Image should be correct
   388  # Fixes https://github.com/containers/podman/issues/8547
   389  t POST containers/create Image=${MultiTagName} 201 \
   390    .Id~[0-9a-f]\\{64\\}
   391  cid=$(jq -r '.Id' <<<"$output")
   392  t GET containers/$cid/json 200 \
   393    .Config.Image=${MultiTagName} \
   394    .Image~sha256:[0-9a-f]\\{64\\}
   395  
   396  t DELETE containers/$cid 204
   397  t DELETE images/${MultiTagName} 200
   398  # vim: filetype=sh
   399  
   400  # Test Volumes field adds an anonymous volume
   401  t POST containers/create Image=$IMAGE Volumes='{"/test":{}}' 201 \
   402    .Id~[0-9a-f]\\{64\\}
   403  cid=$(jq -r '.Id' <<<"$output")
   404  t GET containers/$cid/json 200 \
   405    .Mounts[0].Destination="/test"
   406  
   407  t DELETE containers/$cid?v=true 204
   408  
   409  # Test Volumes with bind mount, for some reason docker-py sets this #18454
   410  t POST containers/create Image=$IMAGE Volumes='{"/test/":{}}'  HostConfig='{"Binds":["/tmp:/test/:ro"]}'  201 \
   411    .Id~[0-9a-f]\\{64\\}
   412  cid=$(jq -r '.Id' <<<"$output")
   413  t GET containers/$cid/json 200 \
   414    .Mounts[0].Destination="/test/"
   415  
   416  t DELETE containers/$cid?v=true 204
   417  
   418  # test port mapping
   419  podman run -d --rm --name bar -p 8080:9090 $IMAGE top
   420  
   421  t GET containers/json 200 \
   422    .[0].Ports[0].PrivatePort=9090 \
   423    .[0].Ports[0].PublicPort=8080 \
   424    .[0].Ports[0].Type="tcp"
   425  
   426  podman stop bar
   427  
   428  #compat api list containers sanity checks
   429  podman run -d --rm --name labelcontainer_with $ENV_WORKDIR_IMG top
   430  podman run -d --rm --name labelcontainer_without $IMAGE top
   431  
   432  t GET containers/json?filters='garb1age}' 500 \
   433      .cause="invalid character 'g' looking for beginning of value"
   434  t GET containers/json?filters='{"label":["testl' 500 \
   435      .cause="unexpected end of JSON input"
   436  
   437  
   438  #libpod api list containers sanity checks
   439  t GET libpod/containers/json?filters='{"status":["removing"]}' 200 length=0
   440  t GET libpod/containers/json?filters='{"status":["bogus"]}' 500 \
   441      .cause="invalid argument"
   442  t GET libpod/containers/json?filters='garb1age}' 500 \
   443      .cause="invalid character 'g' looking for beginning of value"
   444  t GET libpod/containers/json?filters='{"label":["testl' 500 \
   445      .cause="unexpected end of JSON input"
   446  
   447  # Prune containers - bad filter input
   448  t POST containers/prune?filters='garb1age}' 500 \
   449      .cause="invalid character 'g' looking for beginning of value"
   450  t POST libpod/containers/prune?filters='garb1age}' 500 \
   451      .cause="invalid character 'g' looking for beginning of value"
   452  
   453  # Prune containers with illformed label
   454  t POST containers/prune?filters='{"label":["tes' 500 \
   455      .cause="unexpected end of JSON input"
   456  t POST libpod/containers/prune?filters='{"label":["tes' 500 \
   457      .cause="unexpected end of JSON input"
   458  
   459  t GET libpod/containers/json?filters='{"label":["created_by"]}' 200 \
   460    length=1 \
   461    .[0].Names[0]="labelcontainer_with"
   462  t GET libpod/containers/json?filters='{"label!":["created_by"]}' 200 \
   463    length=1 \
   464    .[0].Names[0]="labelcontainer_without"
   465  t GET libpod/containers/json?filters='{"label!":["testlabel"]}' 200 length=2
   466  
   467  podman stop -t0 labelcontainer_with labelcontainer_without
   468  
   469  # libpod api: do not use list filters for prune
   470  t POST libpod/containers/prune?filters='{"name":["anyname"]}' 500 \
   471      .cause="name is an invalid filter"
   472  t POST libpod/containers/prune?filters='{"id":["anyid"]}' 500 \
   473      .cause="id is an invalid filter"
   474  t POST libpod/containers/prune?filters='{"network":["anynetwork"]}' 500 \
   475      .cause="network is an invalid filter"
   476  
   477  # compat api: do not use list filters for prune
   478  t POST containers/prune?filters='{"name":["anyname"]}' 500 \
   479      .cause="name is an invalid filter"
   480  t POST containers/prune?filters='{"id":["anyid"]}' 500 \
   481      .cause="id is an invalid filter"
   482  t POST containers/prune?filters='{"network":["anynetwork"]}' 500 \
   483      .cause="network is an invalid filter"
   484  
   485  # Test CPU limit (NanoCPUs)
   486  nanoCpu=500000
   487  if have_cgroupsv2; then
   488      t POST containers/create Image=$IMAGE HostConfig='{"NanoCpus":500000}' 201 \
   489        .Id~[0-9a-f]\\{64\\}
   490  else
   491      if root; then
   492        # cgroupsv1 rootful : NanoCpus needs to set more than 10000000
   493        t POST containers/create Image=$IMAGE HostConfig='{"NanoCpus":500000}' 500 \
   494          .cause="CPU cfs quota cannot be less than 1ms (i.e. 1000)"
   495        t POST containers/create Image=$IMAGE HostConfig='{"NanoCpus":10000000}' 201 \
   496          .Id~[0-9a-f]\\{64\\}
   497        nanoCpu=10000000
   498      else
   499        # cgroupsv1 rootless : Resource limits that include NanoCPUs are not supported and ignored
   500        t POST containers/create Image=$IMAGE HostConfig='{"NanoCpus":500000}' 201 \
   501          .Id~[0-9a-f]\\{64\\}
   502        nanoCpu=0
   503      fi
   504  fi
   505  
   506  cid=$(jq -r '.Id' <<<"$output")
   507  t GET containers/$cid/json 200 \
   508    .HostConfig.NanoCpus=$nanoCpu
   509  
   510  t DELETE containers/$cid?v=true 204
   511  
   512  # Test Compat Create with default network mode (#10569)
   513  t POST containers/create Image=$IMAGE HostConfig='{"NetworkMode":"default"}' 201 \
   514    .Id~[0-9a-f]\\{64\\}
   515  cid=$(jq -r '.Id' <<<"$output")
   516  t GET containers/$cid/json 200 \
   517    .HostConfig.NetworkMode="bridge"
   518  
   519  t DELETE containers/$cid?v=true 204
   520  
   521  # test create with default netns="host"
   522  stop_service
   523  CONTAINERS_CONF=$TESTS_DIR/containers.host-netns.conf start_service
   524  
   525  # check that the default docker netns "default" is rewritten to "host"
   526  # when the containers.conf explicitly uses "host"
   527  t POST containers/create Image=$IMAGE HostConfig='{"NetworkMode":"default"}' 201 \
   528    .Id~[0-9a-f]\\{64\\}
   529  cid=$(jq -r '.Id' <<<"$output")
   530  t GET containers/$cid/json 200 \
   531    .HostConfig.NetworkMode="host"
   532  
   533  t DELETE containers/$cid?v=true 204
   534  
   535  # test create container like Docker >= 25 cli: NetworkMode="default" but EndpointsConfig struct is explicitly set and netns="host"
   536  t POST containers/create \
   537    Image=$IMAGE \
   538    HostConfig='{"NetworkMode":"default"}' \
   539    NetworkingConfig='{"EndpointsConfig":{"default":{"IPAMConfig":null,"Links":null,"Aliases":null,"MacAddress":"","NetworkID":"","EndpointID":"","Gateway":"","IPAddress":"","IPPrefixLen":0,"IPv6Gateway":"","GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"DriverOpts":null,"DNSNames":null}}}' \
   540    201 \
   541    .Id~[0-9a-f]\\{64\\}
   542  cid=$(jq -r '.Id' <<<"$output")
   543  t GET containers/$cid/json 200 \
   544    .HostConfig.NetworkMode="host"
   545  
   546  t DELETE containers/$cid?v=true 204
   547  
   548  # test creating a container fails with netns="hosts" on podman side but keep using the default network mode
   549  # on docker CLI side and trying to use --ip 1.2.3.4 which is only valid for the bridge network mode (docker CLI
   550  # will assume the default is the bridge mode, so it's valid from docker CLI point of view).
   551  t POST containers/create \
   552    Image=$IMAGE \
   553    HostConfig='{"NetworkMode":"default"}' \
   554    NetworkingConfig='{"EndpointsConfig":{"default":{"IPAMConfig":null,"Links":null,"Aliases":null,"MacAddress":"","NetworkID":"","EndpointID":"","Gateway":"","IPAddress":"1.2.3.4","IPPrefixLen":0,"IPv6Gateway":"","GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"DriverOpts":null,"DNSNames":null}}}' \
   555    500 \
   556      .cause="networks and static ip/mac address can only be used with Bridge mode networking"
   557  
   558  # Restart with the default containers.conf for next tests.
   559  stop_service
   560  start_service
   561  
   562  # Test Compat Create with healthcheck, check default values
   563  t POST containers/create Image=$IMAGE Cmd='["top"]' Healthcheck='{"Test":["true"]}' 201 \
   564    .Id~[0-9a-f]\\{64\\}
   565  cid=$(jq -r '.Id' <<<"$output")
   566  t GET containers/$cid/json 200 \
   567    .Config.Healthcheck.Interval=30000000000 \
   568    .Config.Healthcheck.Timeout=30000000000 \
   569    .Config.Healthcheck.Retries=3
   570  
   571  # compat api: Test for mount options support
   572  # Sigh, JSON can't handle octal. 0755(octal) = 493(decimal)
   573  payload='{"Mounts":[{"Type":"tmpfs","Target":"/mnt/scratch","TmpfsOptions":{"SizeBytes":1024,"Mode":493}}]}'
   574  t POST containers/create Image=$IMAGE HostConfig="$payload" 201 .Id~[0-9a-f]\\{64\\}
   575  cid=$(jq -r '.Id' <<<"$output")
   576  t GET containers/$cid/json 200 \
   577      .HostConfig.Tmpfs['"/mnt/scratch"']~.*size=1024.* \
   578      .HostConfig.Tmpfs['"/mnt/scratch"']~.*mode=755.*
   579  
   580  t DELETE containers/$cid?v=true 204
   581  
   582  # compat api: tmpfs without mount options
   583  payload='{"Mounts":[{"Type":"tmpfs","Target":"/mnt/scratch"}]}'
   584  t POST containers/create Image=$IMAGE HostConfig="$payload" 201 .Id~[0-9a-f]\\{64\\}
   585  cid=$(jq -r '.Id' <<<"$output")
   586  t GET containers/$cid/json 200 \
   587      .HostConfig.Tmpfs['"/mnt/scratch"']~.*tmpcopyup.* \
   588  
   589  t DELETE containers/$cid?v=true 204
   590  
   591  # compat api: bind mount without mount options
   592  payload='{"Mounts":[{"Type":"bind","Source":"/tmp","Target":"/mnt"}]}'
   593  t POST containers/create Image=$IMAGE HostConfig="$payload" 201 .Id~[0-9a-f]\\{64\\}
   594  cid=$(jq -r '.Id' <<<"$output")
   595  t GET containers/$cid/json 200 \
   596      .HostConfig.Binds[0]~/tmp:/mnt:.* \
   597  
   598  t DELETE containers/$cid?v=true 204
   599  
   600  # test apiv2 create/commit
   601  t POST containers/create \
   602    Image=$IMAGE \
   603    Entrypoint='["echo"]' \
   604    Cmd='["param1","param2"]' \
   605    201 \
   606    .Id~[0-9a-f]\\{64\\}
   607  cid=$(jq -r '.Id' <<<"$output")
   608  
   609  # No such container
   610  t POST "commit?container=nonesuch" 404
   611  
   612  cparam="repo=newrepo&tag=v3&comment=abcd&author=eric"
   613  cparam="$cparam&format=docker&changes=CMD%20/bin/bar%0aEXPOSE%209090"
   614  t POST "commit?container=${cid:0:12}&$cparam" 201 \
   615    .Id~[0-9a-f]\\{64\\}
   616  iid=$(jq -r '.Id' <<<"$output")
   617  t GET images/$iid/json 200 \
   618    .RepoTags[0]=docker.io/library/newrepo:v3 \
   619    .Config.ExposedPorts~.*"9090/tcp" \
   620    .Config.Cmd~.*"/bin/bar" \
   621    .Comment="abcd"
   622  
   623  t DELETE containers/$cid 204
   624  t DELETE images/docker.io/library/newrepo:v3?force=false 200
   625  
   626  # test create without default no_hosts
   627  t POST containers/create \
   628    Image=$IMAGE \
   629    201 \
   630    .Id~[0-9a-f]\\{64\\}
   631  cid=$(jq -r '.Id' <<<"$output")
   632  
   633  t POST libpod/containers/$cid/init 204
   634  
   635  t GET libpod/containers/$cid/json 200
   636  
   637  cpid_file=$(jq -r '.ConmonPidFile' <<<"$output")
   638  userdata_path=$(dirname $cpid_file)
   639  
   640  t GET libpod/containers/$cid/json 200 \
   641    .HostsPath=$userdata_path/hosts
   642  
   643  t DELETE containers/$cid 204
   644  
   645  # test create with default no_hosts=true
   646  stop_service
   647  
   648  CONTAINERS_CONF=$TESTS_DIR/containers.no_hosts.conf start_service
   649  
   650  # check docker and libpod endpoint
   651  for endpoint in containers/create libpod/containers/create; do
   652    t POST $endpoint \
   653      Image=$IMAGE \
   654      201 \
   655      .Id~[0-9a-f]\\{64\\}
   656    cid=$(jq -r '.Id' <<<"$output")
   657  
   658    t POST libpod/containers/$cid/init 204
   659  
   660    t GET libpod/containers/$cid/json 200 \
   661      .HostsPath=""
   662  
   663    t DELETE containers/$cid 204
   664  done
   665  
   666  stop_service
   667  start_service
   668  
   669  # Our states are different from Docker's.
   670  # Regression test for #14700 (Docker compat returning unknown "initialized" for status.status) to ensure the stay compatible
   671  podman create --name status-test $IMAGE sh -c "sleep 3"
   672  t GET containers/status-test/json 200 .State.Status="created"
   673  
   674  podman init status-test
   675  t GET containers/status-test/json 200 .State.Status="created"
   676  
   677  podman start status-test
   678  t GET containers/status-test/json 200 .State.Status="running"
   679  
   680  # cgroupsv1 rootless : pause and unpause are not supported in cgroups v1 rootless
   681  if root || have_cgroupsv2; then
   682      podman pause status-test
   683      t GET containers/status-test/json 200 .State.Status="paused"
   684  
   685      podman unpause status-test
   686      t GET containers/status-test/json 200 .State.Status="running"
   687  fi
   688  
   689  podman stop status-test &
   690  sleep 1
   691  t GET containers/status-test/json 200 .State.Status="stopping"
   692  
   693  sleep 3
   694  t GET containers/status-test/json 200 .State.Status="exited"
   695  
   696  # test podman generate spec as input for the api
   697  cname=specgen$(random_string 10)
   698  podman create --name=$cname $IMAGE
   699  
   700  TMPD=$(mktemp -d podman-apiv2-test.build.XXXXXXXX)
   701  
   702  podman generate spec -f ${TMPD}/myspec.json -c $cname
   703  
   704  # Create a container based on that spec
   705  t POST libpod/containers/create ${TMPD}/myspec.json 201 \
   706    .Id~[0-9a-f]\\{64\\}
   707  
   708  # Verify
   709  t GET libpod/containers/$cname/json 200 \
   710    .ImageName=$IMAGE \
   711    .Name=$cname
   712  
   713  if root; then
   714    podman run -dt --name=updateCtr alpine
   715    echo '{"Memory":{"Limit":500000}, "CPU":{"Shares":123}}' >${TMPD}/update.json
   716    t POST libpod/containers/updateCtr/update ${TMPD}/update.json 201
   717  
   718    cgroupPath=/sys/fs/cgroup/cpu.weight
   719    # 002 is the byte length
   720    cpu_weight_expect=$'\001\0025'
   721    if ! have_cgroupsv2; then
   722      cgroupPath=/sys/fs/cgroup/cpu/cpu.shares
   723      # 004 is the byte length
   724      cpu_weight_expect=$'\001\004123'
   725    fi
   726  
   727    # Verify
   728    echo '{ "AttachStdout":true,"Cmd":["cat", "'$cgroupPath'"]}' >${TMPD}/exec.json
   729    t POST containers/updateCtr/exec ${TMPD}/exec.json 201 .Id~[0-9a-f]\\{64\\}
   730    eid=$(jq -r '.Id' <<<"$output")
   731    t POST exec/$eid/start 200 $cpu_weight_expect
   732  
   733    # Now use the compat API
   734    echo '{ "Memory": 536870912 }' >${TMPD}/compatupdate.json
   735    t POST containers/updateCtr/update ${TMPD}/compatupdate.json 200
   736    t GET libpod/containers/updateCtr/json 200 \
   737      .HostConfig.Memory=536870912
   738  
   739    podman rm -f updateCtr
   740  fi
   741  
   742  rm -rf $TMPD
   743  
   744  podman container rm -fa
   745  
   746  # 18951: Make sure container create supports the platform parameter.  Force an
   747  # initial architecture to make sure the test runs on all platforms.
   748  podman pull --platform=linux/amd64 $IMAGE
   749  t POST containers/create?platform=linux/amd64 \
   750    Image=$IMAGE \
   751    201
   752  t POST containers/create?platform=linux/aarch64 \
   753    Image=$IMAGE \
   754    404
   755  podman rmi -f $IMAGE