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