|
| 1 | +package dev_tools |
| 2 | + |
| 3 | +// This file contains tests that can be run on the generated packages. |
| 4 | +// To run these tests use `go test package_test.go`. |
| 5 | + |
| 6 | +import ( |
| 7 | + "archive/tar" |
| 8 | + "archive/zip" |
| 9 | + "bytes" |
| 10 | + "compress/gzip" |
| 11 | + "flag" |
| 12 | + "io" |
| 13 | + "os" |
| 14 | + "path/filepath" |
| 15 | + "regexp" |
| 16 | + "strings" |
| 17 | + "testing" |
| 18 | + |
| 19 | + "github.com/blakesmith/ar" |
| 20 | + "github.com/cavaliercoder/go-rpm" |
| 21 | +) |
| 22 | + |
| 23 | +const ( |
| 24 | + expectedConfigMode = os.FileMode(0600) |
| 25 | + expectedConfigUID = 0 |
| 26 | + expectedConfigGID = 0 |
| 27 | +) |
| 28 | + |
| 29 | +var ( |
| 30 | + configFilePattern = regexp.MustCompile(`.*beat\.yml`) |
| 31 | +) |
| 32 | + |
| 33 | +var ( |
| 34 | + files = flag.String("files", "../build/upload/*/*", "filepath glob containing package files") |
| 35 | +) |
| 36 | + |
| 37 | +func TestRPM(t *testing.T) { |
| 38 | + rpms := getFiles(t, regexp.MustCompile(`\.rpm$`)) |
| 39 | + for _, rpm := range rpms { |
| 40 | + checkRPM(t, rpm) |
| 41 | + } |
| 42 | +} |
| 43 | + |
| 44 | +func TestDeb(t *testing.T) { |
| 45 | + debs := getFiles(t, regexp.MustCompile(`\.deb$`)) |
| 46 | + buf := new(bytes.Buffer) |
| 47 | + for _, deb := range debs { |
| 48 | + checkDeb(t, deb, buf) |
| 49 | + } |
| 50 | +} |
| 51 | + |
| 52 | +func TestTar(t *testing.T) { |
| 53 | + tars := getFiles(t, regexp.MustCompile(`\.tar\.gz$`)) |
| 54 | + for _, tar := range tars { |
| 55 | + checkTar(t, tar) |
| 56 | + } |
| 57 | +} |
| 58 | + |
| 59 | +func TestZip(t *testing.T) { |
| 60 | + zips := getFiles(t, regexp.MustCompile(`^\w+beat-\S+.zip$`)) |
| 61 | + for _, zip := range zips { |
| 62 | + checkZip(t, zip) |
| 63 | + } |
| 64 | +} |
| 65 | + |
| 66 | +// Sub-tests |
| 67 | + |
| 68 | +func checkRPM(t *testing.T, file string) { |
| 69 | + p, err := readRPM(file) |
| 70 | + if err != nil { |
| 71 | + t.Error(err) |
| 72 | + return |
| 73 | + } |
| 74 | + |
| 75 | + checkConfigPermissions(t, p) |
| 76 | +} |
| 77 | + |
| 78 | +func checkDeb(t *testing.T, file string, buf *bytes.Buffer) { |
| 79 | + p, err := readDeb(file, buf) |
| 80 | + if err != nil { |
| 81 | + t.Error(err) |
| 82 | + return |
| 83 | + } |
| 84 | + |
| 85 | + checkConfigPermissions(t, p) |
| 86 | + checkConfigOwner(t, p) |
| 87 | +} |
| 88 | + |
| 89 | +func checkTar(t *testing.T, file string) { |
| 90 | + p, err := readTar(file) |
| 91 | + if err != nil { |
| 92 | + t.Error(err) |
| 93 | + return |
| 94 | + } |
| 95 | + |
| 96 | + checkConfigPermissions(t, p) |
| 97 | + checkConfigOwner(t, p) |
| 98 | +} |
| 99 | + |
| 100 | +func checkZip(t *testing.T, file string) { |
| 101 | + p, err := readZip(file) |
| 102 | + if err != nil { |
| 103 | + t.Error(err) |
| 104 | + return |
| 105 | + } |
| 106 | + |
| 107 | + checkConfigPermissions(t, p) |
| 108 | +} |
| 109 | + |
| 110 | +// Verify that the main configuration file is installed with a 0600 file mode. |
| 111 | +func checkConfigPermissions(t *testing.T, p *packageFile) { |
| 112 | + t.Run(p.Name+" config file permissions", func(t *testing.T) { |
| 113 | + for _, entry := range p.Contents { |
| 114 | + if configFilePattern.MatchString(entry.File) { |
| 115 | + mode := entry.Mode.Perm() |
| 116 | + if expectedConfigMode != mode { |
| 117 | + t.Errorf("file %v has wrong permissions: expected=%v actual=%v", |
| 118 | + entry.Mode, expectedConfigMode, mode) |
| 119 | + } |
| 120 | + return |
| 121 | + } |
| 122 | + } |
| 123 | + t.Errorf("no config file found matching %v", configFilePattern) |
| 124 | + }) |
| 125 | +} |
| 126 | + |
| 127 | +func checkConfigOwner(t *testing.T, p *packageFile) { |
| 128 | + t.Run(p.Name+" config file owner", func(t *testing.T) { |
| 129 | + for _, entry := range p.Contents { |
| 130 | + if configFilePattern.MatchString(entry.File) { |
| 131 | + if expectedConfigUID != entry.UID { |
| 132 | + t.Errorf("file %v should be owned by user %v, owner=%v", entry.File, expectedConfigGID, entry.UID) |
| 133 | + } |
| 134 | + if expectedConfigGID != entry.GID { |
| 135 | + t.Errorf("file %v should be owned by group %v, group=%v", entry.File, expectedConfigGID, entry.GID) |
| 136 | + } |
| 137 | + return |
| 138 | + } |
| 139 | + } |
| 140 | + t.Errorf("no config file found matching %v", configFilePattern) |
| 141 | + }) |
| 142 | +} |
| 143 | + |
| 144 | +// Helpers |
| 145 | + |
| 146 | +type packageFile struct { |
| 147 | + Name string |
| 148 | + Contents map[string]packageEntry |
| 149 | +} |
| 150 | + |
| 151 | +type packageEntry struct { |
| 152 | + File string |
| 153 | + UID int |
| 154 | + GID int |
| 155 | + Mode os.FileMode |
| 156 | +} |
| 157 | + |
| 158 | +func getFiles(t *testing.T, pattern *regexp.Regexp) []string { |
| 159 | + matches, err := filepath.Glob(*files) |
| 160 | + if err != nil { |
| 161 | + t.Fatal(err) |
| 162 | + } |
| 163 | + |
| 164 | + files := matches[:0] |
| 165 | + for _, f := range matches { |
| 166 | + if pattern.MatchString(filepath.Base(f)) { |
| 167 | + files = append(files, f) |
| 168 | + } |
| 169 | + } |
| 170 | + |
| 171 | + return files |
| 172 | +} |
| 173 | + |
| 174 | +func readRPM(rpmFile string) (*packageFile, error) { |
| 175 | + p, err := rpm.OpenPackageFile(rpmFile) |
| 176 | + if err != nil { |
| 177 | + return nil, err |
| 178 | + } |
| 179 | + |
| 180 | + contents := p.Files() |
| 181 | + pf := &packageFile{Name: filepath.Base(rpmFile), Contents: map[string]packageEntry{}} |
| 182 | + |
| 183 | + for _, file := range contents { |
| 184 | + pf.Contents[file.Name()] = packageEntry{ |
| 185 | + File: file.Name(), |
| 186 | + Mode: file.Mode(), |
| 187 | + } |
| 188 | + } |
| 189 | + |
| 190 | + return pf, nil |
| 191 | +} |
| 192 | + |
| 193 | +// readDeb reads the data.tar.gz file from the .deb. |
| 194 | +func readDeb(debFile string, dataBuffer *bytes.Buffer) (*packageFile, error) { |
| 195 | + file, err := os.Open(debFile) |
| 196 | + if err != nil { |
| 197 | + return nil, err |
| 198 | + } |
| 199 | + defer file.Close() |
| 200 | + |
| 201 | + arReader := ar.NewReader(file) |
| 202 | + for { |
| 203 | + header, err := arReader.Next() |
| 204 | + if err != nil { |
| 205 | + if err == io.EOF { |
| 206 | + break |
| 207 | + } |
| 208 | + return nil, err |
| 209 | + } |
| 210 | + |
| 211 | + if strings.HasPrefix(header.Name, "data.tar.gz") { |
| 212 | + dataBuffer.Reset() |
| 213 | + _, err := io.Copy(dataBuffer, arReader) |
| 214 | + if err != nil { |
| 215 | + return nil, err |
| 216 | + } |
| 217 | + |
| 218 | + gz, err := gzip.NewReader(dataBuffer) |
| 219 | + if err != nil { |
| 220 | + return nil, err |
| 221 | + } |
| 222 | + defer gz.Close() |
| 223 | + |
| 224 | + return readTarContents(filepath.Base(debFile), gz) |
| 225 | + } |
| 226 | + } |
| 227 | + |
| 228 | + return nil, io.EOF |
| 229 | +} |
| 230 | + |
| 231 | +func readTar(tarFile string) (*packageFile, error) { |
| 232 | + file, err := os.Open(tarFile) |
| 233 | + if err != nil { |
| 234 | + return nil, err |
| 235 | + } |
| 236 | + defer file.Close() |
| 237 | + |
| 238 | + var fileReader io.ReadCloser = file |
| 239 | + if strings.HasSuffix(tarFile, ".gz") { |
| 240 | + if fileReader, err = gzip.NewReader(file); err != nil { |
| 241 | + return nil, err |
| 242 | + } |
| 243 | + defer fileReader.Close() |
| 244 | + } |
| 245 | + |
| 246 | + return readTarContents(filepath.Base(tarFile), fileReader) |
| 247 | +} |
| 248 | + |
| 249 | +func readTarContents(tarName string, data io.Reader) (*packageFile, error) { |
| 250 | + tarReader := tar.NewReader(data) |
| 251 | + |
| 252 | + p := &packageFile{Name: tarName, Contents: map[string]packageEntry{}} |
| 253 | + for { |
| 254 | + header, err := tarReader.Next() |
| 255 | + if err != nil { |
| 256 | + if err == io.EOF { |
| 257 | + break |
| 258 | + } |
| 259 | + return nil, err |
| 260 | + } |
| 261 | + |
| 262 | + p.Contents[header.Name] = packageEntry{ |
| 263 | + File: header.Name, |
| 264 | + UID: header.Uid, |
| 265 | + GID: header.Gid, |
| 266 | + Mode: os.FileMode(header.Mode), |
| 267 | + } |
| 268 | + } |
| 269 | + |
| 270 | + return p, nil |
| 271 | +} |
| 272 | + |
| 273 | +func readZip(zipFile string) (*packageFile, error) { |
| 274 | + r, err := zip.OpenReader(zipFile) |
| 275 | + if err != nil { |
| 276 | + return nil, err |
| 277 | + } |
| 278 | + defer r.Close() |
| 279 | + |
| 280 | + p := &packageFile{Name: filepath.Base(zipFile), Contents: map[string]packageEntry{}} |
| 281 | + for _, f := range r.File { |
| 282 | + p.Contents[f.Name] = packageEntry{ |
| 283 | + File: f.Name, |
| 284 | + Mode: f.Mode(), |
| 285 | + } |
| 286 | + } |
| 287 | + |
| 288 | + return p, nil |
| 289 | +} |
0 commit comments