1 /*
   2  * Copyright (c) 2020, Red Hat Inc.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 import static org.junit.Assert.assertFalse;
  24 import static org.junit.Assert.assertTrue;
  25 
  26 import java.io.IOException;
  27 import java.nio.charset.StandardCharsets;
  28 import java.nio.file.Files;
  29 import java.nio.file.Path;
  30 import java.nio.file.Paths;
  31 import java.util.Optional;
  32 
  33 import org.junit.After;
  34 import org.junit.Before;
  35 import org.junit.Test;
  36 
  37 import jdk.internal.platform.CgroupSubsystemFactory;
  38 import jdk.internal.platform.CgroupSubsystemFactory.CgroupTypeResult;
  39 import jdk.test.lib.Utils;
  40 import jdk.test.lib.util.FileUtils;
  41 
  42 
  43 /*
  44  * @test
  45  * @requires os.family == "linux"
  46  * @modules java.base/jdk.internal.platform
  47  * @library /test/lib
  48  * @run junit/othervm TestCgroupSubsystemFactory
  49  */
  50 public class TestCgroupSubsystemFactory {
  51 
  52     private Path existingDirectory;
  53     private Path cgroupv1CgInfoZeroHierarchy;
  54     private Path cgroupv1MntInfoZeroHierarchy;
  55     private Path cgroupv2CgInfoZeroHierarchy;
  56     private Path cgroupv2MntInfoZeroHierarchy;
  57     private Path cgroupv1CgInfoNonZeroHierarchy;
  58     private Path cgroupv1MntInfoNonZeroHierarchy;
  59     private String mntInfoEmpty = "";
  60     private String cgroupsZeroHierarchy =
  61             "#subsys_name hierarchy num_cgroups enabled\n" +
  62             "cpuset 0 1 1\n" +
  63             "cpu 0 1 1\n" +
  64             "cpuacct 0 1 1\n" +
  65             "memory 0 1 1\n" +
  66             "devices 0 1 1\n" +
  67             "freezer 0 1 1\n" +
  68             "net_cls 0 1 1\n" +
  69             "blkio 0 1 1\n" +
  70             "perf_event 0 1 1 ";
  71     private String mntInfoHybrid =
  72             "30 23 0:26 / /sys/fs/cgroup ro,nosuid,nodev,noexec shared:4 - tmpfs tmpfs ro,seclabel,mode=755\n" +
  73             "31 30 0:27 / /sys/fs/cgroup/unified rw,nosuid,nodev,noexec,relatime shared:5 - cgroup2 cgroup2 rw,seclabel,nsdelegate\n" +
  74             "32 30 0:28 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime shared:6 - cgroup cgroup rw,seclabel,xattr,name=systemd\n" +
  75             "35 30 0:31 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:7 - cgroup cgroup rw,seclabel,memory\n" +
  76             "36 30 0:32 / /sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime shared:8 - cgroup cgroup rw,seclabel,pids\n" +
  77             "37 30 0:33 / /sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime shared:9 - cgroup cgroup rw,seclabel,perf_event\n" +
  78             "38 30 0:34 / /sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime shared:10 - cgroup cgroup rw,seclabel,net_cls,net_prio\n" +
  79             "39 30 0:35 / /sys/fs/cgroup/hugetlb rw,nosuid,nodev,noexec,relatime shared:11 - cgroup cgroup rw,seclabel,hugetlb\n" +
  80             "40 30 0:36 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:12 - cgroup cgroup rw,seclabel,cpu,cpuacct\n" +
  81             "41 30 0:37 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:13 - cgroup cgroup rw,seclabel,devices\n" +
  82             "42 30 0:38 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:14 - cgroup cgroup rw,seclabel,cpuset\n" +
  83             "43 30 0:39 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime shared:15 - cgroup cgroup rw,seclabel,blkio\n" +
  84             "44 30 0:40 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,seclabel,freezer";
  85     private String cgroupsNonZeroHierarchy =
  86             "#subsys_name hierarchy   num_cgroups enabled\n" +
  87             "cpuset  9   1   1\n" +
  88             "cpu 7   1   1\n" +
  89             "cpuacct 7   1   1\n" +
  90             "blkio   10  1   1\n" +
  91             "memory  2   90  1\n" +
  92             "devices 8   74  1\n" +
  93             "freezer 11  1   1\n" +
  94             "net_cls 5   1   1\n" +
  95             "perf_event  4   1   1\n" +
  96             "net_prio    5   1   1\n" +
  97             "hugetlb 6   1   1\n" +
  98             "pids    3   80  1";
  99     private String mntInfoCgroupsV2Only =
 100             "28 21 0:25 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:4 - cgroup2 cgroup2 rw,seclabel,nsdelegate";
 101 
 102     @Before
 103     public void setup() {
 104         try {
 105             existingDirectory = Utils.createTempDirectory(TestCgroupSubsystemFactory.class.getSimpleName());
 106             Path cgroupsZero = Paths.get(existingDirectory.toString(), "cgroups_zero");
 107             Files.writeString(cgroupsZero, cgroupsZeroHierarchy, StandardCharsets.UTF_8);
 108             cgroupv1CgInfoZeroHierarchy = cgroupsZero;
 109             cgroupv2CgInfoZeroHierarchy = cgroupsZero;
 110             cgroupv1MntInfoZeroHierarchy = Paths.get(existingDirectory.toString(), "mountinfo_empty");
 111             Files.writeString(cgroupv1MntInfoZeroHierarchy, mntInfoEmpty);
 112 
 113             cgroupv2MntInfoZeroHierarchy = Paths.get(existingDirectory.toString(), "mountinfo_cgroupv2");
 114             Files.writeString(cgroupv2MntInfoZeroHierarchy, mntInfoCgroupsV2Only);
 115 
 116             cgroupv1CgInfoNonZeroHierarchy = Paths.get(existingDirectory.toString(), "cgroups_non_zero");
 117             Files.writeString(cgroupv1CgInfoNonZeroHierarchy, cgroupsNonZeroHierarchy);
 118 
 119             cgroupv1MntInfoNonZeroHierarchy = Paths.get(existingDirectory.toString(), "mountinfo_non_zero");
 120             Files.writeString(cgroupv1MntInfoNonZeroHierarchy, mntInfoHybrid);
 121         } catch (IOException e) {
 122             throw new RuntimeException(e);
 123         }
 124     }
 125 
 126     @After
 127     public void teardown() {
 128         try {
 129             FileUtils.deleteFileTreeWithRetry(existingDirectory);
 130         } catch (IOException e) {
 131             System.err.println("Teardown failed. " + e.getMessage());
 132         }
 133     }
 134 
 135     @Test
 136     public void testHybridCgroupsV1() throws IOException {
 137         String cgroups = cgroupv1CgInfoNonZeroHierarchy.toString();
 138         String mountInfo = cgroupv1MntInfoNonZeroHierarchy.toString();
 139         Optional<CgroupTypeResult> result = CgroupSubsystemFactory.determineType(mountInfo, cgroups);
 140 
 141         assertTrue("Expected non-empty cgroup result", result.isPresent());
 142         CgroupTypeResult res = result.get();
 143         assertFalse("hybrid hierarchy expected as cgroups v1", res.isCgroupV2());
 144     }
 145 
 146     @Test
 147     public void testZeroHierarchyCgroupsV1() throws IOException {
 148         String cgroups = cgroupv1CgInfoZeroHierarchy.toString();
 149         String mountInfo = cgroupv1MntInfoZeroHierarchy.toString();
 150         Optional<CgroupTypeResult> result = CgroupSubsystemFactory.determineType(mountInfo, cgroups);
 151 
 152         assertTrue("zero hierarchy ids with no mounted controllers => empty result", result.isEmpty());
 153     }
 154 
 155     @Test
 156     public void testZeroHierarchyCgroupsV2() throws IOException {
 157         String cgroups = cgroupv2CgInfoZeroHierarchy.toString();
 158         String mountInfo = cgroupv2MntInfoZeroHierarchy.toString();
 159         Optional<CgroupTypeResult> result = CgroupSubsystemFactory.determineType(mountInfo, cgroups);
 160 
 161         assertTrue("Expected non-empty cgroup result", result.isPresent());
 162         CgroupTypeResult res = result.get();
 163 
 164         assertTrue("zero hierarchy ids with mounted controllers expected cgroups v2", res.isCgroupV2());
 165     }
 166 
 167     @Test(expected = IOException.class)
 168     public void mountInfoFileNotFound() throws IOException {
 169         String cgroups = cgroupv1CgInfoZeroHierarchy.toString(); // any existing file
 170         String mountInfo = Paths.get(existingDirectory.toString(), "not-existing-mountinfo").toString();
 171 
 172         CgroupSubsystemFactory.determineType(mountInfo, cgroups);
 173     }
 174 
 175     @Test(expected = IOException.class)
 176     public void cgroupsFileNotFound() throws IOException {
 177         String cgroups = Paths.get(existingDirectory.toString(), "not-existing-cgroups").toString();
 178         String mountInfo = cgroupv2MntInfoZeroHierarchy.toString(); // any existing file
 179         CgroupSubsystemFactory.determineType(mountInfo, cgroups);
 180     }
 181 }