github.com/opencontainers/umoci@v0.4.8-0.20240508124516-656e4836fb0d/oci/layer/utils_test.go (about) 1 /* 2 * umoci: Umoci Modifies Open Containers' Images 3 * Copyright (C) 2016-2020 SUSE LLC 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package layer 19 20 import ( 21 "archive/tar" 22 "testing" 23 24 rspec "github.com/opencontainers/runtime-spec/specs-go" 25 rootlesscontainers "github.com/rootless-containers/proto/go-proto" 26 "google.golang.org/protobuf/proto" 27 ) 28 29 // We need to have individual tests for the rootlesscontainers-proto, because 30 // when we do integration tests for the "user.rootlesscontainers" handling we 31 // don't have access to the protobuf parser from within bats (we just match the 32 // strings which isn't as easy to debug). 33 34 // TestMapRootless ensures that mapHeader correctly handles the rootless case 35 // (both for user.rootlesscontainers and the more general case). 36 func TestMapRootless(t *testing.T) { 37 // Just a basic "*archive/tar.Header" that we use for testing. 38 rootUID := 1000 39 rootGID := 100 40 mapOptions := MapOptions{ 41 UIDMappings: []rspec.LinuxIDMapping{{HostID: uint32(rootUID), ContainerID: 0, Size: 1}}, 42 GIDMappings: []rspec.LinuxIDMapping{{HostID: uint32(rootGID), ContainerID: 0, Size: 1}}, 43 Rootless: true, 44 } 45 baseHdr := tar.Header{ 46 Name: "etc/passwd", 47 Typeflag: tar.TypeReg, 48 Xattrs: make(map[string]string), 49 } 50 51 for idx, test := range []struct { 52 uid, gid int // (uid, gid) for hdr 53 proto *rootlesscontainers.Resource // (uid, gid) for xattr 54 outUID, outGID int // (uid, gid) after map 55 errorExpected bool 56 }{ 57 // Noop values. 58 {0, 0, nil, 0, 0, false}, 59 {0, 0, &rootlesscontainers.Resource{Uid: rootlesscontainers.NoopID, Gid: rootlesscontainers.NoopID}, 0, 0, false}, 60 // Basic mappings. 61 {0, 0, &rootlesscontainers.Resource{Uid: 123, Gid: rootlesscontainers.NoopID}, 123, 0, false}, 62 {0, 0, &rootlesscontainers.Resource{Uid: rootlesscontainers.NoopID, Gid: 456}, 0, 456, false}, 63 {0, 0, &rootlesscontainers.Resource{Uid: 8001, Gid: 1337}, 8001, 1337, false}, 64 // XXX: We cannot enable these tests at the moment because we currently 65 // just ignore the owner and always set it to 0 for rootless 66 // containers. 67 //{851, 182, &rootlesscontainers.Resource{Uid: rootlesscontainers.NoopID, Gid: rootlesscontainers.NoopID}, 851, 182, false}, 68 //{154, 992, &rootlesscontainers.Resource{Uid: 9982, Gid: rootlesscontainers.NoopID}, 9982, 992, false}, 69 //{291, 875, &rootlesscontainers.Resource{Uid: 42158, Gid: 31337}, 42158, 31337, false}, 70 } { 71 // Update baseHdr. 72 baseHdr.Uid = test.uid 73 baseHdr.Gid = test.gid 74 delete(baseHdr.Xattrs, rootlesscontainers.Keyname) 75 if test.proto != nil { 76 payload, err := proto.Marshal(test.proto) 77 if err != nil { 78 t.Errorf("test%d: failed to marshal proto in test", idx) 79 continue 80 } 81 baseHdr.Xattrs[rootlesscontainers.Keyname] = string(payload) 82 } 83 84 // Map. 85 err := mapHeader(&baseHdr, mapOptions) 86 if (err != nil) != test.errorExpected { 87 t.Errorf("test%d: error value was unexpected, errorExpected=%v got err=%v", idx, test.errorExpected, err) 88 continue 89 } 90 91 // Output header shouldn't contain "user.rootlesscontainers". 92 if value, ok := baseHdr.Xattrs[rootlesscontainers.Keyname]; ok { 93 t.Errorf("test%d: 'user.rootlesscontainers' present in output: value=%v", idx, value) 94 } 95 // Make sure that the uid and gid are what we wanted. 96 if uid := baseHdr.Uid; uid != test.outUID { 97 t.Errorf("test%d: got unexpected uid: wanted %v got %v", idx, test.outUID, uid) 98 } 99 if gid := baseHdr.Gid; gid != test.outGID { 100 t.Errorf("test%d: got unexpected gid: wanted %v got %v", idx, test.outGID, gid) 101 } 102 } 103 } 104 105 // TestUnmapRootless ensures that unmapHeader correctly handles the rootless 106 // case (both for user.rootlesscontainers and the more general case). 107 func TestUnmapRootless(t *testing.T) { 108 // Just a basic "*archive/tar.Header" that we use for testing. 109 rootUID := 1000 110 rootGID := 100 111 mapOptions := MapOptions{ 112 UIDMappings: []rspec.LinuxIDMapping{{HostID: uint32(rootUID), ContainerID: 0, Size: 1}}, 113 GIDMappings: []rspec.LinuxIDMapping{{HostID: uint32(rootGID), ContainerID: 0, Size: 1}}, 114 Rootless: true, 115 } 116 baseHdr := tar.Header{ 117 Name: "etc/passwd", 118 Typeflag: tar.TypeReg, 119 } 120 121 for idx, test := range []struct { 122 uid, gid int // (uid, gid) for hdr 123 proto *rootlesscontainers.Resource // expected "user.rootlesscontainers" payload 124 }{ 125 // Basic mappings to check. 126 {0, 0, nil}, 127 {521, 0, &rootlesscontainers.Resource{Uid: 521, Gid: rootlesscontainers.NoopID}}, 128 {0, 942, &rootlesscontainers.Resource{Uid: rootlesscontainers.NoopID, Gid: 942}}, 129 {333, 825, &rootlesscontainers.Resource{Uid: 333, Gid: 825}}, 130 {185, 9923, &rootlesscontainers.Resource{Uid: 185, Gid: 9923}}, 131 } { 132 // Update baseHdr. 133 baseHdr.Uid = test.uid 134 baseHdr.Gid = test.gid 135 delete(baseHdr.Xattrs, rootlesscontainers.Keyname) 136 137 // Unmap. 138 if err := unmapHeader(&baseHdr, mapOptions); err != nil { 139 t.Errorf("test%d: unexpected error in unmapHeader: %v", idx, err) 140 continue 141 } 142 143 // Check the owner. 144 if baseHdr.Uid != rootUID { 145 t.Errorf("test%d: got hdr uid %d when expected %d", idx, baseHdr.Uid, rootUID) 146 } 147 if baseHdr.Gid != rootGID { 148 t.Errorf("test%d: got hdr gid %d when expected %d", idx, baseHdr.Gid, rootGID) 149 } 150 151 // Check that the xattr is what we wanted. 152 if payload, ok := baseHdr.Xattrs[rootlesscontainers.Keyname]; (test.proto != nil) != ok { 153 // Only bad if we expected a proto... 154 t.Errorf("test%d: unexpected situation: expected xattr exist to be %v", idx, test.proto != nil) 155 continue 156 } else if ok { 157 var parsed rootlesscontainers.Resource 158 if err := proto.Unmarshal([]byte(payload), &parsed); err != nil { 159 t.Errorf("test%d: unexpected error parsing payload: %v", idx, err) 160 continue 161 } 162 163 if parsed.Uid != test.proto.Uid { 164 t.Errorf("test%d: got xattr uid %d when expected %d", idx, parsed.Uid, test.proto.Uid) 165 } 166 if parsed.Gid != test.proto.Gid { 167 t.Errorf("test%d: got xattr gid %d when expected %d", idx, parsed.Gid, test.proto.Gid) 168 } 169 } 170 171 // Finally, just do a check to ensure that mapHeader returns the old values. 172 if err := mapHeader(&baseHdr, mapOptions); err != nil { 173 t.Errorf("test%d: unexpected error in mapHeader: %v", idx, err) 174 continue 175 } 176 if baseHdr.Uid != test.uid { 177 t.Errorf("test%d: round-trip of uid failed: expected %d got %d", idx, test.uid, baseHdr.Uid) 178 } 179 if baseHdr.Gid != test.gid { 180 t.Errorf("test%d: round-trip of gid failed: expected %d got %d", idx, test.gid, baseHdr.Gid) 181 } 182 } 183 }