diff --git a/test/conformance/runtime/filesystem_test.go b/test/conformance/runtime/filesystem_test.go index 5c34ee7cd162..beeb789f7840 100644 --- a/test/conformance/runtime/filesystem_test.go +++ b/test/conformance/runtime/filesystem_test.go @@ -72,6 +72,14 @@ func testFiles(t *testing.T, clients *test.Clients, paths map[string]types.FileI return fmt.Errorf("%s has invalid permissions string %s: %w", path, riFile.Perm, err) } } + + riFileAccess, ok := ri.Host.FileAccess[path] + if ok && riFileAccess.ReadErr != "" { + return fmt.Errorf("Want no read errors for file %s, got error: %s", path, riFileAccess.ReadErr) + } + if ok && riFileAccess.WriteErr != "" { + return fmt.Errorf("Want no write errors for file %s, got error: %s", path, riFileAccess.WriteErr) + } } return nil } diff --git a/test/test_images/runtime/handlers/file_access_attempt.go b/test/test_images/runtime/handlers/file_access_attempt.go new file mode 100644 index 000000000000..92ac3a20e7df --- /dev/null +++ b/test/test_images/runtime/handlers/file_access_attempt.go @@ -0,0 +1,106 @@ +/* +Copyright 2019 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package handlers + +import ( + "io/ioutil" + "os" + + "knative.dev/serving/test/types" +) + +type permissionBits uint32 + +const ( + otherRead permissionBits = 1 << (2 - iota) + otherWrite +) + +func (p permissionBits) hasPermission(mode permissionBits) bool { + return p&mode == mode +} + +func fileAccessAttempt(filePaths ...string) map[string]types.FileAccessInfo { + files := map[string]types.FileAccessInfo{} + for _, path := range filePaths { + accessInfo := types.FileAccessInfo{} + + if err := checkReadable(path); err != nil { + accessInfo.ReadErr = err.Error() + } + + if err := checkWritable(path); err != nil { + accessInfo.WriteErr = err.Error() + } + + files[path] = accessInfo + } + return files +} + +// checkReadable function checks whether path file or directory is readable +func checkReadable(path string) error { + file, err := os.Stat(path) // It should only return error only if file does not exist + if err != nil { + return err + } + + // We aren't expected to be able to read, so just exit + perm := permissionBits(file.Mode().Perm()) + if !perm.hasPermission(otherRead) { + return nil + } + + if file.IsDir() { + _, err := ioutil.ReadDir(path) + return err + } + + readFile, err := os.OpenFile(path, os.O_RDONLY, 0) + if err != nil { + return err + } + return readFile.Close() +} + +// checkWritable function checks whether path file or directory is writable +func checkWritable(path string) error { + file, err := os.Stat(path) // It should only return error only if file does not exist + if err != nil { + return err + } + + // We aren't expected to be able to write, so just exits + perm := permissionBits(file.Mode().Perm()) + if !perm.hasPermission(otherWrite) { + return nil + } + + if file.IsDir() { + writeFile, err := ioutil.TempFile(path, "random") + if writeFile != nil { + os.Remove(writeFile.Name()) + } + return err + } + + writeFile, err := os.OpenFile(path, os.O_APPEND, 0) + if writeFile != nil { + defer writeFile.Close() + } + return err +} diff --git a/test/test_images/runtime/handlers/runtime.go b/test/test_images/runtime/handlers/runtime.go index 85d06ce51cb4..21d29acf5b36 100644 --- a/test/test_images/runtime/handlers/runtime.go +++ b/test/test_images/runtime/handlers/runtime.go @@ -20,6 +20,11 @@ import ( "knative.dev/serving/test/types" ) +var fileAccessExclusions = []string{ + "/dev/tty", + "/dev/console", +} + func runtimeHandler(w http.ResponseWriter, r *http.Request) { log.Println("Retrieving Runtime Information") w.Header().Set("Content-Type", "application/json") @@ -38,14 +43,33 @@ func runtimeHandler(w http.ResponseWriter, r *http.Request) { k := &types.RuntimeInfo{ Request: requestInfo(r), Host: &types.HostInfo{EnvVars: env(), - Files: fileInfo(filePaths...), - Cgroups: cgroups(cgroupPaths...), - Mounts: mounts(), - Stdin: stdin(), - User: userInfo(), - Args: args(), + Files: fileInfo(filePaths...), + FileAccess: fileAccessAttempt(excludeFilePaths(filePaths, fileAccessExclusions)...), + Cgroups: cgroups(cgroupPaths...), + Mounts: mounts(), + Stdin: stdin(), + User: userInfo(), + Args: args(), }, } writeJSON(w, k) } + +func excludeFilePaths(filePaths []string, excludedPaths []string) []string { + var paths []string + for _, path := range filePaths { + excluded := false + for _, excludedPath := range excludedPaths { + if path == excludedPath { + excluded = true + break + } + } + + if !excluded { + paths = append(paths, path) + } + } + return paths +} diff --git a/test/types/runtime.go b/test/types/runtime.go index 08744adfb20d..c8dd7c242e18 100644 --- a/test/types/runtime.go +++ b/test/types/runtime.go @@ -141,6 +141,8 @@ type HostInfo struct { Files map[string]FileInfo `json:"files"` // EnvVars is a map of all environment variables set. EnvVars map[string]string `json:"envs"` + // FileAccess is a map of file access information + FileAccess map[string]FileAccessInfo `json:"fileaccess"` // Cgroups is a list of cgroup information. Cgroups []*Cgroup `json:"cgroups"` // Mounts is a list of mounted volume information, or error. @@ -189,6 +191,16 @@ type FileInfo struct { Error string `json:"error,omitempty"` } +// FileAccessInfo contains the file access information +type FileAccessInfo struct { + // ReadErr is the String representation of an error received when attempting to read + // a file or directory + ReadErr string `json:"read_error,omitempty"` + // WriteErr is the String representation of an error received when attempting to write + // to a file or directory + WriteErr string `json:"write_error,omitempty"` +} + // Cgroup contains the Cgroup value for a given setting. type Cgroup struct { // Name is the full path name of the cgroup.