github.com/containers/podman/v5@v5.1.0-rc1/test/system/252-quadlet.bats (about)

     1  #!/usr/bin/env bats   -*- bats -*-
     2  #
     3  # Tests generated configurations for systemd.
     4  #
     5  
     6  load helpers
     7  load helpers.network
     8  load helpers.registry
     9  load helpers.systemd
    10  
    11  UNIT_FILES=()
    12  
    13  function start_time() {
    14      sleep_to_next_second # Ensure we're on a new second with no previous logging
    15      STARTED_TIME=$(date "+%F %R:%S") # Start time for new log time
    16  }
    17  
    18  function setup() {
    19      skip_if_remote "quadlet tests are meaningless over remote"
    20      skip_if_rootless_cgroupsv1 "Can't use --cgroups=split w/ CGv1 (issue 17456, wontfix)"
    21      skip_if_journald_unavailable "Needed for RHEL. FIXME: we might be able to re-enable a subset of tests."
    22  
    23      test -x "$QUADLET" || die "Cannot run quadlet tests without executable \$QUADLET ($QUADLET)"
    24  
    25      start_time
    26  
    27      basic_setup
    28  }
    29  
    30  function teardown() {
    31      for UNIT_FILE in ${UNIT_FILES[@]}; do
    32          if [[ -e "$UNIT_FILE" ]]; then
    33              local service=$(basename "$UNIT_FILE")
    34              run systemctl stop "$service"
    35              if [ $status -ne 0 ]; then
    36                 echo "# WARNING: systemctl stop failed in teardown: $output" >&3
    37              fi
    38              rm -f "$UNIT_FILE"
    39          fi
    40      done
    41      systemctl daemon-reload
    42  
    43      basic_teardown
    44  }
    45  
    46  # Converts the quadlet file and installs the result it in $UNIT_DIR
    47  function run_quadlet() {
    48      local sourcefile="$1"
    49      local service=$(quadlet_to_service_name "$sourcefile")
    50  
    51      # quadlet always works on an entire directory, so copy the file
    52      # to transform to the given or newly created tmpdir
    53      local quadlet_tmpdir="$2"
    54      if [ -z "$quadlet_tmpdir" ]; then
    55          quadlet_tmpdir=$(mktemp -d --tmpdir=$PODMAN_TMPDIR quadlet.XXXXXX)
    56      fi
    57      cp $sourcefile $quadlet_tmpdir/
    58  
    59      echo "$_LOG_PROMPT $QUADLET $_DASHUSER $UNIT_DIR"
    60      QUADLET_UNIT_DIRS="$quadlet_tmpdir" run \
    61                       timeout --foreground -v --kill=10 $PODMAN_TIMEOUT \
    62                       $QUADLET $_DASHUSER $UNIT_DIR
    63      echo "$output"
    64      assert $status -eq 0 "Failed to convert quadlet file: $sourcefile"
    65      is "$output" "" "quadlet should report no errors"
    66  
    67      # Ensure this is teared down
    68      UNIT_FILES+=("$UNIT_DIR/$service")
    69  
    70      QUADLET_SERVICE_NAME="$service"
    71      QUADLET_SYSLOG_ID="$(basename $service .service)"
    72      QUADLET_CONTAINER_NAME="systemd-$QUADLET_SYSLOG_ID"
    73  
    74      cat $UNIT_DIR/$QUADLET_SERVICE_NAME
    75  }
    76  
    77  function service_setup() {
    78      local service="$1"
    79      local option="$2"
    80  
    81      systemctl daemon-reload
    82  
    83      local startargs=""
    84      local statusexit=0
    85      local activestate="active"
    86  
    87      # If option wait, start and wait for service to exist
    88      if [ "$option" == "wait" ]; then
    89          startargs="--wait"
    90          statusexit=3
    91          local activestate="inactive"
    92      fi
    93  
    94      systemctl_start $startargs "$service"
    95  
    96      # FIXME FIXME FIXME: this is racy with short-lived containers!
    97      echo "$_LOG_PROMPT systemctl status $service"
    98      run systemctl status "$service"
    99      echo "$output"
   100      assert $status -eq $statusexit "systemctl status $service"
   101  
   102      echo "$_LOG_PROMPT systemctl show --value --property=ActiveState $service"
   103      run systemctl show --value --property=ActiveState "$service"
   104      echo "$output"
   105      assert $status -eq 0 "systemctl show $service"
   106      is "$output" $activestate
   107  }
   108  
   109  # Helper to stop a systemd service running a container
   110  function service_cleanup() {
   111      local service="$1"
   112      local expected_state="$2"
   113  
   114      run systemctl stop "$service"
   115      assert $status -eq 0 "Error stopping systemd unit $service: $output"
   116  
   117      # Regression test for #11304: confirm that unit stops into correct state
   118      if [[ -n "$expected_state" ]]; then
   119          run systemctl show --property=ActiveState "$service"
   120          assert "$output" = "ActiveState=$expected_state" \
   121                 "state of service $service after systemctl stop"
   122      fi
   123  
   124      rm -f "$UNIT_DIR/$service"
   125      systemctl daemon-reload
   126  }
   127  
   128  function create_secret() {
   129      local secret_name=$(random_string)
   130      local secret_file=$PODMAN_TMPDIR/secret_$(random_string)
   131      local secret=$(random_string)
   132  
   133      echo $secret > $secret_file
   134      run_podman secret create $secret_name $secret_file
   135  
   136      SECRET_NAME=$secret_name
   137      SECRET=$secret
   138  }
   139  
   140  function remove_secret() {
   141      local secret_name="$1"
   142  
   143      run_podman secret rm $secret_name
   144  }
   145  
   146  @test "quadlet - basic" {
   147      # Network=none is to work around a Pasta bug, can be removed once a patched Pasta is available.
   148      # Ref https://github.com/containers/podman/pull/21563#issuecomment-1965145324
   149      local quadlet_file=$PODMAN_TMPDIR/basic_$(random_string).container
   150      cat > $quadlet_file <<EOF
   151  [Container]
   152  Image=$IMAGE
   153  Exec=sh -c "echo STARTED CONTAINER; echo "READY=1" | socat -u STDIN unix-sendto:\$NOTIFY_SOCKET; sleep inf"
   154  Notify=yes
   155  LogDriver=passthrough
   156  Network=none
   157  EOF
   158  
   159      # FIXME: Temporary until podman fully removes cgroupsv1 support; see #21431
   160      if [[ -n "$PODMAN_IGNORE_CGROUPSV1_WARNING" ]]; then
   161          skip "Way too complicated to test under cgroupsv1, and not worth the effort"
   162      fi
   163  
   164      run_quadlet "$quadlet_file"
   165      service_setup $QUADLET_SERVICE_NAME
   166  
   167      # Check that we can read the logs from the container with podman logs even
   168      # with the `passthrough` driver.  The log may need a short period of time
   169      # to bubble up into the journal logs, so wait for it.
   170      wait_for_output "STARTED CONTAINER" $QUADLET_CONTAINER_NAME
   171      # Make sure it's an *exact* match, not just a substring (i.e. no spurious
   172      # warnings or other cruft).
   173      run_podman logs $QUADLET_CONTAINER_NAME
   174      assert "$output" == "STARTED CONTAINER" "exact/full match when using the 'passthrough' driver"
   175  
   176      # Also look for the logs via `journalctl`.
   177      run journalctl "--since=$STARTED_TIME" --unit="$QUADLET_SERVICE_NAME"
   178      is "$output" '.*STARTED CONTAINER.*'
   179  
   180      run_podman container inspect  --format "{{.State.Status}}" $QUADLET_CONTAINER_NAME
   181      is "$output" "running" "container should be started by systemd and hence be running"
   182  
   183      service_cleanup $QUADLET_SERVICE_NAME failed
   184  }
   185  
   186  @test "quadlet conflict names" {
   187      # If two directories in the search have files with the same name, quadlet should
   188      # only process the first name
   189      dir1=$PODMAN_TMPDIR/$(random_string)
   190      dir2=$PODMAN_TMPDIR/$(random_string)
   191      local quadlet_file=basic_$(random_string).container
   192      mkdir -p $dir1 $dir2
   193  
   194      cat > $dir1/$quadlet_file <<EOF
   195  [Container]
   196  Image=$IMAGE
   197  Notify=yes
   198  EOF
   199  
   200      cat > $dir2/$quadlet_file <<EOF
   201  [Container]
   202  Image=$IMAGE
   203  Notify=no
   204  EOF
   205      QUADLET_UNIT_DIRS="$dir1:$dir2" run \
   206                      timeout --foreground -v --kill=10 $PODMAN_TIMEOUT \
   207                      $QUADLET --dryrun
   208      assert "$output" =~ "Notify=yes" "quadlet should show Notify=yes"
   209      assert "$output" !~ "Notify=no" "quadlet should not show Notify=no"
   210  }
   211  
   212  @test "quadlet - envvar" {
   213      local quadlet_file=$PODMAN_TMPDIR/envvar_$(random_string).container
   214      cat > $quadlet_file <<EOF
   215  [Container]
   216  Image=$IMAGE
   217  Exec=sh -c "echo OUTPUT: \"\$FOOBAR\" \"\$BAR\""
   218  Environment="FOOBAR=Foo  Bar" BAR=bar
   219  LogDriver=passthrough
   220  EOF
   221  
   222      run_quadlet "$quadlet_file"
   223      service_setup $QUADLET_SERVICE_NAME wait
   224  
   225      # Ensure we have the right output, sync is done via waiting for service to exit (service_setup wait)
   226  
   227      # Note: Here we have to filter by syslog id instead of unit, because there is a (known) race
   228      # condition where if the cgroup is cleaned up before journald sees the message, then the journal
   229      # doesn't know the cgroup  and thus not the unit. (See https://github.com/systemd/systemd/issues/2913)
   230      run journalctl "--since=$STARTED_TIME"  SYSLOG_IDENTIFIER="$QUADLET_SYSLOG_ID"
   231      is "$output" '.*OUTPUT: Foo  Bar bar.*'
   232  
   233      service_cleanup $QUADLET_SERVICE_NAME inactive
   234  }
   235  
   236  @test "quadlet - ContainerName" {
   237      local quadlet_file=$PODMAN_TMPDIR/containername_$(random_string).container
   238      cat > $quadlet_file <<EOF
   239  [Container]
   240  ContainerName=customcontainername
   241  Image=$IMAGE
   242  Exec=top"
   243  EOF
   244  
   245      run_quadlet "$quadlet_file"
   246      service_setup $QUADLET_SERVICE_NAME
   247  
   248      # Ensure we can access with the custom container name
   249      run_podman container inspect  --format "{{.State.Status}}" customcontainername
   250      is "$output" "running" "container should be started by systemd and hence be running"
   251  
   252      service_cleanup $QUADLET_SERVICE_NAME failed
   253  }
   254  
   255  @test "quadlet - labels" {
   256      local quadlet_file=$PODMAN_TMPDIR/labels_$(random_string).container
   257      cat > $quadlet_file <<EOF
   258  [Container]
   259  Image=$IMAGE
   260  Exec=top
   261  Label="foo=foo bar" "key=val"
   262  Annotation="afoo=afoo bar"
   263  Annotation="akey=aval"
   264  EOF
   265  
   266      run_quadlet "$quadlet_file"
   267      service_setup $QUADLET_SERVICE_NAME
   268  
   269      run_podman container inspect --format "{{.Config.Labels.foo}}" $QUADLET_CONTAINER_NAME
   270      is "$output" "foo bar"
   271      run_podman container inspect --format "{{.Config.Labels.key}}" $QUADLET_CONTAINER_NAME
   272      is "$output" "val"
   273      run_podman container inspect --format "{{.Config.Annotations.afoo}}" $QUADLET_CONTAINER_NAME
   274      is "$output" "afoo bar"
   275      run_podman container inspect --format "{{.Config.Annotations.akey}}" $QUADLET_CONTAINER_NAME
   276      is "$output" "aval"
   277  
   278      service_cleanup $QUADLET_SERVICE_NAME failed
   279  }
   280  
   281  @test "quadlet - oneshot" {
   282      local quadlet_file=$PODMAN_TMPDIR/oneshot_$(random_string).container
   283      cat > $quadlet_file <<EOF
   284  [Container]
   285  Image=$IMAGE
   286  Exec=echo INITIALIZED
   287  
   288  [Service]
   289  Type=oneshot
   290  RemainAfterExit=yes
   291  EOF
   292  
   293      run_quadlet "$quadlet_file"
   294  
   295      service_setup $QUADLET_SERVICE_NAME
   296  
   297      # Ensure we have output. Output is synced by oneshot command exit
   298      run journalctl "--since=$STARTED_TIME"  SYSLOG_IDENTIFIER="$QUADLET_SYSLOG_ID"
   299      is "$output" '.*INITIALIZED.*'
   300  
   301      service_cleanup $QUADLET_SERVICE_NAME inactive
   302  }
   303  
   304  @test "quadlet - volume" {
   305      local quadlet_file=$PODMAN_TMPDIR/basic_$(random_string).volume
   306      cat > $quadlet_file <<EOF
   307  [Volume]
   308  Label=foo=bar other="with space"
   309  EOF
   310  
   311      run_quadlet "$quadlet_file"
   312  
   313      service_setup $QUADLET_SERVICE_NAME
   314  
   315      local volume_name=systemd-$(basename $quadlet_file .volume)
   316      run_podman volume ls
   317      is "$output" ".*local.*${volume_name}.*"
   318  
   319      run_podman volume inspect  --format "{{.Labels.foo}}" $volume_name
   320      is "$output" "bar"
   321      run_podman volume inspect  --format "{{.Labels.other}}" $volume_name
   322      is "$output" "with space"
   323  
   324      service_cleanup $QUADLET_SERVICE_NAME inactive
   325  }
   326  
   327  # A quadlet container depends on a quadlet volume
   328  @test "quadlet - volume dependency" {
   329      # Save the unit name to use as the volume for the container
   330      local quadlet_vol_unit=dep_$(random_string).volume
   331      local quadlet_vol_file=$PODMAN_TMPDIR/${quadlet_vol_unit}
   332      cat > $quadlet_vol_file <<EOF
   333  [Volume]
   334  EOF
   335  
   336      # Have quadlet create the systemd unit file for the volume unit
   337      run_quadlet "$quadlet_vol_file"
   338  
   339      # Save the volume service name since the variable will be overwritten
   340      local vol_service=$QUADLET_SERVICE_NAME
   341      local volume_name=systemd-$(basename $quadlet_vol_file .volume)
   342  
   343      local quadlet_file=$PODMAN_TMPDIR/user_$(random_string).container
   344      cat > $quadlet_file <<EOF
   345  [Container]
   346  Image=$IMAGE
   347  Exec=top
   348  Volume=$quadlet_vol_unit:/tmp
   349  EOF
   350  
   351      # Have quadlet create the systemd unit file for the container unit
   352      run_quadlet "$quadlet_file"
   353  
   354      # Save the container service name for readability
   355      local container_service=$QUADLET_SERVICE_NAME
   356  
   357      # Volume should not exist
   358      run_podman 1 volume exists ${volume_name}
   359  
   360      # Start the container service which should also trigger the start of the volume service
   361      service_setup $container_service
   362  
   363      # Volume system unit should be active
   364      run systemctl show --property=ActiveState "$vol_service"
   365      assert "$output" = "ActiveState=active" \
   366             "volume should be active via dependency"
   367  
   368      # Volume should exist
   369      run_podman volume exists ${volume_name}
   370  
   371      # Shutdown the service and remove the volume
   372      service_cleanup $container_service failed
   373      run_podman volume rm $volume_name
   374  }
   375  
   376  # A quadlet container depends on a named quadlet volume
   377  @test "quadlet - named volume dependency" {
   378      # Save the unit name to use as the volume for the container
   379      local quadlet_vol_unit=dep_$(random_string).volume
   380      local quadlet_vol_file=$PODMAN_TMPDIR/${quadlet_vol_unit}
   381      cat > $quadlet_vol_file <<EOF
   382  [Volume]
   383  VolumeName=foo
   384  EOF
   385  
   386      # Have quadlet create the systemd unit file for the volume unit
   387      local quadlet_tmpdir=$(mktemp -d --tmpdir=$PODMAN_TMPDIR quadlet.XXXXXX)
   388      run_quadlet "$quadlet_vol_file" "$quadlet_tmpdir"
   389  
   390      # Save the volume service name since the variable will be overwritten
   391      local vol_service=$QUADLET_SERVICE_NAME
   392      local volume_name="foo"
   393  
   394      local quadlet_file=$PODMAN_TMPDIR/user_$(random_string).container
   395      cat > $quadlet_file <<EOF
   396  [Container]
   397  Image=$IMAGE
   398  Exec=top
   399  Volume=$quadlet_vol_unit:/tmp
   400  EOF
   401  
   402      # Have quadlet create the systemd unit file for the container unit
   403      run_quadlet "$quadlet_file" "$quadlet_tmpdir"
   404  
   405      # Save the container service name for readability
   406      local container_service=$QUADLET_SERVICE_NAME
   407      local container_name=systemd-$(basename $quadlet_file .container)
   408  
   409      # Volume should not exist
   410      run_podman 1 volume exists ${volume_name}
   411  
   412      # Start the container service which should also trigger the start of the volume service
   413      service_setup $container_service
   414  
   415      # Volume system unit should be active
   416      run systemctl show --property=ActiveState "$vol_service"
   417      assert "$output" = "ActiveState=active" "volume should be active via dependency"
   418  
   419      # Volume should exist
   420      run_podman volume exists ${volume_name}
   421  
   422      # Container should be attached to defined volume
   423      run_podman container inspect --format "{{(index .Mounts 0).Name}}" $container_name
   424      assert "$output" = "$volume_name" "container should be attached to network $volume_name"
   425  
   426      # Shutdown the service and remove the volume
   427      service_cleanup $container_service failed
   428      run_podman volume rm $volume_name
   429  }
   430  
   431  @test "quadlet - network" {
   432      local quadlet_file=$PODMAN_TMPDIR/basic_$(random_string).network
   433      cat > $quadlet_file <<EOF
   434  [Network]
   435  Label=foo=bar other="with space"
   436  EOF
   437  
   438      run_quadlet "$quadlet_file"
   439  
   440      service_setup $QUADLET_SERVICE_NAME
   441  
   442      local network_name=systemd-$(basename $quadlet_file .network)
   443      run_podman network exists $network_name
   444  
   445      run_podman network inspect  --format "{{.Labels.foo}}" $network_name
   446      is "$output" "bar"
   447      run_podman network inspect  --format "{{.Labels.other}}" $network_name
   448      is "$output" "with space"
   449  
   450      service_cleanup $QUADLET_SERVICE_NAME inactive
   451  }
   452  
   453  # A quadlet container depends on a quadlet network
   454  @test "quadlet - network dependency" {
   455      # Save the unit name to use as the network for the container
   456      local quadlet_network_unit=dep_$(random_string).network
   457      local quadlet_network_file=$PODMAN_TMPDIR/${quadlet_network_unit}
   458      cat > $quadlet_network_file <<EOF
   459  [Network]
   460  EOF
   461  
   462      # Have quadlet create the systemd unit file for the network unit
   463      run_quadlet "$quadlet_network_file"
   464  
   465      # Save the volume service name since the variable will be overwritten
   466      local network_service=$QUADLET_SERVICE_NAME
   467      local network_name=systemd-$(basename $quadlet_network_file .network)
   468  
   469      local quadlet_file=$PODMAN_TMPDIR/user_$(random_string).container
   470      cat > $quadlet_file <<EOF
   471  [Container]
   472  Image=$IMAGE
   473  Exec=top
   474  Network=$quadlet_network_unit
   475  EOF
   476  
   477      run_quadlet "$quadlet_file"
   478  
   479      # Save the container service name for readability
   480      local container_service=$QUADLET_SERVICE_NAME
   481  
   482      # Network should not exist
   483      run_podman 1 network exists $network_name
   484  
   485      service_setup $container_service
   486  
   487      # Network system unit should be active
   488      run systemctl show --property=ActiveState "$network_service"
   489      assert "$output" = "ActiveState=active" \
   490             "network should be active via dependency"
   491  
   492      # Network should exist
   493      run_podman network exists $network_name
   494  
   495      service_cleanup $QUADLET_SERVICE_NAME failed
   496      run_podman network rm $network_name
   497  }
   498  
   499  # A quadlet container depends on a named quadlet network
   500  @test "quadlet - named network dependency" {
   501      # Save the unit name to use as the network for the container
   502      local quadlet_network_unit=dep_$(random_string).network
   503      local quadlet_network_file=$PODMAN_TMPDIR/${quadlet_network_unit}
   504      cat > $quadlet_network_file <<EOF
   505  [Network]
   506  NetworkName=foo
   507  EOF
   508  
   509      # Have quadlet create the systemd unit file for the network unit
   510      local quadlet_tmpdir=$(mktemp -d --tmpdir=$PODMAN_TMPDIR quadlet.XXXXXX)
   511      run_quadlet "$quadlet_network_file" "$quadlet_tmpdir"
   512  
   513      # Save the network service name since the variable will be overwritten
   514      local network_service=$QUADLET_SERVICE_NAME
   515      local network_name="foo"
   516  
   517      local quadlet_file=$PODMAN_TMPDIR/user_$(random_string).container
   518      cat > $quadlet_file <<EOF
   519  [Container]
   520  Image=$IMAGE
   521  Exec=top
   522  Network=$quadlet_network_unit
   523  EOF
   524  
   525      run_quadlet "$quadlet_file" "$quadlet_tmpdir"
   526  
   527      # Save the container service name for readability
   528      local container_service=$QUADLET_SERVICE_NAME
   529      local container_name=systemd-$(basename $quadlet_file .container)
   530  
   531      # Network should not exist
   532      run_podman 1 network exists $network_name
   533  
   534      service_setup $container_service
   535  
   536      # Network system unit should be active
   537      run systemctl show --property=ActiveState "$network_service"
   538      assert "$output" = "ActiveState=active" "network should be active via dependency"
   539  
   540      # Network should exist
   541      run_podman network exists $network_name
   542  
   543      # Container should be attached to defined network
   544      run_podman container inspect --format "{{index .NetworkSettings.Networks \"$network_name\"}}" $container_name
   545      assert "$output" != "<nil>" "container should be attached to network $network_name"
   546  
   547      service_cleanup $QUADLET_SERVICE_NAME failed
   548      run_podman network rm $network_name
   549  }
   550  
   551  @test "quadlet kube - basic" {
   552      # Create the YAMl file
   553      pod_name="test_pod"
   554      container_name="test"
   555      yaml_source="$PODMAN_TMPDIR/basic_$(random_string).yaml"
   556      cat >$yaml_source <<EOF
   557  apiVersion: v1
   558  kind: Pod
   559  metadata:
   560    labels:
   561      app: test
   562    name: $pod_name
   563  spec:
   564    containers:
   565    - command:
   566      - "sh"
   567      args:
   568      - "-c"
   569      - "echo STARTED CONTAINER; top -b"
   570      image: $IMAGE
   571      name: $container_name
   572  EOF
   573  
   574      # Create the Quadlet file
   575      local quadlet_file=$PODMAN_TMPDIR/basic_$(random_string).kube
   576      cat > $quadlet_file <<EOF
   577  [Kube]
   578  Yaml=${yaml_source}
   579  EOF
   580  
   581      run_quadlet "$quadlet_file"
   582      service_setup $QUADLET_SERVICE_NAME
   583  
   584      # Ensure we have output.
   585      wait_for_output "STARTED CONTAINER" $pod_name-$container_name
   586  
   587      run_podman container inspect  --format "{{.State.Status}}" test_pod-test
   588      is "$output" "running" "container should be started by systemd and hence be running"
   589  
   590      service_cleanup $QUADLET_SERVICE_NAME inactive
   591      run_podman rmi $(pause_image)
   592  }
   593  
   594  @test "quadlet kube - named network dependency" {
   595      # Save the unit name to use as the network for the container
   596      local quadlet_network_unit=dep_$(random_string).network
   597      local quadlet_network_file=$PODMAN_TMPDIR/${quadlet_network_unit}
   598      cat > $quadlet_network_file <<EOF
   599  [Network]
   600  NetworkName=foo
   601  EOF
   602  
   603      # Have quadlet create the systemd unit file for the network unit
   604      local quadlet_tmpdir=$(mktemp -d --tmpdir=$PODMAN_TMPDIR quadlet.XXXXXX)
   605      run_quadlet "$quadlet_network_file" "$quadlet_tmpdir"
   606  
   607      # Save the network service name since the variable will be overwritten
   608      local network_service=$QUADLET_SERVICE_NAME
   609      local network_name="foo"
   610  
   611      # Create the YAMl file
   612      pod_name="test_pod"
   613      container_name="test"
   614      yaml_source="$PODMAN_TMPDIR/basic_$(random_string).yaml"
   615      cat >$yaml_source <<EOF
   616  apiVersion: v1
   617  kind: Pod
   618  metadata:
   619    labels:
   620      app: test
   621    name: $pod_name
   622  spec:
   623    containers:
   624    - command:
   625      - "sh"
   626      args:
   627      - "-c"
   628      - "echo STARTED CONTAINER; top -b"
   629      image: $IMAGE
   630      name: $container_name
   631  EOF
   632  
   633      # Create the Quadlet file
   634      local quadlet_file=$PODMAN_TMPDIR/basic_$(random_string).kube
   635      cat > $quadlet_file <<EOF
   636  [Kube]
   637  Yaml=${yaml_source}
   638  Network=$quadlet_network_unit
   639  EOF
   640  
   641      # Network should not exist
   642      run_podman 1 network exists $network_name
   643  
   644      run_quadlet "$quadlet_file" "$quadlet_tmpdir"
   645      service_setup $QUADLET_SERVICE_NAME
   646  
   647      # Network system unit should be active
   648      run systemctl show --property=ActiveState "$network_service"
   649      assert "$output" = "ActiveState=active" "network should be active via dependency"
   650  
   651      # Network should exist
   652      run_podman network exists $network_name
   653  
   654      # Ensure we have output.
   655      wait_for_output "STARTED CONTAINER" $pod_name-$container_name
   656  
   657      run_podman container inspect  --format "{{.State.Status}}" test_pod-test
   658      assert "$output" =~ "running" "container should be started by systemd and hence be running"
   659  
   660      # Container should be attached to defined network
   661      run_podman container inspect --format "{{index .NetworkSettings.Networks \"$network_name\"}}" test_pod-test
   662      assert "$output" != "<nil>" "container should be attached to network $network_name"
   663  
   664      service_cleanup $QUADLET_SERVICE_NAME inactive
   665      run_podman network rm $network_name
   666      run_podman rmi $(pause_image)
   667  }
   668  
   669  @test "quadlet - rootfs" {
   670      skip_if_no_selinux
   671      skip_if_rootless
   672      local quadlet_file=$PODMAN_TMPDIR/basic_$(random_string).container
   673      cat > $quadlet_file <<EOF
   674  [Container]
   675  Rootfs=/:O
   676  Exec=sh -c "echo STARTED CONTAINER; echo "READY=1" | socat -u STDIN unix-sendto:\$NOTIFY_SOCKET; top -b"
   677  Notify=yes
   678  EOF
   679  
   680      run_quadlet "$quadlet_file"
   681      service_setup $QUADLET_SERVICE_NAME
   682  
   683      wait_for_output "STARTED CONTAINER" $QUADLET_CONTAINER_NAME
   684  }
   685  
   686  @test "quadlet - selinux disable" {
   687      skip_if_no_selinux
   688      local quadlet_file=$PODMAN_TMPDIR/basic_$(random_string).container
   689      cat > $quadlet_file <<EOF
   690  [Container]
   691  Image=$IMAGE
   692  SecurityLabelDisable=true
   693  Exec=sh -c "echo STARTED CONTAINER; top -b"
   694  EOF
   695  
   696      run_quadlet "$quadlet_file"
   697      service_setup $QUADLET_SERVICE_NAME
   698  
   699      # Ensure we have output. Output is synced via sd-notify (socat in Exec)
   700      wait_for_output "STARTED CONTAINER" $QUADLET_CONTAINER_NAME
   701  
   702      run_podman container inspect  --format "{{.ProcessLabel}}" $QUADLET_CONTAINER_NAME
   703      is "$output" "" "container should be started without specifying a Process Label"
   704  
   705      service_cleanup $QUADLET_SERVICE_NAME failed
   706  }
   707  
   708  @test "quadlet - selinux labels" {
   709      skip_if_no_selinux
   710      NAME=$(random_string)
   711      local quadlet_file=$PODMAN_TMPDIR/basic_$(random_string).container
   712      cat > $quadlet_file <<EOF
   713  [Container]
   714  ContainerName=$NAME
   715  Image=$IMAGE
   716  SecurityLabelType=spc_t
   717  SecurityLabelLevel=s0:c100,c200
   718  SecurityLabelFileType=container_ro_file_t
   719  Exec=sh -c "echo STARTED CONTAINER; top -b"
   720  EOF
   721  
   722      run_quadlet "$quadlet_file"
   723      service_setup $QUADLET_SERVICE_NAME
   724  
   725      # Ensure we have output. Output is synced via sd-notify (socat in Exec)
   726      wait_for_output "STARTED CONTAINER" $NAME
   727  
   728      run_podman container ps
   729      run_podman container inspect  --format "{{.ProcessLabel}}" $NAME
   730      is "$output" "system_u:system_r:spc_t:s0:c100,c200" "container should be started with correct Process Label"
   731      run_podman container inspect  --format "{{.MountLabel}}" $NAME
   732      is "$output" "system_u:object_r:container_ro_file_t:s0:c100,c200" "container should be started with correct Mount Label"
   733  
   734      service_cleanup $QUADLET_SERVICE_NAME failed
   735  }
   736  
   737  @test "quadlet - secret as environment variable" {
   738      create_secret
   739  
   740      local quadlet_file=$PODMAN_TMPDIR/basic_$(random_string).container
   741      cat > $quadlet_file <<EOF
   742  [Container]
   743  ContainerName=$NAME
   744  Image=$IMAGE
   745  Secret=$SECRET_NAME,type=env,target=MYSECRET
   746  Exec=sh -c "echo STARTED CONTAINER; echo "READY=1" | socat -u STDIN unix-sendto:\$NOTIFY_SOCKET; top -b"
   747  Notify=yes
   748  EOF
   749  
   750      run_quadlet "$quadlet_file"
   751      service_setup $QUADLET_SERVICE_NAME
   752  
   753      # Ensure we have output. Output is synced via sd-notify (socat in Exec)
   754      wait_for_output "STARTED CONTAINER" $QUADLET_CONTAINER_NAME
   755  
   756      run_podman exec $QUADLET_CONTAINER_NAME /bin/sh -c "printenv MYSECRET"
   757      is "$output" $SECRET
   758  
   759      service_cleanup $QUADLET_SERVICE_NAME failed
   760      remove_secret $SECRET_NAME
   761  }
   762  
   763  @test "quadlet - secret as a file" {
   764      create_secret
   765  
   766      local quadlet_file=$PODMAN_TMPDIR/basic_$(random_string).container
   767      cat > $quadlet_file <<EOF
   768  [Container]
   769  ContainerName=$NAME
   770  Image=$IMAGE
   771  Secret=$SECRET_NAME,type=mount,target=/root/secret
   772  Exec=sh -c "echo STARTED CONTAINER; echo "READY=1" | socat -u STDIN unix-sendto:\$NOTIFY_SOCKET; top -b"
   773  Notify=yes
   774  EOF
   775  
   776      run_quadlet "$quadlet_file"
   777      service_setup $QUADLET_SERVICE_NAME
   778  
   779      # Ensure we have output. Output is synced via sd-notify (socat in Exec)
   780      wait_for_output "STARTED CONTAINER" $QUADLET_CONTAINER_NAME
   781  
   782      run_podman exec $QUADLET_CONTAINER_NAME /bin/sh -c "cat /root/secret"
   783      is "$output" $SECRET
   784  
   785      service_cleanup $QUADLET_SERVICE_NAME failed
   786      remove_secret $SECRET_NAME
   787  }
   788  
   789  @test "quadlet - volume path using systemd %T specifier" {
   790      # "specifier" is systemd-speak for "replaceable fields"; see systemd.unit(5)
   791      #
   792      # Step 1: determine what systemd is using for %T. There does not
   793      # seem to be any systemctly way to find this.
   794      percent_t_file="${PODMAN_TMPDIR}/foo"
   795      local service=get-percent-t.$(random_string 10).service
   796      local unitfile=${UNIT_DIR}/$service
   797      cat >$unitfile <<EOF
   798  [Unit]
   799  Description=Get the value of percent T
   800  
   801  [Service]
   802  ExecStart=/bin/bash -c "echo %T >$percent_t_file"
   803  Type=oneshot
   804  EOF
   805      systemctl daemon-reload
   806      systemctl_start --wait $service
   807      percent_t=$(< $percent_t_file)
   808      # Clean up. Don't bother to systemctl-reload, service_setup does that below.
   809      rm -f $unitfile
   810  
   811      # Sanity check: just make sure it's not "/"
   812      assert "${#percent_t}" -ge 4 "sanity check: length of %T ($percent_t)"
   813  
   814      # Step 2: Make a subdirectory in %T, and in there, a scratch file
   815      local tmp_path=$(mktemp -d --tmpdir=${percent_t} quadlet.volume.XXXXXX)
   816      local tmp_subdir=$(basename $tmp_path)
   817      local file_name="f$(random_string 10).txt"
   818      local file_content="data_$(random_string 15)"
   819      echo $file_content > $tmp_path/$file_name
   820  
   821      local quadlet_file=$PODMAN_TMPDIR/basic_$(random_string).container
   822      cat > $quadlet_file <<EOF
   823  [Container]
   824  Image=$IMAGE
   825  Volume=%T/$tmp_subdir:/test_content:Z
   826  Exec=sh -c "echo STARTED CONTAINER; echo "READY=1" | socat -u STDIN unix-sendto:\$NOTIFY_SOCKET; top"
   827  Notify=yes
   828  EOF
   829  
   830      run_quadlet "$quadlet_file"
   831      service_setup $QUADLET_SERVICE_NAME
   832  
   833      run_podman exec $QUADLET_CONTAINER_NAME cat /test_content/$file_name
   834      is "$output" "$file_content" "contents of testfile in container volume"
   835  
   836      rm -rf $tmp_path
   837  }
   838  
   839  @test "quadlet - tmpfs" {
   840      local quadlet_file=$PODMAN_TMPDIR/basic_$(random_string).container
   841      cat > $quadlet_file <<EOF
   842  [Container]
   843  Image=$IMAGE
   844  Exec=top
   845  Tmpfs=/tmpfs1
   846  Tmpfs=/tmpfs2:ro
   847  EOF
   848  
   849      run_quadlet "$quadlet_file"
   850      service_setup $QUADLET_SERVICE_NAME
   851  
   852      run_podman container inspect  --format '{{index .HostConfig.Tmpfs "/tmpfs1"}}' $QUADLET_CONTAINER_NAME
   853      is "$output" "rw,rprivate,nosuid,nodev,tmpcopyup" "regular tmpfs mount"
   854  
   855      run_podman container inspect  --format '{{index .HostConfig.Tmpfs "/tmpfs2"}}' $QUADLET_CONTAINER_NAME
   856      is "$output" "ro,rprivate,nosuid,nodev,tmpcopyup" "read-only tmpfs mount"
   857  
   858      run_podman container inspect  --format '{{index .HostConfig.Tmpfs "/tmpfs3"}}' $QUADLET_CONTAINER_NAME
   859      is "$output" "" "nonexistent tmpfs mount"
   860  
   861      service_cleanup $QUADLET_SERVICE_NAME failed
   862  }
   863  
   864  @test "quadlet - userns" {
   865      local quadlet_file=$PODMAN_TMPDIR/basic_$(random_string).container
   866      cat > $quadlet_file <<EOF
   867  [Container]
   868  Image=$IMAGE
   869  Exec=top
   870  UserNS=keep-id:uid=200,gid=210
   871  EOF
   872  
   873      run_quadlet "$quadlet_file"
   874      service_setup $QUADLET_SERVICE_NAME
   875  
   876      run_podman container inspect --format '{{.Config.CreateCommand}}' $QUADLET_CONTAINER_NAME
   877      is "${output/* --userns keep-id:uid=200,gid=210 */found}" "found"
   878  
   879      service_cleanup $QUADLET_SERVICE_NAME failed
   880  }
   881  
   882  @test "quadlet - exit-code propagation" {
   883     pod_name="test_pod"
   884     container_name="ctr"
   885     exit_tests="
   886  all  | true  | 0   | inactive
   887  all  | false | 137 | failed
   888  none | false | 0   | inactive
   889  "
   890     while read exit_code_prop cmd exit_code service_state; do
   891        local basename=propagate-${exit_code_prop}-${cmd}-$(random_string)
   892        local quadlet_file=$PODMAN_TMPDIR/$basename.kube
   893        local yaml_file=$PODMAN_TMPDIR/$basename.yaml
   894  
   895        cat > $yaml_file <<EOF
   896  apiVersion: v1
   897  kind: Pod
   898  metadata:
   899    labels:
   900      app: test
   901    name: $pod_name
   902  spec:
   903    restartPolicy: Never
   904    containers:
   905      - name: $container_name
   906        image: $IMAGE
   907        command:
   908        - $cmd
   909  EOF
   910         cat > $quadlet_file <<EOF
   911  [Kube]
   912  Yaml=$yaml_file
   913  ExitCodePropagation=$exit_code_prop
   914  LogDriver=journald
   915  EOF
   916  
   917        run_quadlet "$quadlet_file"
   918        run systemctl status $QUADLET_SERVICE_NAME
   919  
   920        yaml_sha=$(sha256sum $yaml_file)
   921        service_container="${yaml_sha:0:12}-service"
   922  
   923        service_setup $QUADLET_SERVICE_NAME
   924  
   925        # Ensure we have output. Output is synced via sd-notify (socat in Exec)
   926        run journalctl "--since=$STARTED_TIME" --unit="$QUADLET_SERVICE_NAME"
   927        is "$output" '.*Started.*\.service.*'
   928  
   929        # Opportunistic test: confirm that the Propagation field got set.
   930        # This is racy, because the container is short-lived and quadlet
   931        # cleans up on exit (via kube-down in ExecStopPost). So we use '?'
   932        # and only check output if the inspect succeeds.
   933        run_podman '?' container inspect --format '{{.KubeExitCodePropagation}}' $service_container
   934        if [[ $status -eq 0 ]]; then
   935            is "$output" "$exit_code_prop" \
   936               "$basename: service container has the expected policy set in its annotations"
   937        else
   938            assert "$output" =~ "no such container $service_container" \
   939                   "$basename: unexpected error from podman container inspect"
   940        fi
   941  
   942        # Container must stop of its own accord before we call service_cleanup(),
   943        # otherwise the 'systemctl stop' there may affect the unit's status.
   944        # Again, use '?' to handle the abovementioned race condition.
   945        run_podman '?' wait $service_container
   946        if [[ $status -eq 0 ]]; then
   947            assert "$output" = "$exit_code" \
   948                   "$basename: service container reflects expected exit code"
   949        else
   950            assert "$output" =~ "no container with name or ID" \
   951                   "$basename: unexpected error from podman wait"
   952        fi
   953  
   954        # This is the actual propagation check
   955        service_cleanup $QUADLET_SERVICE_NAME $service_state
   956        run_podman ps -aq
   957        is "$output" "" "all containers are cleaned up even in case of errors"
   958     done < <(parse_table "$exit_tests")
   959  
   960     run_podman rmi $(pause_image)
   961  }
   962  
   963  @test "quadlet kube - Working Directory" {
   964      yaml_source="$PODMAN_TMPDIR/basic_$(random_string).yaml"
   965      local_path=local_path$(random_string)
   966      pod_name=test_pod
   967      container_name=test
   968  
   969      cat >$yaml_source <<EOF
   970  apiVersion: v1
   971  kind: Pod
   972  metadata:
   973    labels:
   974      app: test
   975    name: $pod_name
   976  spec:
   977    containers:
   978    - command:
   979      - "sh"
   980      args:
   981      - "-c"
   982      - "echo STARTED CONTAINER; top -b"
   983      image: $IMAGE
   984      name: $container_name
   985      volumeMounts:
   986      - mountPath: /test
   987        name: test-volume
   988    volumes:
   989    - name: test-volume
   990      hostPath:
   991        # directory location on host
   992        path: ./$local_path
   993        # this field is optional
   994        type: DirectoryOrCreate
   995  EOF
   996  
   997      # Create the Quadlet file
   998      local quadlet_file=$PODMAN_TMPDIR/basic_$(random_string).kube
   999      cat > $quadlet_file <<EOF
  1000  [Kube]
  1001  Yaml=${yaml_source}
  1002  SetWorkingDirectory=yaml
  1003  EOF
  1004  
  1005      run_quadlet "$quadlet_file"
  1006      service_setup $QUADLET_SERVICE_NAME
  1007  
  1008      # Ensure we have output.
  1009      wait_for_output "STARTED CONTAINER" $pod_name-$container_name
  1010  
  1011      run_podman container inspect  --format "{{.State.Status}}" $pod_name-$container_name
  1012      is "$output" "running" "container should be started by systemd and hence be running"
  1013  
  1014      run_podman ps
  1015  
  1016      run_podman exec $pod_name-$container_name /bin/sh -c "echo hello > /test/test.txt"
  1017      is $(cat $PODMAN_TMPDIR/$local_path/test.txt) "hello"
  1018  
  1019      service_cleanup $QUADLET_SERVICE_NAME inactive
  1020      run_podman rmi $(pause_image)
  1021  }
  1022  
  1023  @test "quadlet - image files" {
  1024      local quadlet_tmpdir=$PODMAN_TMPDIR/quadlets
  1025  
  1026      local registry=localhost:${PODMAN_LOGIN_REGISTRY_PORT}
  1027      local image_for_test=$registry/quadlet_image_test:$(random_string)
  1028      local authfile=$PODMAN_TMPDIR/authfile.json
  1029  
  1030      local quadlet_image_unit=image_test_$(random_string).image
  1031      local quadlet_image_file=$PODMAN_TMPDIR/$quadlet_image_unit
  1032      cat > $quadlet_image_file <<EOF
  1033  [Image]
  1034  Image=$image_for_test
  1035  AuthFile=$authfile
  1036  TLSVerify=false
  1037  EOF
  1038  
  1039      local quadlet_volume_unit=image_test_$(random_string).volume
  1040      local quadlet_volume_file=$PODMAN_TMPDIR/$quadlet_volume_unit
  1041      local volume_name=systemd-$(basename $quadlet_volume_file .volume)
  1042      cat > $quadlet_volume_file <<EOF
  1043  [Volume]
  1044  Driver=image
  1045  Image=$quadlet_image_unit
  1046  EOF
  1047  
  1048      local quadlet_container_unit=image_test_$(random_string).container
  1049      local quadlet_container_file=$PODMAN_TMPDIR/$quadlet_container_unit
  1050      cat > $quadlet_container_file <<EOF
  1051  [Container]
  1052  Image=$quadlet_image_unit
  1053  Volume=$quadlet_volume_unit:/vol
  1054  Exec=sh -c "echo STARTED CONTAINER; echo "READY=1" | socat -u STDIN unix-sendto:\$NOTIFY_SOCKET; sleep inf"
  1055  EOF
  1056  
  1057      # In order to test image pull but without possible Network issues,
  1058      # this test uses an additional registry.
  1059      # Start the registry and populate the authfile that we can use for the test.
  1060      start_registry
  1061      run_podman login --authfile=$authfile \
  1062          --tls-verify=false \
  1063          --username ${PODMAN_LOGIN_USER} \
  1064          --password ${PODMAN_LOGIN_PASS} \
  1065          $registry
  1066  
  1067      # Push the test image to the registry
  1068      run_podman image tag $IMAGE $image_for_test
  1069      run_podman image push --tls-verify=false --authfile=$authfile $image_for_test
  1070  
  1071      # Remove the local image to make sure it will be pulled again
  1072      run_podman image rm --ignore $image_for_test
  1073  
  1074      # Use the same directory for all quadlet files to make sure later steps access previous ones
  1075      mkdir $quadlet_tmpdir
  1076  
  1077      # Have quadlet create the systemd unit file for the image unit
  1078      run_quadlet "$quadlet_image_file" "$quadlet_tmpdir"
  1079      # Save the image service name since the variable will be overwritten
  1080      local image_service=$QUADLET_SERVICE_NAME
  1081  
  1082      # Have quadlet create the systemd unit file for the volume unit
  1083      run_quadlet "$quadlet_volume_file" "$quadlet_tmpdir"
  1084      # Save the image service name since the variable will be overwritten
  1085      local volume_service=$QUADLET_SERVICE_NAME
  1086  
  1087      # Image should not exist
  1088      run_podman 1 image exists ${image_for_test}
  1089      # Volume should not exist
  1090      run_podman 1 volume exists ${volume_name}
  1091  
  1092      # Have quadlet create the systemd unit file for the image unit
  1093      run_quadlet "$quadlet_container_file" "$quadlet_tmpdir"
  1094      local container_service=$QUADLET_SERVICE_NAME
  1095      local container_name=$QUADLET_CONTAINER_NAME
  1096  
  1097      service_setup $container_service
  1098  
  1099      # Image system unit should be active
  1100      run systemctl show --property=ActiveState "$image_service"
  1101      assert "$output" = "ActiveState=active" \
  1102             "quadlet - image files: image should be active via dependency but is not"
  1103  
  1104      # Volume system unit should be active
  1105      run systemctl show --property=ActiveState "$volume_service"
  1106      assert "$output" = "ActiveState=active" \
  1107             "quadlet - image files: volume should be active via dependency but is not"
  1108  
  1109      # Image should exist
  1110      run_podman image exists ${image_for_test}
  1111  
  1112      # Volume should exist
  1113      run_podman volume exists ${volume_name}
  1114  
  1115      # Verify that the volume was created correctly
  1116      run_podman volume inspect --format "{{ .Driver }}" $volume_name
  1117      assert "$output" = "image" \
  1118             "quadlet - image files: volume driver should be image"
  1119  
  1120      run_podman volume inspect --format "{{ .Options.image }}" $volume_name
  1121      assert "$output" = "$image_for_test" \
  1122             "quadlet - image files: the image for the volume should be $image_for_test"
  1123  
  1124      # Verify that the container mounts the volume
  1125      run_podman container inspect --format "{{(index .Mounts 0).Type}}" $container_name
  1126      assert "$output" = "volume" \
  1127             "quadlet - image files: container should be attached to a volume of type volume"
  1128  
  1129      run_podman container inspect --format "{{(index .Mounts 0).Name}}" $container_name
  1130      assert "$output" = "$volume_name" \
  1131             "quadlet - image files: container should be attached to the volume named $volume_name"
  1132  
  1133      run_podman exec $QUADLET_CONTAINER_NAME cat /home/podman/testimage-id
  1134      assert "$output" = $PODMAN_TEST_IMAGE_TAG \
  1135             "quadlet - image files: incorrect testimage-id in root"
  1136  
  1137      run_podman exec $QUADLET_CONTAINER_NAME cat /vol/home/podman/testimage-id
  1138      assert "$output" = $PODMAN_TEST_IMAGE_TAG \
  1139              "quadlet - image files: incorrect testimage-id in bound volume"
  1140  
  1141      # Shutdown the service and remove the volume
  1142      service_cleanup $container_service failed
  1143      run_podman volume rm $volume_name
  1144      run_podman image rm --ignore $image_for_test
  1145  }
  1146  
  1147  @test "quadlet - kube oneshot" {
  1148      local quadlet_tmpdir=$PODMAN_TMPDIR/quadlets
  1149      local test_random_string=$(random_string)
  1150  
  1151      local quadlet_kube_volume_name=test-volume_$test_random_string
  1152      local quadlet_kube_volume_yaml_file=$PODMAN_TMPDIR/volume_$test_random_string.yaml
  1153      cat > $quadlet_kube_volume_yaml_file <<EOF
  1154  ---
  1155  apiVersion: v1
  1156  kind: PersistentVolumeClaim
  1157  metadata:
  1158    name: $quadlet_kube_volume_name
  1159  spec:
  1160    accessModes:
  1161    - ReadWriteOnce
  1162    resources:
  1163      requests:
  1164        storage: 1Gi
  1165  EOF
  1166  
  1167      local quadlet_kube_volume_unit_file=$PODMAN_TMPDIR/volume_$test_random_string.kube
  1168      cat > $quadlet_kube_volume_unit_file <<EOF
  1169  [Kube]
  1170  Yaml=$quadlet_kube_volume_yaml_file
  1171  
  1172  [Service]
  1173  Type=oneshot
  1174  RemainAfterExit=yes
  1175  EOF
  1176  
  1177      local pod_name="test_pod_$test_random_string"
  1178      local container_name="test"
  1179      local quadlet_kube_pod_yaml_file=$PODMAN_TMPDIR/pod_$test_random_string.yaml
  1180      cat > $quadlet_kube_pod_yaml_file <<EOF
  1181  apiVersion: v1
  1182  kind: Pod
  1183  metadata:
  1184    labels:
  1185      app: test
  1186    name: $pod_name
  1187  spec:
  1188    containers:
  1189    - command:
  1190      - "sh"
  1191      args:
  1192      - "-c"
  1193      - "echo STARTED CONTAINER; top -b"
  1194      image: $IMAGE
  1195      name: $container_name
  1196      volumeMounts:
  1197      - name: storage
  1198        mountPath: /mnt/storage
  1199    volumes:
  1200    - name: storage
  1201      persistentVolumeClaim:
  1202        claimName: $quadlet_kube_volume_name
  1203  EOF
  1204  
  1205      # Use the same directory for all quadlet files to make sure later steps access previous ones
  1206      mkdir $quadlet_tmpdir
  1207  
  1208      # Have quadlet create the systemd unit file for the kube based volume unit
  1209      run_quadlet "$quadlet_kube_volume_unit_file" "$quadlet_tmpdir"
  1210      # Save the volume service name since the variable will be overwritten
  1211      local volume_service=$QUADLET_SERVICE_NAME
  1212  
  1213      # Volume should not exist
  1214      run_podman 1 volume exists ${quadlet_kube_volume_name}
  1215  
  1216      local quadlet_kube_pod_unit_file=$PODMAN_TMPDIR/pod_$test_random_string.kube
  1217      cat > $quadlet_kube_pod_unit_file <<EOF
  1218  [Kube]
  1219  Yaml=$quadlet_kube_pod_yaml_file
  1220  
  1221  [Unit]
  1222  Requires=$volume_service
  1223  After=$volume_service
  1224  EOF
  1225  
  1226      # Have quadlet create the systemd unit file for the pod unit
  1227      run_quadlet "$quadlet_kube_pod_unit_file" "$quadlet_tmpdir"
  1228      local pod_service=$QUADLET_SERVICE_NAME
  1229  
  1230      service_setup $pod_service
  1231  
  1232      # Volume system unit should be active
  1233      run systemctl show --property=ActiveState "$volume_service"
  1234      assert "$output" = "ActiveState=active" \
  1235             "quadlet - kube oneshot: volume should be active via dependency but is not"
  1236  
  1237      # Volume should exist
  1238      run_podman volume exists ${quadlet_kube_volume_name}
  1239  
  1240      run_podman container inspect --format "{{(index .Mounts 0).Type}}" $pod_name-$container_name
  1241      assert "$output" = "volume" \
  1242             "quadlet - kube oneshot: volume .Type"
  1243  
  1244      run_podman container inspect --format "{{(index .Mounts 0).Name}}" $pod_name-$container_name
  1245      assert "$output" = "$quadlet_kube_volume_name" \
  1246             "quadlet - kube oneshot: volume .Name"
  1247  
  1248      # Shutdown the service and remove the volume
  1249      service_cleanup $pod_service inactive
  1250      run_podman volume rm $quadlet_kube_volume_name
  1251      run_podman rmi --ignore $(pause_image)
  1252  }
  1253  
  1254  @test "quadlet - kube down force" {
  1255      local test_random_string=$(random_string)
  1256  
  1257      local quadlet_kube_volume_name=test-volume_$test_random_string
  1258      local pod_name="test_pod_$test_random_string"
  1259      local container_name="test"
  1260      local quadlet_kube_pod_yaml_file=$PODMAN_TMPDIR/pod_$test_random_string.yaml
  1261      cat > $quadlet_kube_pod_yaml_file <<EOF
  1262  ---
  1263  apiVersion: v1
  1264  kind: PersistentVolumeClaim
  1265  metadata:
  1266    name: $quadlet_kube_volume_name
  1267  spec:
  1268    accessModes:
  1269    - ReadWriteOnce
  1270    resources:
  1271      requests:
  1272        storage: 1Gi
  1273  ---
  1274  apiVersion: v1
  1275  kind: Pod
  1276  metadata:
  1277    labels:
  1278      app: test
  1279    name: $pod_name
  1280  spec:
  1281    containers:
  1282    - command:
  1283      - "sh"
  1284      args:
  1285      - "-c"
  1286      - "echo STARTED CONTAINER; top -b"
  1287      image: $IMAGE
  1288      name: $container_name
  1289      volumeMounts:
  1290      - name: storage
  1291        mountPath: /mnt/storage
  1292    volumes:
  1293    - name: storage
  1294      persistentVolumeClaim:
  1295        claimName: $quadlet_kube_volume_name
  1296  EOF
  1297  
  1298      local quadlet_kube_pod_unit_file=$PODMAN_TMPDIR/pod_$test_random_string.kube
  1299      cat > $quadlet_kube_pod_unit_file <<EOF
  1300  [Kube]
  1301  Yaml=$quadlet_kube_pod_yaml_file
  1302  KubeDownForce=true
  1303  EOF
  1304  
  1305      # Have quadlet create the systemd unit file for the pod unit
  1306      run_quadlet "$quadlet_kube_pod_unit_file" "$quadlet_tmpdir"
  1307      local pod_service=$QUADLET_SERVICE_NAME
  1308  
  1309      # Volume should not exist
  1310      run_podman 1 volume exists ${quadlet_kube_volume_name}
  1311  
  1312      service_setup $pod_service
  1313  
  1314      # Volume should exist
  1315      run_podman volume exists ${quadlet_kube_volume_name}
  1316  
  1317      run_podman container inspect --format "{{(index .Mounts 0).Type}}" $pod_name-$container_name
  1318      assert "$output" = "volume" \
  1319             "quadlet - kube oneshot: volume .Type"
  1320  
  1321      run_podman container inspect --format "{{(index .Mounts 0).Name}}" $pod_name-$container_name
  1322      assert "$output" = "$quadlet_kube_volume_name" \
  1323             "quadlet - kube oneshot: volume .Name"
  1324  
  1325      # Shutdown the service
  1326      service_cleanup $pod_service failed
  1327  
  1328      # Volume should not exist
  1329      run_podman 1 volume exists ${quadlet_kube_volume_name}
  1330      run_podman rmi --ignore $(pause_image)
  1331  }
  1332  
  1333  @test "quadlet - image tag" {
  1334      local quadlet_tmpdir=$PODMAN_TMPDIR/quadlets
  1335      local archive_file=$PODMAN_TMPDIR/archive-file.tar
  1336      local image_for_test=localhost/quadlet_image_test:$(random_string)
  1337  
  1338      local quadlet_image_unit=image_test_$(random_string).image
  1339      local quadlet_image_file=$PODMAN_TMPDIR/$quadlet_image_unit
  1340      cat > $quadlet_image_file <<EOF
  1341  [Image]
  1342  Image=docker-archive:$archive_file
  1343  ImageTag=$image_for_test
  1344  EOF
  1345  
  1346      local quadlet_volume_unit=image_test_$(random_string).volume
  1347      local quadlet_volume_file=$PODMAN_TMPDIR/$quadlet_volume_unit
  1348      local volume_name=systemd-$(basename $quadlet_volume_file .volume)
  1349      cat > $quadlet_volume_file <<EOF
  1350  [Volume]
  1351  Driver=image
  1352  Image=$quadlet_image_unit
  1353  EOF
  1354  
  1355      local quadlet_container_unit=image_test_$(random_string).container
  1356      local quadlet_container_file=$PODMAN_TMPDIR/$quadlet_container_unit
  1357      cat > $quadlet_container_file <<EOF
  1358  [Container]
  1359  Image=$IMAGE
  1360  Exec=sh -c "echo STARTED CONTAINER; echo "READY=1" | socat -u STDIN unix-sendto:\$NOTIFY_SOCKET; sleep inf"
  1361  Volume=$quadlet_volume_unit:/vol
  1362  EOF
  1363  
  1364      # Tag the image, save it into a file and remove it
  1365      run_podman image tag $IMAGE $image_for_test
  1366      run_podman image save --format docker-archive --output $archive_file $image_for_test
  1367      run_podman image rm $image_for_test
  1368  
  1369      # Use the same directory for all quadlet files to make sure later steps access previous ones
  1370      mkdir $quadlet_tmpdir
  1371  
  1372      # Have quadlet create the systemd unit file for the image unit
  1373      run_quadlet "$quadlet_image_file" "$quadlet_tmpdir"
  1374      # Save the image service name since the variable will be overwritten
  1375      local image_service=$QUADLET_SERVICE_NAME
  1376  
  1377      # Have quadlet create the systemd unit file for the volume unit
  1378      run_quadlet "$quadlet_volume_file" "$quadlet_tmpdir"
  1379      # Save the image service name since the variable will be overwritten
  1380      local volume_service=$QUADLET_SERVICE_NAME
  1381  
  1382      # Image should not exist
  1383      run_podman 1 image exists ${image_for_test}
  1384      # Volume should not exist
  1385      run_podman 1 volume exists ${volume_name}
  1386  
  1387      # Have quadlet create the systemd unit file for the image unit
  1388      run_quadlet "$quadlet_container_file" "$quadlet_tmpdir"
  1389      local container_service=$QUADLET_SERVICE_NAME
  1390      local container_name=$QUADLET_CONTAINER_NAME
  1391  
  1392      service_setup $container_service
  1393  
  1394      # Image system unit should be active
  1395      run systemctl show --property=ActiveState "$image_service"
  1396      assert "$output" = "ActiveState=active" \
  1397             "quadlet - image tag: image service ActiveState"
  1398  
  1399      # Volume system unit should be active
  1400      run systemctl show --property=ActiveState "$volume_service"
  1401      assert "$output" = "ActiveState=active" \
  1402             "quadlet - image tag: volume service ActiveState"
  1403  
  1404      # Image should exist
  1405      run_podman image exists ${image_for_test}
  1406  
  1407      # Volume should exist
  1408      run_podman volume exists ${volume_name}
  1409  
  1410      run_podman exec $QUADLET_CONTAINER_NAME cat /vol/home/podman/testimage-id
  1411      assert "$output" = $PODMAN_TEST_IMAGE_TAG \
  1412              "quadlet - image files: incorrect testimage-id in bound volume"
  1413  
  1414      # Shutdown the service and remove the image
  1415      service_cleanup $container_service failed
  1416      run_podman image rm --ignore $image_for_test
  1417      run_podman rmi --ignore $(pause_image)
  1418  }
  1419  
  1420  @test "quadlet - pod simple" {
  1421      local quadlet_tmpdir=$PODMAN_TMPDIR/quadlets
  1422  
  1423      local test_pod_name=pod_test_$(random_string)
  1424      local quadlet_pod_unit=$test_pod_name.pod
  1425      local quadlet_pod_file=$PODMAN_TMPDIR/$quadlet_pod_unit
  1426      cat > $quadlet_pod_file <<EOF
  1427  [Pod]
  1428  PodName=$test_pod_name
  1429  EOF
  1430  
  1431      local quadlet_container_unit=pod_test_$(random_string).container
  1432      local quadlet_container_file=$PODMAN_TMPDIR/$quadlet_container_unit
  1433      cat > $quadlet_container_file <<EOF
  1434  [Container]
  1435  Image=$IMAGE
  1436  Exec=sh -c "echo STARTED CONTAINER; echo "READY=1" | socat -u STDIN unix-sendto:\$NOTIFY_SOCKET; sleep inf"
  1437  Pod=$quadlet_pod_unit
  1438  EOF
  1439  
  1440      # Use the same directory for all quadlet files to make sure later steps access previous ones
  1441      mkdir $quadlet_tmpdir
  1442  
  1443      # Have quadlet create the systemd unit file for the pod unit
  1444      run_quadlet "$quadlet_pod_file" "$quadlet_tmpdir"
  1445      # Save the pod service name since the variable will be overwritten
  1446      local pod_service=$QUADLET_SERVICE_NAME
  1447  
  1448      # Have quadlet create the systemd unit file for the container unit
  1449      run_quadlet "$quadlet_container_file" "$quadlet_tmpdir"
  1450      local container_service=$QUADLET_SERVICE_NAME
  1451      local container_name=$QUADLET_CONTAINER_NAME
  1452  
  1453      # Start the pod service
  1454      service_setup $pod_service
  1455  
  1456      # Pod should exist
  1457      run_podman pod exists ${test_pod_name}
  1458  
  1459      # Wait for systemd to activate the container service
  1460      wait_for_command_output "systemctl show --property=ActiveState $container_service" "ActiveState=active"
  1461  
  1462      # Container should exist
  1463      run_podman container exists ${container_name}
  1464  
  1465      # Shutdown the service
  1466      service_cleanup $pod_service inactive
  1467  
  1468      # The service of the container should be active
  1469      run systemctl show --property=ActiveState "$container_service"
  1470      assert "ActiveState=failed" \
  1471             "quadlet - pod base: container service ActiveState"
  1472  
  1473      # Container should not exist
  1474      run_podman 1 container exists ${container_name}
  1475  
  1476      run_podman rmi $(pause_image)
  1477  }
  1478  
  1479  # This test reproduces https://github.com/containers/podman/issues/20432
  1480  # In order to reproduce the issue, the image in the FROM must no be available locally
  1481  # and must not have a tag. The first forces Pull and the second the resolution where the crash occurs
  1482  # Using a local registry does not work since kube play does not pass the autofile and tls-verify flags to the build
  1483  @test "quadlet - kube build from unavailable image with no tag" {
  1484      local quadlet_tmpdir=$PODMAN_TMPDIR/quadlets
  1485  
  1486      local untagged_image=quay.io/libpod/busybox
  1487      local built_image=test_image
  1488      local yaml_dir=$quadlet_tmpdir/$built_image
  1489      local build_dir=$yaml_dir/$built_image
  1490  
  1491      # Use the same directory for all quadlet files to make sure later steps access previous ones
  1492      mkdir -p $build_dir
  1493  
  1494      container_file_path=$build_dir/Containerfile
  1495      cat >$container_file_path << EOF
  1496  FROM $untagged_image
  1497  EOF
  1498  
  1499      # Create the YAMl file
  1500      pod_name="test_pod"
  1501      container_name="test"
  1502      yaml_source="$yaml_dir/build_$(random_string).yaml"
  1503      cat >$yaml_source <<EOF
  1504  apiVersion: v1
  1505  kind: Pod
  1506  metadata:
  1507    labels:
  1508      app: test
  1509    name: $pod_name
  1510  spec:
  1511    containers:
  1512    - command:
  1513      - "sh"
  1514      args:
  1515      - "-c"
  1516      - "echo STARTED CONTAINER; sleep inf"
  1517      image: $built_image
  1518      name: $container_name
  1519  EOF
  1520  
  1521      # Create the Quadlet file
  1522      local quadlet_file=$quadlet_tmpdir/build_$(random_string).kube
  1523      cat > $quadlet_file <<EOF
  1524  [Kube]
  1525  Yaml=${yaml_source}
  1526  PodmanArgs=--build
  1527  SetWorkingDirectory=yaml
  1528  EOF
  1529  
  1530      # Make sure the tagged image is not locally available
  1531      run_podman rmi -i $untagged_image:latest
  1532  
  1533      run_quadlet "$quadlet_file"
  1534      service_setup $QUADLET_SERVICE_NAME
  1535  
  1536      # Ensure we have output.
  1537      wait_for_output "STARTED CONTAINER" $pod_name-$container_name
  1538  
  1539      run_podman container inspect  --format "{{.State.Status}}" test_pod-test
  1540      is "$output" "running" "container should be started by systemd and hence be running"
  1541  
  1542      service_cleanup $QUADLET_SERVICE_NAME inactive
  1543      run_podman rmi $untagged_image:latest $built_image $(pause_image)
  1544  }
  1545  # vim: filetype=sh