mirror of
				https://github.com/NVIDIA/nvidia-container-toolkit
				synced 2025-06-26 18:18:24 +00:00 
			
		
		
		
	Add lookup abstraction for locating executable files
Signed-off-by: Evan Lezar <elezar@nvidia.com>
This commit is contained in:
		
							parent
							
								
									7137f4b05b
								
							
						
					
					
						commit
						390e5747ea
					
				
							
								
								
									
										85
									
								
								internal/lookup/file.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								internal/lookup/file.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,85 @@ | |||||||
|  | /* | ||||||
|  | # Copyright (c) 2021, NVIDIA CORPORATION.  All rights reserved. | ||||||
|  | # | ||||||
|  | # 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 lookup | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"os" | ||||||
|  | 	"path/filepath" | ||||||
|  | 
 | ||||||
|  | 	log "github.com/sirupsen/logrus" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // file can be used to locate file (or file-like elements) at a specified set of
 | ||||||
|  | // prefixes. The validity of a file is determined by a filter function.
 | ||||||
|  | type file struct { | ||||||
|  | 	logger   *log.Logger | ||||||
|  | 	prefixes []string | ||||||
|  | 	filter   func(string) error | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // NewFileLocator creates a Locator that can be used to find files at the specified root. A logger
 | ||||||
|  | // can also be specified.
 | ||||||
|  | func NewFileLocator(logger *log.Logger, root string) Locator { | ||||||
|  | 	l := newFileLocator(logger, root) | ||||||
|  | 
 | ||||||
|  | 	return &l | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func newFileLocator(logger *log.Logger, root string) file { | ||||||
|  | 	return file{ | ||||||
|  | 		logger:   logger, | ||||||
|  | 		prefixes: []string{root}, | ||||||
|  | 		filter:   assertFile, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | var _ Locator = (*file)(nil) | ||||||
|  | 
 | ||||||
|  | // Locate attempts to find the specified file. All prefixes are searched and any matching
 | ||||||
|  | // candidates are returned. If no matches are found, an error is returned.
 | ||||||
|  | func (p file) Locate(filename string) ([]string, error) { | ||||||
|  | 	var filenames []string | ||||||
|  | 	for _, prefix := range p.prefixes { | ||||||
|  | 		candidate := filepath.Join(prefix, filename) | ||||||
|  | 		p.logger.Debugf("Checking candidate '%v'", candidate) | ||||||
|  | 		err := p.filter(candidate) | ||||||
|  | 		if err != nil { | ||||||
|  | 			p.logger.Debugf("Candidate '%v' does not meet requirements: %v", candidate, err) | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		filenames = append(filenames, candidate) | ||||||
|  | 	} | ||||||
|  | 	if len(filename) == 0 { | ||||||
|  | 		return nil, fmt.Errorf("file %v not found", filename) | ||||||
|  | 	} | ||||||
|  | 	return filenames, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // assertFile checks whether the specified path is a regular file
 | ||||||
|  | func assertFile(filename string) error { | ||||||
|  | 	info, err := os.Stat(filename) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("error getting info for %v: %v", filename, err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if info.IsDir() { | ||||||
|  | 		return fmt.Errorf("specified path '%v' is a directory", filename) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										24
									
								
								internal/lookup/locator.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								internal/lookup/locator.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | |||||||
|  | /* | ||||||
|  | # Copyright (c) 2021, NVIDIA CORPORATION.  All rights reserved. | ||||||
|  | # | ||||||
|  | # 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 lookup | ||||||
|  | 
 | ||||||
|  | //go:generate moq -stub -out locator_mock.go . Locator
 | ||||||
|  | 
 | ||||||
|  | // Locator defines the interface for locating files on a system.
 | ||||||
|  | type Locator interface { | ||||||
|  | 	Locate(string) ([]string, error) | ||||||
|  | } | ||||||
							
								
								
									
										77
									
								
								internal/lookup/locator_mock.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								internal/lookup/locator_mock.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,77 @@ | |||||||
|  | // Code generated by moq; DO NOT EDIT.
 | ||||||
|  | // github.com/matryer/moq
 | ||||||
|  | 
 | ||||||
|  | package lookup | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"sync" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Ensure, that LocatorMock does implement Locator.
 | ||||||
|  | // If this is not the case, regenerate this file with moq.
 | ||||||
|  | var _ Locator = &LocatorMock{} | ||||||
|  | 
 | ||||||
|  | // LocatorMock is a mock implementation of Locator.
 | ||||||
|  | //
 | ||||||
|  | // 	func TestSomethingThatUsesLocator(t *testing.T) {
 | ||||||
|  | //
 | ||||||
|  | // 		// make and configure a mocked Locator
 | ||||||
|  | // 		mockedLocator := &LocatorMock{
 | ||||||
|  | // 			LocateFunc: func(s string) ([]string, error) {
 | ||||||
|  | // 				panic("mock out the Locate method")
 | ||||||
|  | // 			},
 | ||||||
|  | // 		}
 | ||||||
|  | //
 | ||||||
|  | // 		// use mockedLocator in code that requires Locator
 | ||||||
|  | // 		// and then make assertions.
 | ||||||
|  | //
 | ||||||
|  | // 	}
 | ||||||
|  | type LocatorMock struct { | ||||||
|  | 	// LocateFunc mocks the Locate method.
 | ||||||
|  | 	LocateFunc func(s string) ([]string, error) | ||||||
|  | 
 | ||||||
|  | 	// calls tracks calls to the methods.
 | ||||||
|  | 	calls struct { | ||||||
|  | 		// Locate holds details about calls to the Locate method.
 | ||||||
|  | 		Locate []struct { | ||||||
|  | 			// S is the s argument value.
 | ||||||
|  | 			S string | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	lockLocate sync.RWMutex | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Locate calls LocateFunc.
 | ||||||
|  | func (mock *LocatorMock) Locate(s string) ([]string, error) { | ||||||
|  | 	callInfo := struct { | ||||||
|  | 		S string | ||||||
|  | 	}{ | ||||||
|  | 		S: s, | ||||||
|  | 	} | ||||||
|  | 	mock.lockLocate.Lock() | ||||||
|  | 	mock.calls.Locate = append(mock.calls.Locate, callInfo) | ||||||
|  | 	mock.lockLocate.Unlock() | ||||||
|  | 	if mock.LocateFunc == nil { | ||||||
|  | 		var ( | ||||||
|  | 			stringsOut []string | ||||||
|  | 			errOut     error | ||||||
|  | 		) | ||||||
|  | 		return stringsOut, errOut | ||||||
|  | 	} | ||||||
|  | 	return mock.LocateFunc(s) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // LocateCalls gets all the calls that were made to Locate.
 | ||||||
|  | // Check the length with:
 | ||||||
|  | //     len(mockedLocator.LocateCalls())
 | ||||||
|  | func (mock *LocatorMock) LocateCalls() []struct { | ||||||
|  | 	S string | ||||||
|  | } { | ||||||
|  | 	var calls []struct { | ||||||
|  | 		S string | ||||||
|  | 	} | ||||||
|  | 	mock.lockLocate.RLock() | ||||||
|  | 	calls = mock.calls.Locate | ||||||
|  | 	mock.lockLocate.RUnlock() | ||||||
|  | 	return calls | ||||||
|  | } | ||||||
							
								
								
									
										93
									
								
								internal/lookup/path.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								internal/lookup/path.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,93 @@ | |||||||
|  | /* | ||||||
|  | # Copyright (c) 2021, NVIDIA CORPORATION.  All rights reserved. | ||||||
|  | # | ||||||
|  | # 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 lookup | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"os" | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"strings" | ||||||
|  | 
 | ||||||
|  | 	log "github.com/sirupsen/logrus" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	envPath = "PATH" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var defaultPaths = []string{"/usr/local/sbin", "/usr/local/bin", "/usr/sbin", "/usr/bin", "/sbin", "/bin"} | ||||||
|  | 
 | ||||||
|  | type path struct { | ||||||
|  | 	file | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // NewPathLocator creates a locator to fine executable files in the path. A logger can also be specified.
 | ||||||
|  | func NewPathLocator(logger *log.Logger, root string) Locator { | ||||||
|  | 	pathEnv := os.Getenv(envPath) | ||||||
|  | 	paths := filepath.SplitList(pathEnv) | ||||||
|  | 
 | ||||||
|  | 	if root != "" { | ||||||
|  | 		paths = append(paths, defaultPaths...) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var prefixes []string | ||||||
|  | 	for _, dir := range paths { | ||||||
|  | 		prefixes = append(prefixes, filepath.Join(root, dir)) | ||||||
|  | 	} | ||||||
|  | 	l := path{ | ||||||
|  | 		file: file{ | ||||||
|  | 			logger:   logger, | ||||||
|  | 			prefixes: prefixes, | ||||||
|  | 			filter:   assertExecutable, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	return &l | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | var _ Locator = (*path)(nil) | ||||||
|  | 
 | ||||||
|  | // Locate finds executable files in the path. If a relative or absolute path is specified, the prefix paths are not considered.
 | ||||||
|  | func (p path) Locate(filename string) ([]string, error) { | ||||||
|  | 	// For absolute paths we ensure that it is executable
 | ||||||
|  | 	if strings.Contains(filename, "/") { | ||||||
|  | 		err := assertExecutable(filename) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, fmt.Errorf("absolute path %v is not an executable file: %v", filename, err) | ||||||
|  | 		} | ||||||
|  | 		return []string{filename}, nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return p.file.Locate(filename) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // assertExecutable checks whether the specified path is an execuable file.
 | ||||||
|  | func assertExecutable(filename string) error { | ||||||
|  | 	err := assertFile(filename) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	info, err := os.Stat(filename) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if info.Mode()&0111 == 0 { | ||||||
|  | 		return fmt.Errorf("specified file '%v' is not executable", filename) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user