diff --git a/internal/fileutil/fileutil.go b/internal/fileutil/fileutil.go deleted file mode 100644 index 739093f3b..000000000 --- a/internal/fileutil/fileutil.go +++ /dev/null @@ -1,51 +0,0 @@ -/* -Copyright The Helm 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 fileutil - -import ( - "io" - "io/ioutil" - "os" - "path/filepath" - - "helm.sh/helm/v3/internal/third_party/dep/fs" -) - -// AtomicWriteFile atomically (as atomic as os.Rename allows) writes a file to a -// disk. -func AtomicWriteFile(filename string, reader io.Reader, mode os.FileMode) error { - tempFile, err := ioutil.TempFile(filepath.Split(filename)) - if err != nil { - return err - } - tempName := tempFile.Name() - - if _, err := io.Copy(tempFile, reader); err != nil { - tempFile.Close() // return value is ignored as we are already on error path - return err - } - - if err := tempFile.Close(); err != nil { - return err - } - - if err := os.Chmod(tempName, mode); err != nil { - return err - } - - return fs.RenameWithFallback(tempName, filename) -} diff --git a/internal/fileutil/fileutil_test.go b/internal/fileutil/fileutil_test.go deleted file mode 100644 index 76cd8f074..000000000 --- a/internal/fileutil/fileutil_test.go +++ /dev/null @@ -1,58 +0,0 @@ -/* -Copyright The Helm 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 fileutil - -import ( - "bytes" - "io/ioutil" - "os" - "path/filepath" - "testing" -) - -func TestAtomicWriteFile(t *testing.T) { - dir := t.TempDir() - - testpath := filepath.Join(dir, "test") - stringContent := "Test content" - reader := bytes.NewReader([]byte(stringContent)) - mode := os.FileMode(0644) - - err := AtomicWriteFile(testpath, reader, mode) - if err != nil { - t.Errorf("AtomicWriteFile error: %s", err) - } - - got, err := ioutil.ReadFile(testpath) - if err != nil { - t.Fatal(err) - } - - if stringContent != string(got) { - t.Fatalf("expected: %s, got: %s", stringContent, string(got)) - } - - gotinfo, err := os.Stat(testpath) - if err != nil { - t.Fatal(err) - } - - if mode != gotinfo.Mode() { - t.Fatalf("expected %s: to be the same mode as %s", - mode, gotinfo.Mode()) - } -} diff --git a/internal/ignore/doc.go b/internal/ignore/doc.go deleted file mode 100644 index e6a6a6c7b..000000000 --- a/internal/ignore/doc.go +++ /dev/null @@ -1,67 +0,0 @@ -/* -Copyright The Helm 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 ignore provides tools for writing ignore files (a la .gitignore). - -This provides both an ignore parser and a file-aware processor. - -The format of ignore files closely follows, but does not exactly match, the -format for .gitignore files (https://git-scm.com/docs/gitignore). - -The formatting rules are as follows: - - - Parsing is line-by-line - - Empty lines are ignored - - Lines the begin with # (comments) will be ignored - - Leading and trailing spaces are always ignored - - Inline comments are NOT supported ('foo* # Any foo' does not contain a comment) - - There is no support for multi-line patterns - - Shell glob patterns are supported. See Go's "path/filepath".Match - - If a pattern begins with a leading !, the match will be negated. - - If a pattern begins with a leading /, only paths relatively rooted will match. - - If the pattern ends with a trailing /, only directories will match - - If a pattern contains no slashes, file basenames are tested (not paths) - - The pattern sequence "**", while legal in a glob, will cause an error here - (to indicate incompatibility with .gitignore). - -Example: - - # Match any file named foo.txt - foo.txt - - # Match any text file - *.txt - - # Match only directories named mydir - mydir/ - - # Match only text files in the top-level directory - /*.txt - - # Match only the file foo.txt in the top-level directory - /foo.txt - - # Match any file named ab.txt, ac.txt, or ad.txt - a[b-d].txt - -Notable differences from .gitignore: - - The '**' syntax is not supported. - - The globbing library is Go's 'filepath.Match', not fnmatch(3) - - Trailing spaces are always ignored (there is no supported escape sequence) - - The evaluation of escape sequences has not been tested for compatibility - - There is no support for '\!' as a special leading sequence. -*/ -package ignore // import "helm.sh/helm/v3/internal/ignore" diff --git a/internal/ignore/rules.go b/internal/ignore/rules.go deleted file mode 100644 index a80923baf..000000000 --- a/internal/ignore/rules.go +++ /dev/null @@ -1,228 +0,0 @@ -/* -Copyright The Helm 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 ignore - -import ( - "bufio" - "bytes" - "io" - "log" - "os" - "path/filepath" - "strings" - - "github.com/pkg/errors" -) - -// HelmIgnore default name of an ignorefile. -const HelmIgnore = ".helmignore" - -// Rules is a collection of path matching rules. -// -// Parse() and ParseFile() will construct and populate new Rules. -// Empty() will create an immutable empty ruleset. -type Rules struct { - patterns []*pattern -} - -// Empty builds an empty ruleset. -func Empty() *Rules { - return &Rules{patterns: []*pattern{}} -} - -// AddDefaults adds default ignore patterns. -// -// Ignore all dotfiles in "templates/" -func (r *Rules) AddDefaults() { - r.parseRule(`templates/.?*`) -} - -// ParseFile parses a helmignore file and returns the *Rules. -func ParseFile(file string) (*Rules, error) { - f, err := os.Open(file) - if err != nil { - return nil, err - } - defer f.Close() - return Parse(f) -} - -// Parse parses a rules file -func Parse(file io.Reader) (*Rules, error) { - r := &Rules{patterns: []*pattern{}} - - s := bufio.NewScanner(file) - currentLine := 0 - utf8bom := []byte{0xEF, 0xBB, 0xBF} - for s.Scan() { - scannedBytes := s.Bytes() - // We trim UTF8 BOM - if currentLine == 0 { - scannedBytes = bytes.TrimPrefix(scannedBytes, utf8bom) - } - line := string(scannedBytes) - currentLine++ - - if err := r.parseRule(line); err != nil { - return r, err - } - } - return r, s.Err() -} - -// Ignore evaluates the file at the given path, and returns true if it should be ignored. -// -// Ignore evaluates path against the rules in order. Evaluation stops when a match -// is found. Matching a negative rule will stop evaluation. -func (r *Rules) Ignore(path string, fi os.FileInfo) bool { - // Don't match on empty dirs. - if path == "" { - return false - } - - // Disallow ignoring the current working directory. - // See issue: - // 1776 (New York City) Hamilton: "Pardon me, are you Aaron Burr, sir?" - if path == "." || path == "./" { - return false - } - for _, p := range r.patterns { - if p.match == nil { - log.Printf("ignore: no matcher supplied for %q", p.raw) - return false - } - - // For negative rules, we need to capture and return non-matches, - // and continue for matches. - if p.negate { - if p.mustDir && !fi.IsDir() { - return true - } - if !p.match(path, fi) { - return true - } - continue - } - - // If the rule is looking for directories, and this is not a directory, - // skip it. - if p.mustDir && !fi.IsDir() { - continue - } - if p.match(path, fi) { - return true - } - } - return false -} - -// parseRule parses a rule string and creates a pattern, which is then stored in the Rules object. -func (r *Rules) parseRule(rule string) error { - rule = strings.TrimSpace(rule) - - // Ignore blank lines - if rule == "" { - return nil - } - // Comment - if strings.HasPrefix(rule, "#") { - return nil - } - - // Fail any rules that contain ** - if strings.Contains(rule, "**") { - return errors.New("double-star (**) syntax is not supported") - } - - // Fail any patterns that can't compile. A non-empty string must be - // given to Match() to avoid optimization that skips rule evaluation. - if _, err := filepath.Match(rule, "abc"); err != nil { - return err - } - - p := &pattern{raw: rule} - - // Negation is handled at a higher level, so strip the leading ! from the - // string. - if strings.HasPrefix(rule, "!") { - p.negate = true - rule = rule[1:] - } - - // Directory verification is handled by a higher level, so the trailing / - // is removed from the rule. That way, a directory named "foo" matches, - // even if the supplied string does not contain a literal slash character. - if strings.HasSuffix(rule, "/") { - p.mustDir = true - rule = strings.TrimSuffix(rule, "/") - } - - if strings.HasPrefix(rule, "/") { - // Require path matches the root path. - p.match = func(n string, fi os.FileInfo) bool { - rule = strings.TrimPrefix(rule, "/") - ok, err := filepath.Match(rule, n) - if err != nil { - log.Printf("Failed to compile %q: %s", rule, err) - return false - } - return ok - } - } else if strings.Contains(rule, "/") { - // require structural match. - p.match = func(n string, fi os.FileInfo) bool { - ok, err := filepath.Match(rule, n) - if err != nil { - log.Printf("Failed to compile %q: %s", rule, err) - return false - } - return ok - } - } else { - p.match = func(n string, fi os.FileInfo) bool { - // When there is no slash in the pattern, we evaluate ONLY the - // filename. - n = filepath.Base(n) - ok, err := filepath.Match(rule, n) - if err != nil { - log.Printf("Failed to compile %q: %s", rule, err) - return false - } - return ok - } - } - - r.patterns = append(r.patterns, p) - return nil -} - -// matcher is a function capable of computing a match. -// -// It returns true if the rule matches. -type matcher func(name string, fi os.FileInfo) bool - -// pattern describes a pattern to be matched in a rule set. -type pattern struct { - // raw is the unparsed string, with nothing stripped. - raw string - // match is the matcher function. - match matcher - // negate indicates that the rule's outcome should be negated. - negate bool - // mustDir indicates that the matched file must be a directory. - mustDir bool -} diff --git a/internal/ignore/rules_test.go b/internal/ignore/rules_test.go deleted file mode 100644 index 9581cf09f..000000000 --- a/internal/ignore/rules_test.go +++ /dev/null @@ -1,155 +0,0 @@ -/* -Copyright The Helm 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 ignore - -import ( - "bytes" - "os" - "path/filepath" - "testing" -) - -var testdata = "./testdata" - -func TestParse(t *testing.T) { - rules := `#ignore - - #ignore -foo -bar/* -baz/bar/foo.txt - -one/more -` - r, err := parseString(rules) - if err != nil { - t.Fatalf("Error parsing rules: %s", err) - } - - if len(r.patterns) != 4 { - t.Errorf("Expected 4 rules, got %d", len(r.patterns)) - } - - expects := []string{"foo", "bar/*", "baz/bar/foo.txt", "one/more"} - for i, p := range r.patterns { - if p.raw != expects[i] { - t.Errorf("Expected %q, got %q", expects[i], p.raw) - } - if p.match == nil { - t.Errorf("Expected %s to have a matcher function.", p.raw) - } - } -} - -func TestParseFail(t *testing.T) { - shouldFail := []string{"foo/**/bar", "[z-"} - for _, fail := range shouldFail { - _, err := parseString(fail) - if err == nil { - t.Errorf("Rule %q should have failed", fail) - } - } -} - -func TestParseFile(t *testing.T) { - f := filepath.Join(testdata, HelmIgnore) - if _, err := os.Stat(f); err != nil { - t.Fatalf("Fixture %s missing: %s", f, err) - } - - r, err := ParseFile(f) - if err != nil { - t.Fatalf("Failed to parse rules file: %s", err) - } - - if len(r.patterns) != 3 { - t.Errorf("Expected 3 patterns, got %d", len(r.patterns)) - } -} - -func TestIgnore(t *testing.T) { - // Test table: Given pattern and name, Ignore should return expect. - tests := []struct { - pattern string - name string - expect bool - }{ - // Glob tests - {`helm.txt`, "helm.txt", true}, - {`helm.*`, "helm.txt", true}, - {`helm.*`, "rudder.txt", false}, - {`*.txt`, "tiller.txt", true}, - {`*.txt`, "cargo/a.txt", true}, - {`cargo/*.txt`, "cargo/a.txt", true}, - {`cargo/*.*`, "cargo/a.txt", true}, - {`cargo/*.txt`, "mast/a.txt", false}, - {`ru[c-e]?er.txt`, "rudder.txt", true}, - {`templates/.?*`, "templates/.dotfile", true}, - // "." should never get ignored. https://github.com/helm/helm/issues/1776 - {`.*`, ".", false}, - {`.*`, "./", false}, - {`.*`, ".joonix", true}, - {`.*`, "helm.txt", false}, - {`.*`, "", false}, - - // Directory tests - {`cargo/`, "cargo", true}, - {`cargo/`, "cargo/", true}, - {`cargo/`, "mast/", false}, - {`helm.txt/`, "helm.txt", false}, - - // Negation tests - {`!helm.txt`, "helm.txt", false}, - {`!helm.txt`, "tiller.txt", true}, - {`!*.txt`, "cargo", true}, - {`!cargo/`, "mast/", true}, - - // Absolute path tests - {`/a.txt`, "a.txt", true}, - {`/a.txt`, "cargo/a.txt", false}, - {`/cargo/a.txt`, "cargo/a.txt", true}, - } - - for _, test := range tests { - r, err := parseString(test.pattern) - if err != nil { - t.Fatalf("Failed to parse: %s", err) - } - fi, err := os.Stat(filepath.Join(testdata, test.name)) - if err != nil { - t.Fatalf("Fixture missing: %s", err) - } - - if r.Ignore(test.name, fi) != test.expect { - t.Errorf("Expected %q to be %v for pattern %q", test.name, test.expect, test.pattern) - } - } -} - -func TestAddDefaults(t *testing.T) { - r := Rules{} - r.AddDefaults() - - if len(r.patterns) != 1 { - t.Errorf("Expected 1 default patterns, got %d", len(r.patterns)) - } -} - -func parseString(str string) (*Rules, error) { - b := bytes.NewBuffer([]byte(str)) - return Parse(b) -} diff --git a/internal/ignore/testdata/.helmignore b/internal/ignore/testdata/.helmignore deleted file mode 100644 index b2693bae7..000000000 --- a/internal/ignore/testdata/.helmignore +++ /dev/null @@ -1,3 +0,0 @@ -mast/a.txt -.DS_Store -.git diff --git a/internal/ignore/testdata/.joonix b/internal/ignore/testdata/.joonix deleted file mode 100644 index e69de29bb..000000000 diff --git a/internal/ignore/testdata/a.txt b/internal/ignore/testdata/a.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/internal/ignore/testdata/cargo/a.txt b/internal/ignore/testdata/cargo/a.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/internal/ignore/testdata/cargo/b.txt b/internal/ignore/testdata/cargo/b.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/internal/ignore/testdata/cargo/c.txt b/internal/ignore/testdata/cargo/c.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/internal/ignore/testdata/helm.txt b/internal/ignore/testdata/helm.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/internal/ignore/testdata/mast/a.txt b/internal/ignore/testdata/mast/a.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/internal/ignore/testdata/mast/b.txt b/internal/ignore/testdata/mast/b.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/internal/ignore/testdata/mast/c.txt b/internal/ignore/testdata/mast/c.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/internal/ignore/testdata/rudder.txt b/internal/ignore/testdata/rudder.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/internal/ignore/testdata/templates/.dotfile b/internal/ignore/testdata/templates/.dotfile deleted file mode 100644 index e69de29bb..000000000 diff --git a/internal/ignore/testdata/tiller.txt b/internal/ignore/testdata/tiller.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/internal/monocular/client.go b/internal/monocular/client.go deleted file mode 100644 index 88a2564b9..000000000 --- a/internal/monocular/client.go +++ /dev/null @@ -1,68 +0,0 @@ -/* -Copyright The Helm 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 monocular - -import ( - "errors" - "net/url" -) - -// ErrHostnameNotProvided indicates the url is missing a hostname -var ErrHostnameNotProvided = errors.New("no hostname provided") - -// Client represents a client capable of communicating with the Monocular API. -type Client struct { - - // The base URL for requests - BaseURL string - - // The internal logger to use - Log func(string, ...interface{}) -} - -// New creates a new client -func New(u string) (*Client, error) { - - // Validate we have a URL - if err := validate(u); err != nil { - return nil, err - } - - return &Client{ - BaseURL: u, - Log: nopLogger, - }, nil -} - -var nopLogger = func(_ string, _ ...interface{}) {} - -// Validate if the base URL for monocular is valid. -func validate(u string) error { - - // Check if it is parsable - p, err := url.Parse(u) - if err != nil { - return err - } - - // Check that a host is attached - if p.Hostname() == "" { - return ErrHostnameNotProvided - } - - return nil -} diff --git a/internal/monocular/client_test.go b/internal/monocular/client_test.go deleted file mode 100644 index abf914ef5..000000000 --- a/internal/monocular/client_test.go +++ /dev/null @@ -1,31 +0,0 @@ -/* -Copyright The Helm 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 monocular - -import ( - "testing" -) - -func TestNew(t *testing.T) { - c, err := New("https://hub.helm.sh") - if err != nil { - t.Errorf("error creating client: %s", err) - } - if c.BaseURL != "https://hub.helm.sh" { - t.Errorf("incorrect BaseURL. Expected \"https://hub.helm.sh\" but got %q", c.BaseURL) - } -} diff --git a/internal/monocular/doc.go b/internal/monocular/doc.go deleted file mode 100644 index 5d402d35f..000000000 --- a/internal/monocular/doc.go +++ /dev/null @@ -1,22 +0,0 @@ -/* -Copyright The Helm 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 monocular contains the logic for interacting with a Monocular -// compatible search API endpoint. For example, as implemented by the Artifact -// Hub. -// -// This is a library for interacting with a monocular compatible search API -package monocular diff --git a/internal/monocular/search.go b/internal/monocular/search.go deleted file mode 100644 index 3082ff361..000000000 --- a/internal/monocular/search.go +++ /dev/null @@ -1,145 +0,0 @@ -/* -Copyright The Helm 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 monocular - -import ( - "encoding/json" - "fmt" - "net/http" - "net/url" - "path" - "time" - - "helm.sh/helm/v3/internal/version" - "helm.sh/helm/v3/pkg/chart" -) - -// SearchPath is the url path to the search API in monocular. -const SearchPath = "api/chartsvc/v1/charts/search" - -// The structs below represent the structure of the response from the monocular -// search API. The structs were not imported from monocular because monocular -// imports from Helm v2 (avoiding circular version dependency) and the mappings -// are slightly different (monocular search results do not directly reflect -// the struct definitions). - -// SearchResult represents an individual chart result -type SearchResult struct { - ID string `json:"id"` - ArtifactHub ArtifactHub `json:"artifactHub"` - Type string `json:"type"` - Attributes Chart `json:"attributes"` - Links Links `json:"links"` - Relationships Relationships `json:"relationships"` -} - -// ArtifactHub represents data specific to Artifact Hub instances -type ArtifactHub struct { - PackageURL string `json:"packageUrl"` -} - -// Chart is the attributes for the chart -type Chart struct { - Name string `json:"name"` - Repo Repo `json:"repo"` - Description string `json:"description"` - Home string `json:"home"` - Keywords []string `json:"keywords"` - Maintainers []chart.Maintainer `json:"maintainers"` - Sources []string `json:"sources"` - Icon string `json:"icon"` -} - -// Repo contains the name in monocular the url for the repository -type Repo struct { - Name string `json:"name"` - URL string `json:"url"` -} - -// Links provides a set of links relative to the chartsvc base -type Links struct { - Self string `json:"self"` -} - -// Relationships provides information on the latest version of the chart -type Relationships struct { - LatestChartVersion LatestChartVersion `json:"latestChartVersion"` -} - -// LatestChartVersion provides the details on the latest version of the chart -type LatestChartVersion struct { - Data ChartVersion `json:"data"` - Links Links `json:"links"` -} - -// ChartVersion provides the specific data on the chart version -type ChartVersion struct { - Version string `json:"version"` - AppVersion string `json:"app_version"` - Created time.Time `json:"created"` - Digest string `json:"digest"` - Urls []string `json:"urls"` - Readme string `json:"readme"` - Values string `json:"values"` -} - -// Search performs a search against the monocular search API -func (c *Client) Search(term string) ([]SearchResult, error) { - - // Create the URL to the search endpoint - // Note, this is currently an internal API for the Hub. This should be - // formatted without showing how monocular operates. - p, err := url.Parse(c.BaseURL) - if err != nil { - return nil, err - } - - // Set the path to the monocular API endpoint for search - p.Path = path.Join(p.Path, SearchPath) - - p.RawQuery = "q=" + url.QueryEscape(term) - - // Create request - req, err := http.NewRequest("GET", p.String(), nil) - if err != nil { - return nil, err - } - - // Set the user agent so that monocular can identify where the request - // is coming from - req.Header.Set("User-Agent", version.GetUserAgent()) - - res, err := http.DefaultClient.Do(req) - if err != nil { - return nil, err - } - defer res.Body.Close() - - if res.StatusCode != 200 { - return nil, fmt.Errorf("failed to fetch %s : %s", p.String(), res.Status) - } - - result := &searchResponse{} - - json.NewDecoder(res.Body).Decode(result) - - return result.Data, nil -} - -type searchResponse struct { - Data []SearchResult `json:"data"` -} diff --git a/internal/monocular/search_test.go b/internal/monocular/search_test.go deleted file mode 100644 index 9f6954af7..000000000 --- a/internal/monocular/search_test.go +++ /dev/null @@ -1,49 +0,0 @@ -/* -Copyright The Helm 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 monocular - -import ( - "fmt" - "net/http" - "net/http/httptest" - "testing" -) - -// A search response for phpmyadmin containing 2 results -var searchResult = `{"data":[{"id":"stable/phpmyadmin","type":"chart","attributes":{"name":"phpmyadmin","repo":{"name":"stable","url":"https://charts.helm.sh/stable"},"description":"phpMyAdmin is an mysql administration frontend","home":"https://www.phpmyadmin.net/","keywords":["mariadb","mysql","phpmyadmin"],"maintainers":[{"name":"Bitnami","email":"containers@bitnami.com"}],"sources":["https://github.com/bitnami/bitnami-docker-phpmyadmin"],"icon":""},"links":{"self":"/v1/charts/stable/phpmyadmin"},"relationships":{"latestChartVersion":{"data":{"version":"3.0.0","app_version":"4.9.0-1","created":"2019-08-08T17:57:31.38Z","digest":"119c499251bffd4b06ff0cd5ac98c2ce32231f84899fb4825be6c2d90971c742","urls":["https://charts.helm.sh/stable/phpmyadmin-3.0.0.tgz"],"readme":"/v1/assets/stable/phpmyadmin/versions/3.0.0/README.md","values":"/v1/assets/stable/phpmyadmin/versions/3.0.0/values.yaml"},"links":{"self":"/v1/charts/stable/phpmyadmin/versions/3.0.0"}}}},{"id":"bitnami/phpmyadmin","type":"chart","attributes":{"name":"phpmyadmin","repo":{"name":"bitnami","url":"https://charts.bitnami.com"},"description":"phpMyAdmin is an mysql administration frontend","home":"https://www.phpmyadmin.net/","keywords":["mariadb","mysql","phpmyadmin"],"maintainers":[{"name":"Bitnami","email":"containers@bitnami.com"}],"sources":["https://github.com/bitnami/bitnami-docker-phpmyadmin"],"icon":""},"links":{"self":"/v1/charts/bitnami/phpmyadmin"},"relationships":{"latestChartVersion":{"data":{"version":"3.0.0","app_version":"4.9.0-1","created":"2019-08-08T18:34:13.341Z","digest":"66d77cf6d8c2b52c488d0a294cd4996bd5bad8dc41d3829c394498fb401c008a","urls":["https://charts.bitnami.com/bitnami/phpmyadmin-3.0.0.tgz"],"readme":"/v1/assets/bitnami/phpmyadmin/versions/3.0.0/README.md","values":"/v1/assets/bitnami/phpmyadmin/versions/3.0.0/values.yaml"},"links":{"self":"/v1/charts/bitnami/phpmyadmin/versions/3.0.0"}}}}]}` - -func TestSearch(t *testing.T) { - - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintln(w, searchResult) - })) - defer ts.Close() - - c, err := New(ts.URL) - if err != nil { - t.Errorf("unable to create monocular client: %s", err) - } - - results, err := c.Search("phpmyadmin") - if err != nil { - t.Errorf("unable to search monocular: %s", err) - } - - if len(results) != 2 { - t.Error("Did not receive the expected number of results") - } -} diff --git a/internal/resolver/resolver.go b/internal/resolver/resolver.go deleted file mode 100644 index 5e8921f96..000000000 --- a/internal/resolver/resolver.go +++ /dev/null @@ -1,263 +0,0 @@ -/* -Copyright The Helm 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 resolver - -import ( - "bytes" - "encoding/json" - "fmt" - "os" - "path/filepath" - "strings" - "time" - - "github.com/Masterminds/semver/v3" - "github.com/pkg/errors" - - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chart/loader" - "helm.sh/helm/v3/pkg/helmpath" - "helm.sh/helm/v3/pkg/provenance" - "helm.sh/helm/v3/pkg/registry" - "helm.sh/helm/v3/pkg/repo" -) - -// Resolver resolves dependencies from semantic version ranges to a particular version. -type Resolver struct { - chartpath string - cachepath string - registryClient *registry.Client -} - -// New creates a new resolver for a given chart, helm home and registry client. -func New(chartpath, cachepath string, registryClient *registry.Client) *Resolver { - return &Resolver{ - chartpath: chartpath, - cachepath: cachepath, - registryClient: registryClient, - } -} - -// Resolve resolves dependencies and returns a lock file with the resolution. -func (r *Resolver) Resolve(reqs []*chart.Dependency, repoNames map[string]string) (*chart.Lock, error) { - - // Now we clone the dependencies, locking as we go. - locked := make([]*chart.Dependency, len(reqs)) - missing := []string{} - for i, d := range reqs { - constraint, err := semver.NewConstraint(d.Version) - if err != nil { - return nil, errors.Wrapf(err, "dependency %q has an invalid version/constraint format", d.Name) - } - - if d.Repository == "" { - // Local chart subfolder - if _, err := GetLocalPath(filepath.Join("charts", d.Name), r.chartpath); err != nil { - return nil, err - } - - locked[i] = &chart.Dependency{ - Name: d.Name, - Repository: "", - Version: d.Version, - } - continue - } - if strings.HasPrefix(d.Repository, "file://") { - - chartpath, err := GetLocalPath(d.Repository, r.chartpath) - if err != nil { - return nil, err - } - - ch, err := loader.LoadDir(chartpath) - if err != nil { - return nil, err - } - - v, err := semver.NewVersion(ch.Metadata.Version) - if err != nil { - // Not a legit entry. - continue - } - - if !constraint.Check(v) { - missing = append(missing, d.Name) - continue - } - - locked[i] = &chart.Dependency{ - Name: d.Name, - Repository: d.Repository, - Version: ch.Metadata.Version, - } - continue - } - - repoName := repoNames[d.Name] - // if the repository was not defined, but the dependency defines a repository url, bypass the cache - if repoName == "" && d.Repository != "" { - locked[i] = &chart.Dependency{ - Name: d.Name, - Repository: d.Repository, - Version: d.Version, - } - continue - } - - var vs repo.ChartVersions - var version string - var ok bool - found := true - if !registry.IsOCI(d.Repository) { - repoIndex, err := repo.LoadIndexFile(filepath.Join(r.cachepath, helmpath.CacheIndexFile(repoName))) - if err != nil { - return nil, errors.Wrapf(err, "no cached repository for %s found. (try 'helm repo update')", repoName) - } - - vs, ok = repoIndex.Entries[d.Name] - if !ok { - return nil, errors.Errorf("%s chart not found in repo %s", d.Name, d.Repository) - } - found = false - } else { - version = d.Version - - // Check to see if an explicit version has been provided - _, err := semver.NewVersion(version) - - // Use an explicit version, otherwise search for tags - if err == nil { - vs = []*repo.ChartVersion{{ - Metadata: &chart.Metadata{ - Version: version, - }, - }} - - } else { - // Retrieve list of tags for repository - ref := fmt.Sprintf("%s/%s", strings.TrimPrefix(d.Repository, fmt.Sprintf("%s://", registry.OCIScheme)), d.Name) - tags, err := r.registryClient.Tags(ref) - if err != nil { - return nil, errors.Wrapf(err, "could not retrieve list of tags for repository %s", d.Repository) - } - - vs = make(repo.ChartVersions, len(tags)) - for ti, t := range tags { - // Mock chart version objects - version := &repo.ChartVersion{ - Metadata: &chart.Metadata{ - Version: t, - }, - } - vs[ti] = version - } - } - } - - locked[i] = &chart.Dependency{ - Name: d.Name, - Repository: d.Repository, - Version: version, - } - // The version are already sorted and hence the first one to satisfy the constraint is used - for _, ver := range vs { - v, err := semver.NewVersion(ver.Version) - // OCI does not need URLs - if err != nil || (!registry.IsOCI(d.Repository) && len(ver.URLs) == 0) { - // Not a legit entry. - continue - } - if constraint.Check(v) { - found = true - locked[i].Version = v.Original() - break - } - } - - if !found { - missing = append(missing, d.Name) - } - } - if len(missing) > 0 { - return nil, errors.Errorf("can't get a valid version for repositories %s. Try changing the version constraint in Chart.yaml", strings.Join(missing, ", ")) - } - - digest, err := HashReq(reqs, locked) - if err != nil { - return nil, err - } - - return &chart.Lock{ - Generated: time.Now(), - Digest: digest, - Dependencies: locked, - }, nil -} - -// HashReq generates a hash of the dependencies. -// -// This should be used only to compare against another hash generated by this -// function. -func HashReq(req, lock []*chart.Dependency) (string, error) { - data, err := json.Marshal([2][]*chart.Dependency{req, lock}) - if err != nil { - return "", err - } - s, err := provenance.Digest(bytes.NewBuffer(data)) - return "sha256:" + s, err -} - -// HashV2Req generates a hash of requirements generated in Helm v2. -// -// This should be used only to compare against another hash generated by the -// Helm v2 hash function. It is to handle issue: -// https://github.com/helm/helm/issues/7233 -func HashV2Req(req []*chart.Dependency) (string, error) { - dep := make(map[string][]*chart.Dependency) - dep["dependencies"] = req - data, err := json.Marshal(dep) - if err != nil { - return "", err - } - s, err := provenance.Digest(bytes.NewBuffer(data)) - return "sha256:" + s, err -} - -// GetLocalPath generates absolute local path when use -// "file://" in repository of dependencies -func GetLocalPath(repo, chartpath string) (string, error) { - var depPath string - var err error - p := strings.TrimPrefix(repo, "file://") - - // root path is absolute - if strings.HasPrefix(p, "/") { - if depPath, err = filepath.Abs(p); err != nil { - return "", err - } - } else { - depPath = filepath.Join(chartpath, p) - } - - if _, err = os.Stat(depPath); os.IsNotExist(err) { - return "", errors.Errorf("directory %s not found", depPath) - } else if err != nil { - return "", err - } - - return depPath, nil -} diff --git a/internal/resolver/resolver_test.go b/internal/resolver/resolver_test.go deleted file mode 100644 index a79852175..000000000 --- a/internal/resolver/resolver_test.go +++ /dev/null @@ -1,310 +0,0 @@ -/* -Copyright The Helm 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 resolver - -import ( - "runtime" - "testing" - - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/registry" -) - -func TestResolve(t *testing.T) { - tests := []struct { - name string - req []*chart.Dependency - expect *chart.Lock - err bool - }{ - { - name: "repo from invalid version", - req: []*chart.Dependency{ - {Name: "base", Repository: "file://base", Version: "1.1.0"}, - }, - expect: &chart.Lock{ - Dependencies: []*chart.Dependency{ - {Name: "base", Repository: "file://base", Version: "0.1.0"}, - }, - }, - err: true, - }, - { - name: "version failure", - req: []*chart.Dependency{ - {Name: "oedipus-rex", Repository: "http://example.com", Version: ">a1"}, - }, - err: true, - }, - { - name: "cache index failure", - req: []*chart.Dependency{ - {Name: "oedipus-rex", Repository: "http://example.com", Version: "1.0.0"}, - }, - expect: &chart.Lock{ - Dependencies: []*chart.Dependency{ - {Name: "oedipus-rex", Repository: "http://example.com", Version: "1.0.0"}, - }, - }, - }, - { - name: "chart not found failure", - req: []*chart.Dependency{ - {Name: "redis", Repository: "http://example.com", Version: "1.0.0"}, - }, - err: true, - }, - { - name: "constraint not satisfied failure", - req: []*chart.Dependency{ - {Name: "alpine", Repository: "http://example.com", Version: ">=1.0.0"}, - }, - err: true, - }, - { - name: "valid lock", - req: []*chart.Dependency{ - {Name: "alpine", Repository: "http://example.com", Version: ">=0.1.0"}, - }, - expect: &chart.Lock{ - Dependencies: []*chart.Dependency{ - {Name: "alpine", Repository: "http://example.com", Version: "0.2.0"}, - }, - }, - }, - { - name: "repo from valid local path", - req: []*chart.Dependency{ - {Name: "base", Repository: "file://base", Version: "0.1.0"}, - }, - expect: &chart.Lock{ - Dependencies: []*chart.Dependency{ - {Name: "base", Repository: "file://base", Version: "0.1.0"}, - }, - }, - }, - { - name: "repo from valid local path with range resolution", - req: []*chart.Dependency{ - {Name: "base", Repository: "file://base", Version: "^0.1.0"}, - }, - expect: &chart.Lock{ - Dependencies: []*chart.Dependency{ - {Name: "base", Repository: "file://base", Version: "0.1.0"}, - }, - }, - }, - { - name: "repo from invalid local path", - req: []*chart.Dependency{ - {Name: "nonexistent", Repository: "file://testdata/nonexistent", Version: "0.1.0"}, - }, - err: true, - }, - { - name: "repo from valid path under charts path", - req: []*chart.Dependency{ - {Name: "localdependency", Repository: "", Version: "0.1.0"}, - }, - expect: &chart.Lock{ - Dependencies: []*chart.Dependency{ - {Name: "localdependency", Repository: "", Version: "0.1.0"}, - }, - }, - }, - { - name: "repo from invalid path under charts path", - req: []*chart.Dependency{ - {Name: "nonexistentdependency", Repository: "", Version: "0.1.0"}, - }, - expect: &chart.Lock{ - Dependencies: []*chart.Dependency{ - {Name: "nonexistentlocaldependency", Repository: "", Version: "0.1.0"}, - }, - }, - err: true, - }, - } - - repoNames := map[string]string{"alpine": "kubernetes-charts", "redis": "kubernetes-charts"} - registryClient, _ := registry.NewClient() - r := New("testdata/chartpath", "testdata/repository", registryClient) - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - l, err := r.Resolve(tt.req, repoNames) - if err != nil { - if tt.err { - return - } - t.Fatal(err) - } - - if tt.err { - t.Fatalf("Expected error in test %q", tt.name) - } - - if h, err := HashReq(tt.req, tt.expect.Dependencies); err != nil { - t.Fatal(err) - } else if h != l.Digest { - t.Errorf("%q: hashes don't match.", tt.name) - } - - // Check fields. - if len(l.Dependencies) != len(tt.req) { - t.Errorf("%s: wrong number of dependencies in lock", tt.name) - } - d0 := l.Dependencies[0] - e0 := tt.expect.Dependencies[0] - if d0.Name != e0.Name { - t.Errorf("%s: expected name %s, got %s", tt.name, e0.Name, d0.Name) - } - if d0.Repository != e0.Repository { - t.Errorf("%s: expected repo %s, got %s", tt.name, e0.Repository, d0.Repository) - } - if d0.Version != e0.Version { - t.Errorf("%s: expected version %s, got %s", tt.name, e0.Version, d0.Version) - } - }) - } -} - -func TestHashReq(t *testing.T) { - expect := "sha256:fb239e836325c5fa14b29d1540a13b7d3ba13151b67fe719f820e0ef6d66aaaf" - - tests := []struct { - name string - chartVersion string - lockVersion string - wantError bool - }{ - { - name: "chart with the expected digest", - chartVersion: "0.1.0", - lockVersion: "0.1.0", - wantError: false, - }, - { - name: "ranged version but same resolved lock version", - chartVersion: "^0.1.0", - lockVersion: "0.1.0", - wantError: true, - }, - { - name: "ranged version resolved as higher version", - chartVersion: "^0.1.0", - lockVersion: "0.1.2", - wantError: true, - }, - { - name: "different version", - chartVersion: "0.1.2", - lockVersion: "0.1.2", - wantError: true, - }, - { - name: "different version with a range", - chartVersion: "^0.1.2", - lockVersion: "0.1.2", - wantError: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - req := []*chart.Dependency{ - {Name: "alpine", Version: tt.chartVersion, Repository: "http://localhost:8879/charts"}, - } - lock := []*chart.Dependency{ - {Name: "alpine", Version: tt.lockVersion, Repository: "http://localhost:8879/charts"}, - } - h, err := HashReq(req, lock) - if err != nil { - t.Fatal(err) - } - if !tt.wantError && expect != h { - t.Errorf("Expected %q, got %q", expect, h) - } else if tt.wantError && expect == h { - t.Errorf("Expected not %q, but same", expect) - } - }) - } -} - -func TestGetLocalPath(t *testing.T) { - tests := []struct { - name string - repo string - chartpath string - expect string - winExpect string - err bool - }{ - { - name: "absolute path", - repo: "file:////", - expect: "/", - winExpect: "\\", - }, - { - name: "relative path", - repo: "file://../../testdata/chartpath/base", - chartpath: "foo/bar", - expect: "testdata/chartpath/base", - winExpect: "testdata\\chartpath\\base", - }, - { - name: "current directory path", - repo: "../charts/localdependency", - chartpath: "testdata/chartpath/charts", - expect: "testdata/chartpath/charts/localdependency", - winExpect: "testdata\\chartpath\\charts\\localdependency", - }, - { - name: "invalid local path", - repo: "file://testdata/nonexistent", - chartpath: "testdata/chartpath", - err: true, - }, - { - name: "invalid path under current directory", - repo: "charts/nonexistentdependency", - chartpath: "testdata/chartpath/charts", - err: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - p, err := GetLocalPath(tt.repo, tt.chartpath) - if err != nil { - if tt.err { - return - } - t.Fatal(err) - } - if tt.err { - t.Fatalf("Expected error in test %q", tt.name) - } - expect := tt.expect - if runtime.GOOS == "windows" { - expect = tt.winExpect - } - if p != expect { - t.Errorf("%q: expected %q, got %q", tt.name, expect, p) - } - }) - } -} diff --git a/internal/resolver/testdata/chartpath/base/Chart.yaml b/internal/resolver/testdata/chartpath/base/Chart.yaml deleted file mode 100644 index 860b09091..000000000 --- a/internal/resolver/testdata/chartpath/base/Chart.yaml +++ /dev/null @@ -1,3 +0,0 @@ -apiVersion: v2 -name: base -version: 0.1.0 diff --git a/internal/resolver/testdata/chartpath/charts/localdependency/Chart.yaml b/internal/resolver/testdata/chartpath/charts/localdependency/Chart.yaml deleted file mode 100644 index 083c51ee5..000000000 --- a/internal/resolver/testdata/chartpath/charts/localdependency/Chart.yaml +++ /dev/null @@ -1,3 +0,0 @@ -description: A Helm chart for Kubernetes -name: localdependency -version: 0.1.0 diff --git a/internal/resolver/testdata/repository/kubernetes-charts-index.yaml b/internal/resolver/testdata/repository/kubernetes-charts-index.yaml deleted file mode 100644 index c6b7962a1..000000000 --- a/internal/resolver/testdata/repository/kubernetes-charts-index.yaml +++ /dev/null @@ -1,49 +0,0 @@ -apiVersion: v1 -entries: - alpine: - - name: alpine - urls: - - https://charts.helm.sh/stable/alpine-0.1.0.tgz - checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d - home: https://helm.sh/helm - sources: - - https://github.com/helm/helm - version: 0.2.0 - description: Deploy a basic Alpine Linux pod - keywords: [] - maintainers: [] - icon: "" - apiVersion: v2 - - name: alpine - urls: - - https://charts.helm.sh/stable/alpine-0.2.0.tgz - checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d - home: https://helm.sh/helm - sources: - - https://github.com/helm/helm - version: 0.1.0 - description: Deploy a basic Alpine Linux pod - keywords: [] - maintainers: [] - icon: "" - apiVersion: v2 - mariadb: - - name: mariadb - urls: - - https://charts.helm.sh/stable/mariadb-0.3.0.tgz - checksum: 65229f6de44a2be9f215d11dbff311673fc8ba56 - home: https://mariadb.org - sources: - - https://github.com/bitnami/bitnami-docker-mariadb - version: 0.3.0 - description: Chart for MariaDB - keywords: - - mariadb - - mysql - - database - - sql - maintainers: - - name: Bitnami - email: containers@bitnami.com - icon: "" - apiVersion: v2 diff --git a/internal/sympath/walk.go b/internal/sympath/walk.go deleted file mode 100644 index a276cfeff..000000000 --- a/internal/sympath/walk.go +++ /dev/null @@ -1,119 +0,0 @@ -/* -Copyright (c) for portions of walk.go are held by The Go Authors, 2009 and are -provided under the BSD license. - -https://github.com/golang/go/blob/master/LICENSE - -Copyright The Helm 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 sympath - -import ( - "log" - "os" - "path/filepath" - "sort" - - "github.com/pkg/errors" -) - -// Walk walks the file tree rooted at root, calling walkFn for each file or directory -// in the tree, including root. All errors that arise visiting files and directories -// are filtered by walkFn. The files are walked in lexical order, which makes the -// output deterministic but means that for very large directories Walk can be -// inefficient. Walk follows symbolic links. -func Walk(root string, walkFn filepath.WalkFunc) error { - info, err := os.Lstat(root) - if err != nil { - err = walkFn(root, nil, err) - } else { - err = symwalk(root, info, walkFn) - } - if err == filepath.SkipDir { - return nil - } - return err -} - -// readDirNames reads the directory named by dirname and returns -// a sorted list of directory entries. -func readDirNames(dirname string) ([]string, error) { - f, err := os.Open(dirname) - if err != nil { - return nil, err - } - names, err := f.Readdirnames(-1) - f.Close() - if err != nil { - return nil, err - } - sort.Strings(names) - return names, nil -} - -// symwalk recursively descends path, calling walkFn. -func symwalk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error { - // Recursively walk symlinked directories. - if IsSymlink(info) { - resolved, err := filepath.EvalSymlinks(path) - if err != nil { - return errors.Wrapf(err, "error evaluating symlink %s", path) - } - log.Printf("found symbolic link in path: %s resolves to %s. Contents of linked file included and used", path, resolved) - if info, err = os.Lstat(resolved); err != nil { - return err - } - if err := symwalk(path, info, walkFn); err != nil && err != filepath.SkipDir { - return err - } - return nil - } - - if err := walkFn(path, info, nil); err != nil { - return err - } - - if !info.IsDir() { - return nil - } - - names, err := readDirNames(path) - if err != nil { - return walkFn(path, info, err) - } - - for _, name := range names { - filename := filepath.Join(path, name) - fileInfo, err := os.Lstat(filename) - if err != nil { - if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir { - return err - } - } else { - err = symwalk(filename, fileInfo, walkFn) - if err != nil { - if (!fileInfo.IsDir() && !IsSymlink(fileInfo)) || err != filepath.SkipDir { - return err - } - } - } - } - return nil -} - -// IsSymlink is used to determine if the fileinfo is a symbolic link. -func IsSymlink(fi os.FileInfo) bool { - return fi.Mode()&os.ModeSymlink != 0 -} diff --git a/internal/sympath/walk_test.go b/internal/sympath/walk_test.go deleted file mode 100644 index 25f737134..000000000 --- a/internal/sympath/walk_test.go +++ /dev/null @@ -1,151 +0,0 @@ -/* -Copyright (c) for portions of walk_test.go are held by The Go Authors, 2009 and are -provided under the BSD license. - -https://github.com/golang/go/blob/master/LICENSE - -Copyright The Helm 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 sympath - -import ( - "os" - "path/filepath" - "testing" -) - -type Node struct { - name string - entries []*Node // nil if the entry is a file - marks int - expectedMarks int - symLinkedTo string -} - -var tree = &Node{ - "testdata", - []*Node{ - {"a", nil, 0, 1, ""}, - {"b", []*Node{}, 0, 1, ""}, - {"c", nil, 0, 2, ""}, - {"d", nil, 0, 0, "c"}, - { - "e", - []*Node{ - {"x", nil, 0, 1, ""}, - {"y", []*Node{}, 0, 1, ""}, - { - "z", - []*Node{ - {"u", nil, 0, 1, ""}, - {"v", nil, 0, 1, ""}, - {"w", nil, 0, 1, ""}, - }, - 0, - 1, - "", - }, - }, - 0, - 1, - "", - }, - }, - 0, - 1, - "", -} - -func walkTree(n *Node, path string, f func(path string, n *Node)) { - f(path, n) - for _, e := range n.entries { - walkTree(e, filepath.Join(path, e.name), f) - } -} - -func makeTree(t *testing.T) { - walkTree(tree, tree.name, func(path string, n *Node) { - if n.entries == nil { - if n.symLinkedTo != "" { - if err := os.Symlink(n.symLinkedTo, path); err != nil { - t.Fatalf("makeTree: %v", err) - } - } else { - fd, err := os.Create(path) - if err != nil { - t.Fatalf("makeTree: %v", err) - return - } - fd.Close() - } - } else { - if err := os.Mkdir(path, 0770); err != nil { - t.Fatalf("makeTree: %v", err) - } - } - }) -} - -func checkMarks(t *testing.T, report bool) { - walkTree(tree, tree.name, func(path string, n *Node) { - if n.marks != n.expectedMarks && report { - t.Errorf("node %s mark = %d; expected %d", path, n.marks, n.expectedMarks) - } - n.marks = 0 - }) -} - -// Assumes that each node name is unique. Good enough for a test. -// If clear is true, any incoming error is cleared before return. The errors -// are always accumulated, though. -func mark(info os.FileInfo, err error, errors *[]error, clear bool) error { - if err != nil { - *errors = append(*errors, err) - if clear { - return nil - } - return err - } - name := info.Name() - walkTree(tree, tree.name, func(path string, n *Node) { - if n.name == name { - n.marks++ - } - }) - return nil -} - -func TestWalk(t *testing.T) { - makeTree(t) - errors := make([]error, 0, 10) - clear := true - markFn := func(path string, info os.FileInfo, err error) error { - return mark(info, err, &errors, clear) - } - // Expect no errors. - err := Walk(tree.name, markFn) - if err != nil { - t.Fatalf("no error expected, found: %s", err) - } - if len(errors) != 0 { - t.Fatalf("unexpected errors: %s", errors) - } - checkMarks(t, true) - - // cleanup - if err := os.RemoveAll(tree.name); err != nil { - t.Errorf("removeTree: %v", err) - } -} diff --git a/internal/test/ensure/ensure.go b/internal/test/ensure/ensure.go deleted file mode 100644 index 3c0e4575c..000000000 --- a/internal/test/ensure/ensure.go +++ /dev/null @@ -1,70 +0,0 @@ -/* -Copyright The Helm 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 ensure - -import ( - "io/ioutil" - "os" - "path/filepath" - "testing" - - "helm.sh/helm/v3/pkg/helmpath" - "helm.sh/helm/v3/pkg/helmpath/xdg" -) - -// HelmHome sets up a Helm Home in a temp dir. -func HelmHome(t *testing.T) func() { - t.Helper() - base := TempDir(t) - os.Setenv(xdg.CacheHomeEnvVar, base) - os.Setenv(xdg.ConfigHomeEnvVar, base) - os.Setenv(xdg.DataHomeEnvVar, base) - os.Setenv(helmpath.CacheHomeEnvVar, "") - os.Setenv(helmpath.ConfigHomeEnvVar, "") - os.Setenv(helmpath.DataHomeEnvVar, "") - return func() { - os.RemoveAll(base) - } -} - -// TempDir ensures a scratch test directory for unit testing purposes. -func TempDir(t *testing.T) string { - t.Helper() - d, err := ioutil.TempDir("", "helm") - if err != nil { - t.Fatal(err) - } - return d -} - -// TempFile ensures a temp file for unit testing purposes. -// -// It returns the path to the directory (to which you will still need to join the filename) -// -// You must clean up the directory that is returned. -// -// tempdir := TempFile(t, "foo", []byte("bar")) -// defer os.RemoveAll(tempdir) -// filename := filepath.Join(tempdir, "foo") -func TempFile(t *testing.T, name string, data []byte) string { - path := TempDir(t) - filename := filepath.Join(path, name) - if err := ioutil.WriteFile(filename, data, 0755); err != nil { - t.Fatal(err) - } - return path -} diff --git a/internal/test/test.go b/internal/test/test.go deleted file mode 100644 index 4d5862c70..000000000 --- a/internal/test/test.go +++ /dev/null @@ -1,96 +0,0 @@ -/* -Copyright The Helm 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 test - -import ( - "bytes" - "flag" - "io/ioutil" - "path/filepath" - - "github.com/pkg/errors" -) - -// UpdateGolden writes out the golden files with the latest values, rather than failing the test. -var updateGolden = flag.Bool("update", false, "update golden files") - -// TestingT describes a testing object compatible with the critical functions from the testing.T type -type TestingT interface { - Fatal(...interface{}) - Fatalf(string, ...interface{}) - HelperT -} - -// HelperT describes a test with a helper function -type HelperT interface { - Helper() -} - -// AssertGoldenString asserts that the given string matches the contents of the given file. -func AssertGoldenString(t TestingT, actual, filename string) { - t.Helper() - - if err := compare([]byte(actual), path(filename)); err != nil { - t.Fatalf("%v\n", err) - } -} - -// AssertGoldenFile asserts that the content of the actual file matches the contents of the expected file -func AssertGoldenFile(t TestingT, actualFileName string, expectedFilename string) { - t.Helper() - - actual, err := ioutil.ReadFile(actualFileName) - if err != nil { - t.Fatalf("%v", err) - } - AssertGoldenString(t, string(actual), expectedFilename) -} - -func path(filename string) string { - if filepath.IsAbs(filename) { - return filename - } - return filepath.Join("testdata", filename) -} - -func compare(actual []byte, filename string) error { - actual = normalize(actual) - if err := update(filename, actual); err != nil { - return err - } - - expected, err := ioutil.ReadFile(filename) - if err != nil { - return errors.Wrapf(err, "unable to read testdata %s", filename) - } - expected = normalize(expected) - if !bytes.Equal(expected, actual) { - return errors.Errorf("does not match golden file %s\n\nWANT:\n'%s'\n\nGOT:\n'%s'", filename, expected, actual) - } - return nil -} - -func update(filename string, in []byte) error { - if !*updateGolden { - return nil - } - return ioutil.WriteFile(filename, normalize(in), 0666) -} - -func normalize(in []byte) []byte { - return bytes.Replace(in, []byte("\r\n"), []byte("\n"), -1) -} diff --git a/internal/third_party/dep/fs/fs.go b/internal/third_party/dep/fs/fs.go deleted file mode 100644 index 4e4eacc60..000000000 --- a/internal/third_party/dep/fs/fs.go +++ /dev/null @@ -1,372 +0,0 @@ -/* -Copyright (c) for portions of fs.go are held by The Go Authors, 2016 and are provided under -the BSD license. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -package fs - -import ( - "io" - "os" - "path/filepath" - "runtime" - "syscall" - - "github.com/pkg/errors" -) - -// fs contains a copy of a few functions from dep tool code to avoid a dependency on golang/dep. -// This code is copied from https://github.com/golang/dep/blob/37d6c560cdf407be7b6cd035b23dba89df9275cf/internal/fs/fs.go -// No changes to the code were made other than removing some unused functions - -// RenameWithFallback attempts to rename a file or directory, but falls back to -// copying in the event of a cross-device link error. If the fallback copy -// succeeds, src is still removed, emulating normal rename behavior. -func RenameWithFallback(src, dst string) error { - _, err := os.Stat(src) - if err != nil { - return errors.Wrapf(err, "cannot stat %s", src) - } - - err = os.Rename(src, dst) - if err == nil { - return nil - } - - return renameFallback(err, src, dst) -} - -// renameByCopy attempts to rename a file or directory by copying it to the -// destination and then removing the src thus emulating the rename behavior. -func renameByCopy(src, dst string) error { - var cerr error - if dir, _ := IsDir(src); dir { - cerr = CopyDir(src, dst) - if cerr != nil { - cerr = errors.Wrap(cerr, "copying directory failed") - } - } else { - cerr = copyFile(src, dst) - if cerr != nil { - cerr = errors.Wrap(cerr, "copying file failed") - } - } - - if cerr != nil { - return errors.Wrapf(cerr, "rename fallback failed: cannot rename %s to %s", src, dst) - } - - return errors.Wrapf(os.RemoveAll(src), "cannot delete %s", src) -} - -var ( - errSrcNotDir = errors.New("source is not a directory") - errDstExist = errors.New("destination already exists") -) - -// CopyDir recursively copies a directory tree, attempting to preserve permissions. -// Source directory must exist, destination directory must *not* exist. -func CopyDir(src, dst string) error { - src = filepath.Clean(src) - dst = filepath.Clean(dst) - - // We use os.Lstat() here to ensure we don't fall in a loop where a symlink - // actually links to a one of its parent directories. - fi, err := os.Lstat(src) - if err != nil { - return err - } - if !fi.IsDir() { - return errSrcNotDir - } - - _, err = os.Stat(dst) - if err != nil && !os.IsNotExist(err) { - return err - } - if err == nil { - return errDstExist - } - - if err = os.MkdirAll(dst, fi.Mode()); err != nil { - return errors.Wrapf(err, "cannot mkdir %s", dst) - } - - entries, err := os.ReadDir(src) - if err != nil { - return errors.Wrapf(err, "cannot read directory %s", dst) - } - - for _, entry := range entries { - srcPath := filepath.Join(src, entry.Name()) - dstPath := filepath.Join(dst, entry.Name()) - - if entry.IsDir() { - if err = CopyDir(srcPath, dstPath); err != nil { - return errors.Wrap(err, "copying directory failed") - } - } else { - // This will include symlinks, which is what we want when - // copying things. - if err = copyFile(srcPath, dstPath); err != nil { - return errors.Wrap(err, "copying file failed") - } - } - } - - return nil -} - -// copyFile copies the contents of the file named src to the file named -// by dst. The file will be created if it does not already exist. If the -// destination file exists, all its contents will be replaced by the contents -// of the source file. The file mode will be copied from the source. -func copyFile(src, dst string) (err error) { - if sym, err := IsSymlink(src); err != nil { - return errors.Wrap(err, "symlink check failed") - } else if sym { - if err := cloneSymlink(src, dst); err != nil { - if runtime.GOOS == "windows" { - // If cloning the symlink fails on Windows because the user - // does not have the required privileges, ignore the error and - // fall back to copying the file contents. - // - // ERROR_PRIVILEGE_NOT_HELD is 1314 (0x522): - // https://msdn.microsoft.com/en-us/library/windows/desktop/ms681385(v=vs.85).aspx - if lerr, ok := err.(*os.LinkError); ok && lerr.Err != syscall.Errno(1314) { - return err - } - } else { - return err - } - } else { - return nil - } - } - - in, err := os.Open(src) - if err != nil { - return - } - defer in.Close() - - out, err := os.Create(dst) - if err != nil { - return - } - - if _, err = io.Copy(out, in); err != nil { - out.Close() - return - } - - // Check for write errors on Close - if err = out.Close(); err != nil { - return - } - - si, err := os.Stat(src) - if err != nil { - return - } - - // Temporary fix for Go < 1.9 - // - // See: https://github.com/golang/dep/issues/774 - // and https://github.com/golang/go/issues/20829 - if runtime.GOOS == "windows" { - dst = fixLongPath(dst) - } - err = os.Chmod(dst, si.Mode()) - - return -} - -// cloneSymlink will create a new symlink that points to the resolved path of sl. -// If sl is a relative symlink, dst will also be a relative symlink. -func cloneSymlink(sl, dst string) error { - resolved, err := os.Readlink(sl) - if err != nil { - return err - } - - return os.Symlink(resolved, dst) -} - -// IsDir determines is the path given is a directory or not. -func IsDir(name string) (bool, error) { - fi, err := os.Stat(name) - if err != nil { - return false, err - } - if !fi.IsDir() { - return false, errors.Errorf("%q is not a directory", name) - } - return true, nil -} - -// IsSymlink determines if the given path is a symbolic link. -func IsSymlink(path string) (bool, error) { - l, err := os.Lstat(path) - if err != nil { - return false, err - } - - return l.Mode()&os.ModeSymlink == os.ModeSymlink, nil -} - -// fixLongPath returns the extended-length (\\?\-prefixed) form of -// path when needed, in order to avoid the default 260 character file -// path limit imposed by Windows. If path is not easily converted to -// the extended-length form (for example, if path is a relative path -// or contains .. elements), or is short enough, fixLongPath returns -// path unmodified. -// -// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath -func fixLongPath(path string) string { - // Do nothing (and don't allocate) if the path is "short". - // Empirically (at least on the Windows Server 2013 builder), - // the kernel is arbitrarily okay with < 248 bytes. That - // matches what the docs above say: - // "When using an API to create a directory, the specified - // path cannot be so long that you cannot append an 8.3 file - // name (that is, the directory name cannot exceed MAX_PATH - // minus 12)." Since MAX_PATH is 260, 260 - 12 = 248. - // - // The MSDN docs appear to say that a normal path that is 248 bytes long - // will work; empirically the path must be less then 248 bytes long. - if len(path) < 248 { - // Don't fix. (This is how Go 1.7 and earlier worked, - // not automatically generating the \\?\ form) - return path - } - - // The extended form begins with \\?\, as in - // \\?\c:\windows\foo.txt or \\?\UNC\server\share\foo.txt. - // The extended form disables evaluation of . and .. path - // elements and disables the interpretation of / as equivalent - // to \. The conversion here rewrites / to \ and elides - // . elements as well as trailing or duplicate separators. For - // simplicity it avoids the conversion entirely for relative - // paths or paths containing .. elements. For now, - // \\server\share paths are not converted to - // \\?\UNC\server\share paths because the rules for doing so - // are less well-specified. - if len(path) >= 2 && path[:2] == `\\` { - // Don't canonicalize UNC paths. - return path - } - if !isAbs(path) { - // Relative path - return path - } - - const prefix = `\\?` - - pathbuf := make([]byte, len(prefix)+len(path)+len(`\`)) - copy(pathbuf, prefix) - n := len(path) - r, w := 0, len(prefix) - for r < n { - switch { - case os.IsPathSeparator(path[r]): - // empty block - r++ - case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])): - // /./ - r++ - case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])): - // /../ is currently unhandled - return path - default: - pathbuf[w] = '\\' - w++ - for ; r < n && !os.IsPathSeparator(path[r]); r++ { - pathbuf[w] = path[r] - w++ - } - } - } - // A drive's root directory needs a trailing \ - if w == len(`\\?\c:`) { - pathbuf[w] = '\\' - w++ - } - return string(pathbuf[:w]) -} - -func isAbs(path string) (b bool) { - v := volumeName(path) - if v == "" { - return false - } - path = path[len(v):] - if path == "" { - return false - } - return os.IsPathSeparator(path[0]) -} - -func volumeName(path string) (v string) { - if len(path) < 2 { - return "" - } - // with drive letter - c := path[0] - if path[1] == ':' && - ('0' <= c && c <= '9' || 'a' <= c && c <= 'z' || - 'A' <= c && c <= 'Z') { - return path[:2] - } - // is it UNC - if l := len(path); l >= 5 && os.IsPathSeparator(path[0]) && os.IsPathSeparator(path[1]) && - !os.IsPathSeparator(path[2]) && path[2] != '.' { - // first, leading `\\` and next shouldn't be `\`. its server name. - for n := 3; n < l-1; n++ { - // second, next '\' shouldn't be repeated. - if os.IsPathSeparator(path[n]) { - n++ - // third, following something characters. its share name. - if !os.IsPathSeparator(path[n]) { - if path[n] == '.' { - break - } - for ; n < l; n++ { - if os.IsPathSeparator(path[n]) { - break - } - } - return path[:n] - } - break - } - } - } - return "" -} diff --git a/internal/third_party/dep/fs/fs_test.go b/internal/third_party/dep/fs/fs_test.go deleted file mode 100644 index c9e6ce65b..000000000 --- a/internal/third_party/dep/fs/fs_test.go +++ /dev/null @@ -1,643 +0,0 @@ -/* -Copyright (c) for portions of fs_test.go are held by The Go Authors, 2016 and are provided under -the BSD license. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -package fs - -import ( - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "runtime" - "sync" - "testing" -) - -var ( - mu sync.Mutex -) - -func TestRenameWithFallback(t *testing.T) { - dir := t.TempDir() - - if err := RenameWithFallback(filepath.Join(dir, "does_not_exists"), filepath.Join(dir, "dst")); err == nil { - t.Fatal("expected an error for non existing file, but got nil") - } - - srcpath := filepath.Join(dir, "src") - - if srcf, err := os.Create(srcpath); err != nil { - t.Fatal(err) - } else { - srcf.Close() - } - - if err := RenameWithFallback(srcpath, filepath.Join(dir, "dst")); err != nil { - t.Fatal(err) - } - - srcpath = filepath.Join(dir, "a") - if err := os.MkdirAll(srcpath, 0777); err != nil { - t.Fatal(err) - } - - dstpath := filepath.Join(dir, "b") - if err := os.MkdirAll(dstpath, 0777); err != nil { - t.Fatal(err) - } - - if err := RenameWithFallback(srcpath, dstpath); err == nil { - t.Fatal("expected an error if dst is an existing directory, but got nil") - } -} - -func TestCopyDir(t *testing.T) { - dir := t.TempDir() - - srcdir := filepath.Join(dir, "src") - if err := os.MkdirAll(srcdir, 0755); err != nil { - t.Fatal(err) - } - - files := []struct { - path string - contents string - fi os.FileInfo - }{ - {path: "myfile", contents: "hello world"}, - {path: filepath.Join("subdir", "file"), contents: "subdir file"}, - } - - // Create structure indicated in 'files' - for i, file := range files { - fn := filepath.Join(srcdir, file.path) - dn := filepath.Dir(fn) - if err := os.MkdirAll(dn, 0755); err != nil { - t.Fatal(err) - } - - fh, err := os.Create(fn) - if err != nil { - t.Fatal(err) - } - - if _, err = fh.Write([]byte(file.contents)); err != nil { - t.Fatal(err) - } - fh.Close() - - files[i].fi, err = os.Stat(fn) - if err != nil { - t.Fatal(err) - } - } - - destdir := filepath.Join(dir, "dest") - if err := CopyDir(srcdir, destdir); err != nil { - t.Fatal(err) - } - - // Compare copy against structure indicated in 'files' - for _, file := range files { - fn := filepath.Join(srcdir, file.path) - dn := filepath.Dir(fn) - dirOK, err := IsDir(dn) - if err != nil { - t.Fatal(err) - } - if !dirOK { - t.Fatalf("expected %s to be a directory", dn) - } - - got, err := ioutil.ReadFile(fn) - if err != nil { - t.Fatal(err) - } - - if file.contents != string(got) { - t.Fatalf("expected: %s, got: %s", file.contents, string(got)) - } - - gotinfo, err := os.Stat(fn) - if err != nil { - t.Fatal(err) - } - - if file.fi.Mode() != gotinfo.Mode() { - t.Fatalf("expected %s: %#v\n to be the same mode as %s: %#v", - file.path, file.fi.Mode(), fn, gotinfo.Mode()) - } - } -} - -func TestCopyDirFail_SrcInaccessible(t *testing.T) { - if runtime.GOOS == "windows" { - // XXX: setting permissions works differently in - // Microsoft Windows. Skipping this until a - // compatible implementation is provided. - t.Skip("skipping on windows") - } - - var currentUID = os.Getuid() - - if currentUID == 0 { - // Skipping if root, because all files are accessible - t.Skip("Skipping for root user") - } - - var srcdir, dstdir string - - cleanup := setupInaccessibleDir(t, func(dir string) error { - srcdir = filepath.Join(dir, "src") - return os.MkdirAll(srcdir, 0755) - }) - defer cleanup() - - dir := t.TempDir() - - dstdir = filepath.Join(dir, "dst") - if err := CopyDir(srcdir, dstdir); err == nil { - t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir) - } -} - -func TestCopyDirFail_DstInaccessible(t *testing.T) { - if runtime.GOOS == "windows" { - // XXX: setting permissions works differently in - // Microsoft Windows. Skipping this until a - // compatible implementation is provided. - t.Skip("skipping on windows") - } - - var currentUID = os.Getuid() - - if currentUID == 0 { - // Skipping if root, because all files are accessible - t.Skip("Skipping for root user") - } - - var srcdir, dstdir string - - dir := t.TempDir() - - srcdir = filepath.Join(dir, "src") - if err := os.MkdirAll(srcdir, 0755); err != nil { - t.Fatal(err) - } - - cleanup := setupInaccessibleDir(t, func(dir string) error { - dstdir = filepath.Join(dir, "dst") - return nil - }) - defer cleanup() - - if err := CopyDir(srcdir, dstdir); err == nil { - t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir) - } -} - -func TestCopyDirFail_SrcIsNotDir(t *testing.T) { - var srcdir, dstdir string - var err error - - dir := t.TempDir() - - srcdir = filepath.Join(dir, "src") - if _, err = os.Create(srcdir); err != nil { - t.Fatal(err) - } - - dstdir = filepath.Join(dir, "dst") - - if err = CopyDir(srcdir, dstdir); err == nil { - t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir) - } - - if err != errSrcNotDir { - t.Fatalf("expected %v error for CopyDir(%s, %s), got %s", errSrcNotDir, srcdir, dstdir, err) - } - -} - -func TestCopyDirFail_DstExists(t *testing.T) { - var srcdir, dstdir string - var err error - - dir := t.TempDir() - - srcdir = filepath.Join(dir, "src") - if err = os.MkdirAll(srcdir, 0755); err != nil { - t.Fatal(err) - } - - dstdir = filepath.Join(dir, "dst") - if err = os.MkdirAll(dstdir, 0755); err != nil { - t.Fatal(err) - } - - if err = CopyDir(srcdir, dstdir); err == nil { - t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir) - } - - if err != errDstExist { - t.Fatalf("expected %v error for CopyDir(%s, %s), got %s", errDstExist, srcdir, dstdir, err) - } -} - -func TestCopyDirFailOpen(t *testing.T) { - if runtime.GOOS == "windows" { - // XXX: setting permissions works differently in - // Microsoft Windows. os.Chmod(..., 0222) below is not - // enough for the file to be readonly, and os.Chmod(..., - // 0000) returns an invalid argument error. Skipping - // this until a compatible implementation is - // provided. - t.Skip("skipping on windows") - } - - var currentUID = os.Getuid() - - if currentUID == 0 { - // Skipping if root, because all files are accessible - t.Skip("Skipping for root user") - } - - var srcdir, dstdir string - - dir := t.TempDir() - - srcdir = filepath.Join(dir, "src") - if err := os.MkdirAll(srcdir, 0755); err != nil { - t.Fatal(err) - } - - srcfn := filepath.Join(srcdir, "file") - srcf, err := os.Create(srcfn) - if err != nil { - t.Fatal(err) - } - srcf.Close() - - // setup source file so that it cannot be read - if err = os.Chmod(srcfn, 0222); err != nil { - t.Fatal(err) - } - - dstdir = filepath.Join(dir, "dst") - - if err = CopyDir(srcdir, dstdir); err == nil { - t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir) - } -} - -func TestCopyFile(t *testing.T) { - dir := t.TempDir() - - srcf, err := os.Create(filepath.Join(dir, "srcfile")) - if err != nil { - t.Fatal(err) - } - - want := "hello world" - if _, err := srcf.Write([]byte(want)); err != nil { - t.Fatal(err) - } - srcf.Close() - - destf := filepath.Join(dir, "destf") - if err := copyFile(srcf.Name(), destf); err != nil { - t.Fatal(err) - } - - got, err := ioutil.ReadFile(destf) - if err != nil { - t.Fatal(err) - } - - if want != string(got) { - t.Fatalf("expected: %s, got: %s", want, string(got)) - } - - wantinfo, err := os.Stat(srcf.Name()) - if err != nil { - t.Fatal(err) - } - - gotinfo, err := os.Stat(destf) - if err != nil { - t.Fatal(err) - } - - if wantinfo.Mode() != gotinfo.Mode() { - t.Fatalf("expected %s: %#v\n to be the same mode as %s: %#v", srcf.Name(), wantinfo.Mode(), destf, gotinfo.Mode()) - } -} - -func cleanUpDir(dir string) { - // NOTE(mattn): It seems that sometimes git.exe is not dead - // when cleanUpDir() is called. But we do not know any way to wait for it. - if runtime.GOOS == "windows" { - mu.Lock() - exec.Command(`taskkill`, `/F`, `/IM`, `git.exe`).Run() - mu.Unlock() - } - if dir != "" { - os.RemoveAll(dir) - } -} - -func TestCopyFileSymlink(t *testing.T) { - tempdir := t.TempDir() - - testcases := map[string]string{ - filepath.Join("./testdata/symlinks/file-symlink"): filepath.Join(tempdir, "dst-file"), - filepath.Join("./testdata/symlinks/windows-file-symlink"): filepath.Join(tempdir, "windows-dst-file"), - filepath.Join("./testdata/symlinks/invalid-symlink"): filepath.Join(tempdir, "invalid-symlink"), - } - - for symlink, dst := range testcases { - t.Run(symlink, func(t *testing.T) { - var err error - if err = copyFile(symlink, dst); err != nil { - t.Fatalf("failed to copy symlink: %s", err) - } - - var want, got string - - if runtime.GOOS == "windows" { - // Creating symlinks on Windows require an additional permission - // regular users aren't granted usually. So we copy the file - // content as a fall back instead of creating a real symlink. - srcb, err := ioutil.ReadFile(symlink) - if err != nil { - t.Fatalf("%+v", err) - } - dstb, err := ioutil.ReadFile(dst) - if err != nil { - t.Fatalf("%+v", err) - } - - want = string(srcb) - got = string(dstb) - } else { - want, err = os.Readlink(symlink) - if err != nil { - t.Fatalf("%+v", err) - } - - got, err = os.Readlink(dst) - if err != nil { - t.Fatalf("could not resolve symlink: %s", err) - } - } - - if want != got { - t.Fatalf("resolved path is incorrect. expected %s, got %s", want, got) - } - }) - } -} - -func TestCopyFileFail(t *testing.T) { - if runtime.GOOS == "windows" { - // XXX: setting permissions works differently in - // Microsoft Windows. Skipping this until a - // compatible implementation is provided. - t.Skip("skipping on windows") - } - - var currentUID = os.Getuid() - - if currentUID == 0 { - // Skipping if root, because all files are accessible - t.Skip("Skipping for root user") - } - - dir := t.TempDir() - - srcf, err := os.Create(filepath.Join(dir, "srcfile")) - if err != nil { - t.Fatal(err) - } - srcf.Close() - - var dstdir string - - cleanup := setupInaccessibleDir(t, func(dir string) error { - dstdir = filepath.Join(dir, "dir") - return os.Mkdir(dstdir, 0777) - }) - defer cleanup() - - fn := filepath.Join(dstdir, "file") - if err := copyFile(srcf.Name(), fn); err == nil { - t.Fatalf("expected error for %s, got none", fn) - } -} - -// setupInaccessibleDir creates a temporary location with a single -// directory in it, in such a way that directory is not accessible -// after this function returns. -// -// op is called with the directory as argument, so that it can create -// files or other test artifacts. -// -// If setupInaccessibleDir fails in its preparation, or op fails, t.Fatal -// will be invoked. -// -// This function returns a cleanup function that removes all the temporary -// files this function creates. It is the caller's responsibility to call -// this function before the test is done running, whether there's an error or not. -func setupInaccessibleDir(t *testing.T, op func(dir string) error) func() { - dir := t.TempDir() - - subdir := filepath.Join(dir, "dir") - - cleanup := func() { - if err := os.Chmod(subdir, 0777); err != nil { - t.Error(err) - } - } - - if err := os.Mkdir(subdir, 0777); err != nil { - cleanup() - t.Fatal(err) - return nil - } - - if err := op(subdir); err != nil { - cleanup() - t.Fatal(err) - return nil - } - - if err := os.Chmod(subdir, 0666); err != nil { - cleanup() - t.Fatal(err) - return nil - } - - return cleanup -} - -func TestIsDir(t *testing.T) { - - var currentUID = os.Getuid() - - if currentUID == 0 { - // Skipping if root, because all files are accessible - t.Skip("Skipping for root user") - } - - wd, err := os.Getwd() - if err != nil { - t.Fatal(err) - } - - var dn string - - cleanup := setupInaccessibleDir(t, func(dir string) error { - dn = filepath.Join(dir, "dir") - return os.Mkdir(dn, 0777) - }) - defer cleanup() - - tests := map[string]struct { - exists bool - err bool - }{ - wd: {true, false}, - filepath.Join(wd, "testdata"): {true, false}, - filepath.Join(wd, "main.go"): {false, true}, - filepath.Join(wd, "this_file_does_not_exist.thing"): {false, true}, - dn: {false, true}, - } - - if runtime.GOOS == "windows" { - // This test doesn't work on Microsoft Windows because - // of the differences in how file permissions are - // implemented. For this to work, the directory where - // the directory exists should be inaccessible. - delete(tests, dn) - } - - for f, want := range tests { - got, err := IsDir(f) - if err != nil && !want.err { - t.Fatalf("expected no error, got %v", err) - } - - if got != want.exists { - t.Fatalf("expected %t for %s, got %t", want.exists, f, got) - } - } -} - -func TestIsSymlink(t *testing.T) { - - var currentUID = os.Getuid() - - if currentUID == 0 { - // Skipping if root, because all files are accessible - t.Skip("Skipping for root user") - } - - dir := t.TempDir() - - dirPath := filepath.Join(dir, "directory") - if err := os.MkdirAll(dirPath, 0777); err != nil { - t.Fatal(err) - } - - filePath := filepath.Join(dir, "file") - f, err := os.Create(filePath) - if err != nil { - t.Fatal(err) - } - f.Close() - - dirSymlink := filepath.Join(dir, "dirSymlink") - fileSymlink := filepath.Join(dir, "fileSymlink") - - if err = os.Symlink(dirPath, dirSymlink); err != nil { - t.Fatal(err) - } - if err = os.Symlink(filePath, fileSymlink); err != nil { - t.Fatal(err) - } - - var ( - inaccessibleFile string - inaccessibleSymlink string - ) - - cleanup := setupInaccessibleDir(t, func(dir string) error { - inaccessibleFile = filepath.Join(dir, "file") - if fh, err := os.Create(inaccessibleFile); err != nil { - return err - } else if err = fh.Close(); err != nil { - return err - } - - inaccessibleSymlink = filepath.Join(dir, "symlink") - return os.Symlink(inaccessibleFile, inaccessibleSymlink) - }) - defer cleanup() - - tests := map[string]struct{ expected, err bool }{ - dirPath: {false, false}, - filePath: {false, false}, - dirSymlink: {true, false}, - fileSymlink: {true, false}, - inaccessibleFile: {false, true}, - inaccessibleSymlink: {false, true}, - } - - if runtime.GOOS == "windows" { - // XXX: setting permissions works differently in Windows. Skipping - // these cases until a compatible implementation is provided. - delete(tests, inaccessibleFile) - delete(tests, inaccessibleSymlink) - } - - for path, want := range tests { - got, err := IsSymlink(path) - if err != nil { - if !want.err { - t.Errorf("expected no error, got %v", err) - } - } - - if got != want.expected { - t.Errorf("expected %t for %s, got %t", want.expected, path, got) - } - } -} diff --git a/internal/third_party/dep/fs/rename.go b/internal/third_party/dep/fs/rename.go deleted file mode 100644 index a3e5e56a6..000000000 --- a/internal/third_party/dep/fs/rename.go +++ /dev/null @@ -1,58 +0,0 @@ -//go:build !windows - -/* -Copyright (c) for portions of rename.go are held by The Go Authors, 2016 and are provided under -the BSD license. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -package fs - -import ( - "os" - "syscall" - - "github.com/pkg/errors" -) - -// renameFallback attempts to determine the appropriate fallback to failed rename -// operation depending on the resulting error. -func renameFallback(err error, src, dst string) error { - // Rename may fail if src and dst are on different devices; fall back to - // copy if we detect that case. syscall.EXDEV is the common name for the - // cross device link error which has varying output text across different - // operating systems. - terr, ok := err.(*os.LinkError) - if !ok { - return err - } else if terr.Err != syscall.EXDEV { - return errors.Wrapf(terr, "link error: cannot rename %s to %s", src, dst) - } - - return renameByCopy(src, dst) -} diff --git a/internal/third_party/dep/fs/rename_windows.go b/internal/third_party/dep/fs/rename_windows.go deleted file mode 100644 index a377720a6..000000000 --- a/internal/third_party/dep/fs/rename_windows.go +++ /dev/null @@ -1,69 +0,0 @@ -//go:build windows - -/* -Copyright (c) for portions of rename_windows.go are held by The Go Authors, 2016 and are provided under -the BSD license. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -package fs - -import ( - "os" - "syscall" - - "github.com/pkg/errors" -) - -// renameFallback attempts to determine the appropriate fallback to failed rename -// operation depending on the resulting error. -func renameFallback(err error, src, dst string) error { - // Rename may fail if src and dst are on different devices; fall back to - // copy if we detect that case. syscall.EXDEV is the common name for the - // cross device link error which has varying output text across different - // operating systems. - terr, ok := err.(*os.LinkError) - if !ok { - return err - } - - if terr.Err != syscall.EXDEV { - // In windows it can drop down to an operating system call that - // returns an operating system error with a different number and - // message. Checking for that as a fall back. - noerr, ok := terr.Err.(syscall.Errno) - - // 0x11 (ERROR_NOT_SAME_DEVICE) is the windows error. - // See https://msdn.microsoft.com/en-us/library/cc231199.aspx - if ok && noerr != 0x11 { - return errors.Wrapf(terr, "link error: cannot rename %s to %s", src, dst) - } - } - - return renameByCopy(src, dst) -} diff --git a/internal/third_party/dep/fs/testdata/symlinks/file-symlink b/internal/third_party/dep/fs/testdata/symlinks/file-symlink deleted file mode 120000 index 4c52274de..000000000 --- a/internal/third_party/dep/fs/testdata/symlinks/file-symlink +++ /dev/null @@ -1 +0,0 @@ -../test.file \ No newline at end of file diff --git a/internal/third_party/dep/fs/testdata/symlinks/invalid-symlink b/internal/third_party/dep/fs/testdata/symlinks/invalid-symlink deleted file mode 120000 index 0edf4f301..000000000 --- a/internal/third_party/dep/fs/testdata/symlinks/invalid-symlink +++ /dev/null @@ -1 +0,0 @@ -/non/existing/file \ No newline at end of file diff --git a/internal/third_party/dep/fs/testdata/symlinks/windows-file-symlink b/internal/third_party/dep/fs/testdata/symlinks/windows-file-symlink deleted file mode 120000 index af1d6c8f5..000000000 --- a/internal/third_party/dep/fs/testdata/symlinks/windows-file-symlink +++ /dev/null @@ -1 +0,0 @@ -C:/Users/ibrahim/go/src/github.com/golang/dep/internal/fs/testdata/test.file \ No newline at end of file diff --git a/internal/third_party/dep/fs/testdata/test.file b/internal/third_party/dep/fs/testdata/test.file deleted file mode 100644 index e69de29bb..000000000 diff --git a/internal/third_party/k8s.io/kubernetes/deployment/util/deploymentutil.go b/internal/third_party/k8s.io/kubernetes/deployment/util/deploymentutil.go deleted file mode 100644 index 103db35c4..000000000 --- a/internal/third_party/k8s.io/kubernetes/deployment/util/deploymentutil.go +++ /dev/null @@ -1,178 +0,0 @@ -/* -Copyright 2016 The Kubernetes 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 util - -import ( - "context" - "sort" - - apps "k8s.io/api/apps/v1" - v1 "k8s.io/api/core/v1" - apiequality "k8s.io/apimachinery/pkg/api/equality" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - intstrutil "k8s.io/apimachinery/pkg/util/intstr" - appsclient "k8s.io/client-go/kubernetes/typed/apps/v1" -) - -// deploymentutil contains a copy of a few functions from Kubernetes controller code to avoid a dependency on k8s.io/kubernetes. -// This code is copied from https://github.com/kubernetes/kubernetes/blob/e856613dd5bb00bcfaca6974431151b5c06cbed5/pkg/controller/deployment/util/deployment_util.go -// No changes to the code were made other than removing some unused functions - -// RsListFunc returns the ReplicaSet from the ReplicaSet namespace and the List metav1.ListOptions. -type RsListFunc func(string, metav1.ListOptions) ([]*apps.ReplicaSet, error) - -// ListReplicaSets returns a slice of RSes the given deployment targets. -// Note that this does NOT attempt to reconcile ControllerRef (adopt/orphan), -// because only the controller itself should do that. -// However, it does filter out anything whose ControllerRef doesn't match. -func ListReplicaSets(deployment *apps.Deployment, getRSList RsListFunc) ([]*apps.ReplicaSet, error) { - // TODO: Right now we list replica sets by their labels. We should list them by selector, i.e. the replica set's selector - // should be a superset of the deployment's selector, see https://github.com/kubernetes/kubernetes/issues/19830. - namespace := deployment.Namespace - selector, err := metav1.LabelSelectorAsSelector(deployment.Spec.Selector) - if err != nil { - return nil, err - } - options := metav1.ListOptions{LabelSelector: selector.String()} - all, err := getRSList(namespace, options) - if err != nil { - return nil, err - } - // Only include those whose ControllerRef matches the Deployment. - owned := make([]*apps.ReplicaSet, 0, len(all)) - for _, rs := range all { - if metav1.IsControlledBy(rs, deployment) { - owned = append(owned, rs) - } - } - return owned, nil -} - -// ReplicaSetsByCreationTimestamp sorts a list of ReplicaSet by creation timestamp, using their names as a tie breaker. -type ReplicaSetsByCreationTimestamp []*apps.ReplicaSet - -func (o ReplicaSetsByCreationTimestamp) Len() int { return len(o) } -func (o ReplicaSetsByCreationTimestamp) Swap(i, j int) { o[i], o[j] = o[j], o[i] } -func (o ReplicaSetsByCreationTimestamp) Less(i, j int) bool { - if o[i].CreationTimestamp.Equal(&o[j].CreationTimestamp) { - return o[i].Name < o[j].Name - } - return o[i].CreationTimestamp.Before(&o[j].CreationTimestamp) -} - -// FindNewReplicaSet returns the new RS this given deployment targets (the one with the same pod template). -func FindNewReplicaSet(deployment *apps.Deployment, rsList []*apps.ReplicaSet) *apps.ReplicaSet { - sort.Sort(ReplicaSetsByCreationTimestamp(rsList)) - for i := range rsList { - if EqualIgnoreHash(&rsList[i].Spec.Template, &deployment.Spec.Template) { - // In rare cases, such as after cluster upgrades, Deployment may end up with - // having more than one new ReplicaSets that have the same template as its template, - // see https://github.com/kubernetes/kubernetes/issues/40415 - // We deterministically choose the oldest new ReplicaSet. - return rsList[i] - } - } - // new ReplicaSet does not exist. - return nil -} - -// EqualIgnoreHash returns true if two given podTemplateSpec are equal, ignoring the diff in value of Labels[pod-template-hash] -// We ignore pod-template-hash because: -// 1. The hash result would be different upon podTemplateSpec API changes -// (e.g. the addition of a new field will cause the hash code to change) -// 2. The deployment template won't have hash labels -func EqualIgnoreHash(template1, template2 *v1.PodTemplateSpec) bool { - t1Copy := template1.DeepCopy() - t2Copy := template2.DeepCopy() - // Remove hash labels from template.Labels before comparing - delete(t1Copy.Labels, apps.DefaultDeploymentUniqueLabelKey) - delete(t2Copy.Labels, apps.DefaultDeploymentUniqueLabelKey) - return apiequality.Semantic.DeepEqual(t1Copy, t2Copy) -} - -// GetNewReplicaSet returns a replica set that matches the intent of the given deployment; get ReplicaSetList from client interface. -// Returns nil if the new replica set doesn't exist yet. -func GetNewReplicaSet(deployment *apps.Deployment, c appsclient.AppsV1Interface) (*apps.ReplicaSet, error) { - rsList, err := ListReplicaSets(deployment, RsListFromClient(c)) - if err != nil { - return nil, err - } - return FindNewReplicaSet(deployment, rsList), nil -} - -// RsListFromClient returns an rsListFunc that wraps the given client. -func RsListFromClient(c appsclient.AppsV1Interface) RsListFunc { - return func(namespace string, options metav1.ListOptions) ([]*apps.ReplicaSet, error) { - rsList, err := c.ReplicaSets(namespace).List(context.Background(), options) - if err != nil { - return nil, err - } - var ret []*apps.ReplicaSet - for i := range rsList.Items { - ret = append(ret, &rsList.Items[i]) - } - return ret, err - } -} - -// IsRollingUpdate returns true if the strategy type is a rolling update. -func IsRollingUpdate(deployment *apps.Deployment) bool { - return deployment.Spec.Strategy.Type == apps.RollingUpdateDeploymentStrategyType -} - -// MaxUnavailable returns the maximum unavailable pods a rolling deployment can take. -func MaxUnavailable(deployment apps.Deployment) int32 { - if !IsRollingUpdate(&deployment) || *(deployment.Spec.Replicas) == 0 { - return int32(0) - } - // Error caught by validation - _, maxUnavailable, _ := ResolveFenceposts(deployment.Spec.Strategy.RollingUpdate.MaxSurge, deployment.Spec.Strategy.RollingUpdate.MaxUnavailable, *(deployment.Spec.Replicas)) - if maxUnavailable > *deployment.Spec.Replicas { - return *deployment.Spec.Replicas - } - return maxUnavailable -} - -// ResolveFenceposts resolves both maxSurge and maxUnavailable. This needs to happen in one -// step. For example: -// -// 2 desired, max unavailable 1%, surge 0% - should scale old(-1), then new(+1), then old(-1), then new(+1) -// 1 desired, max unavailable 1%, surge 0% - should scale old(-1), then new(+1) -// 2 desired, max unavailable 25%, surge 1% - should scale new(+1), then old(-1), then new(+1), then old(-1) -// 1 desired, max unavailable 25%, surge 1% - should scale new(+1), then old(-1) -// 2 desired, max unavailable 0%, surge 1% - should scale new(+1), then old(-1), then new(+1), then old(-1) -// 1 desired, max unavailable 0%, surge 1% - should scale new(+1), then old(-1) -func ResolveFenceposts(maxSurge, maxUnavailable *intstrutil.IntOrString, desired int32) (int32, int32, error) { - surge, err := intstrutil.GetValueFromIntOrPercent(intstrutil.ValueOrDefault(maxSurge, intstrutil.FromInt(0)), int(desired), true) - if err != nil { - return 0, 0, err - } - unavailable, err := intstrutil.GetValueFromIntOrPercent(intstrutil.ValueOrDefault(maxUnavailable, intstrutil.FromInt(0)), int(desired), false) - if err != nil { - return 0, 0, err - } - - if surge == 0 && unavailable == 0 { - // Validation should never allow the user to explicitly use zero values for both maxSurge - // maxUnavailable. Due to rounding down maxUnavailable though, it may resolve to zero. - // If both fenceposts resolve to zero, then we should set maxUnavailable to 1 on the - // theory that surge might not work due to quota. - unavailable = 1 - } - - return int32(surge), int32(unavailable), nil -} diff --git a/internal/tlsutil/cfg.go b/internal/tlsutil/cfg.go deleted file mode 100644 index 8b9d4329f..000000000 --- a/internal/tlsutil/cfg.go +++ /dev/null @@ -1,58 +0,0 @@ -/* -Copyright The Helm 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 tlsutil - -import ( - "crypto/tls" - "crypto/x509" - "os" - - "github.com/pkg/errors" -) - -// Options represents configurable options used to create client and server TLS configurations. -type Options struct { - CaCertFile string - // If either the KeyFile or CertFile is empty, ClientConfig() will not load them. - KeyFile string - CertFile string - // Client-only options - InsecureSkipVerify bool -} - -// ClientConfig returns a TLS configuration for use by a Helm client. -func ClientConfig(opts Options) (cfg *tls.Config, err error) { - var cert *tls.Certificate - var pool *x509.CertPool - - if opts.CertFile != "" || opts.KeyFile != "" { - if cert, err = CertFromFilePair(opts.CertFile, opts.KeyFile); err != nil { - if os.IsNotExist(err) { - return nil, errors.Wrapf(err, "could not load x509 key pair (cert: %q, key: %q)", opts.CertFile, opts.KeyFile) - } - return nil, errors.Wrapf(err, "could not read x509 key pair (cert: %q, key: %q)", opts.CertFile, opts.KeyFile) - } - } - if !opts.InsecureSkipVerify && opts.CaCertFile != "" { - if pool, err = CertPoolFromFile(opts.CaCertFile); err != nil { - return nil, err - } - } - - cfg = &tls.Config{InsecureSkipVerify: opts.InsecureSkipVerify, Certificates: []tls.Certificate{*cert}, RootCAs: pool} - return cfg, nil -} diff --git a/internal/tlsutil/tls.go b/internal/tlsutil/tls.go deleted file mode 100644 index ed7795dbe..000000000 --- a/internal/tlsutil/tls.go +++ /dev/null @@ -1,76 +0,0 @@ -/* -Copyright The Helm 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 tlsutil - -import ( - "crypto/tls" - "crypto/x509" - "io/ioutil" - - "github.com/pkg/errors" -) - -// NewClientTLS returns tls.Config appropriate for client auth. -func NewClientTLS(certFile, keyFile, caFile string) (*tls.Config, error) { - config := tls.Config{} - - if certFile != "" && keyFile != "" { - cert, err := CertFromFilePair(certFile, keyFile) - if err != nil { - return nil, err - } - config.Certificates = []tls.Certificate{*cert} - } - - if caFile != "" { - cp, err := CertPoolFromFile(caFile) - if err != nil { - return nil, err - } - config.RootCAs = cp - } - - return &config, nil -} - -// CertPoolFromFile returns an x509.CertPool containing the certificates -// in the given PEM-encoded file. -// Returns an error if the file could not be read, a certificate could not -// be parsed, or if the file does not contain any certificates -func CertPoolFromFile(filename string) (*x509.CertPool, error) { - b, err := ioutil.ReadFile(filename) - if err != nil { - return nil, errors.Errorf("can't read CA file: %v", filename) - } - cp := x509.NewCertPool() - if !cp.AppendCertsFromPEM(b) { - return nil, errors.Errorf("failed to append certificates from file: %s", filename) - } - return cp, nil -} - -// CertFromFilePair returns an tls.Certificate containing the -// certificates public/private key pair from a pair of given PEM-encoded files. -// Returns an error if the file could not be read, a certificate could not -// be parsed, or if the file does not contain any certificates -func CertFromFilePair(certFile, keyFile string) (*tls.Certificate, error) { - cert, err := tls.LoadX509KeyPair(certFile, keyFile) - if err != nil { - return nil, errors.Wrapf(err, "can't load key pair from cert %s and key %s", certFile, keyFile) - } - return &cert, err -} diff --git a/internal/tlsutil/tlsutil_test.go b/internal/tlsutil/tlsutil_test.go deleted file mode 100644 index e660c030c..000000000 --- a/internal/tlsutil/tlsutil_test.go +++ /dev/null @@ -1,113 +0,0 @@ -/* -Copyright The Helm 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 tlsutil - -import ( - "path/filepath" - "testing" -) - -const tlsTestDir = "../../testdata" - -const ( - testCaCertFile = "rootca.crt" - testCertFile = "crt.pem" - testKeyFile = "key.pem" -) - -func TestClientConfig(t *testing.T) { - opts := Options{ - CaCertFile: testfile(t, testCaCertFile), - CertFile: testfile(t, testCertFile), - KeyFile: testfile(t, testKeyFile), - InsecureSkipVerify: false, - } - - cfg, err := ClientConfig(opts) - if err != nil { - t.Fatalf("error building tls client config: %v", err) - } - - if got := len(cfg.Certificates); got != 1 { - t.Fatalf("expecting 1 client certificates, got %d", got) - } - if cfg.InsecureSkipVerify { - t.Fatalf("insecure skip verify mismatch, expecting false") - } - if cfg.RootCAs == nil { - t.Fatalf("mismatch tls RootCAs, expecting non-nil") - } -} - -func testfile(t *testing.T, file string) (path string) { - var err error - if path, err = filepath.Abs(filepath.Join(tlsTestDir, file)); err != nil { - t.Fatalf("error getting absolute path to test file %q: %v", file, err) - } - return path -} - -func TestNewClientTLS(t *testing.T) { - certFile := testfile(t, testCertFile) - keyFile := testfile(t, testKeyFile) - caCertFile := testfile(t, testCaCertFile) - - cfg, err := NewClientTLS(certFile, keyFile, caCertFile) - if err != nil { - t.Error(err) - } - - if got := len(cfg.Certificates); got != 1 { - t.Fatalf("expecting 1 client certificates, got %d", got) - } - if cfg.InsecureSkipVerify { - t.Fatalf("insecure skip verify mismatch, expecting false") - } - if cfg.RootCAs == nil { - t.Fatalf("mismatch tls RootCAs, expecting non-nil") - } - - cfg, err = NewClientTLS("", "", caCertFile) - if err != nil { - t.Error(err) - } - - if got := len(cfg.Certificates); got != 0 { - t.Fatalf("expecting 0 client certificates, got %d", got) - } - if cfg.InsecureSkipVerify { - t.Fatalf("insecure skip verify mismatch, expecting false") - } - if cfg.RootCAs == nil { - t.Fatalf("mismatch tls RootCAs, expecting non-nil") - } - - cfg, err = NewClientTLS(certFile, keyFile, "") - if err != nil { - t.Error(err) - } - - if got := len(cfg.Certificates); got != 1 { - t.Fatalf("expecting 1 client certificates, got %d", got) - } - if cfg.InsecureSkipVerify { - t.Fatalf("insecure skip verify mismatch, expecting false") - } - if cfg.RootCAs != nil { - t.Fatalf("mismatch tls RootCAs, expecting nil") - } -} diff --git a/internal/urlutil/urlutil.go b/internal/urlutil/urlutil.go deleted file mode 100644 index a8cf7398c..000000000 --- a/internal/urlutil/urlutil.go +++ /dev/null @@ -1,73 +0,0 @@ -/* -Copyright The Helm 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 urlutil - -import ( - "net/url" - "path" - "path/filepath" -) - -// URLJoin joins a base URL to one or more path components. -// -// It's like filepath.Join for URLs. If the baseURL is pathish, this will still -// perform a join. -// -// If the URL is unparsable, this returns an error. -func URLJoin(baseURL string, paths ...string) (string, error) { - u, err := url.Parse(baseURL) - if err != nil { - return "", err - } - // We want path instead of filepath because path always uses /. - all := []string{u.Path} - all = append(all, paths...) - u.Path = path.Join(all...) - return u.String(), nil -} - -// Equal normalizes two URLs and then compares for equality. -func Equal(a, b string) bool { - au, err := url.Parse(a) - if err != nil { - a = filepath.Clean(a) - b = filepath.Clean(b) - // If urls are paths, return true only if they are an exact match - return a == b - } - bu, err := url.Parse(b) - if err != nil { - return false - } - - for _, u := range []*url.URL{au, bu} { - if u.Path == "" { - u.Path = "/" - } - u.Path = filepath.Clean(u.Path) - } - return au.String() == bu.String() -} - -// ExtractHostname returns hostname from URL -func ExtractHostname(addr string) (string, error) { - u, err := url.Parse(addr) - if err != nil { - return "", err - } - return u.Hostname(), nil -} diff --git a/internal/urlutil/urlutil_test.go b/internal/urlutil/urlutil_test.go deleted file mode 100644 index 82acc40fe..000000000 --- a/internal/urlutil/urlutil_test.go +++ /dev/null @@ -1,81 +0,0 @@ -/* -Copyright The Helm 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 urlutil - -import "testing" - -func TestURLJoin(t *testing.T) { - tests := []struct { - name, url, expect string - paths []string - }{ - {name: "URL, one path", url: "http://example.com", paths: []string{"hello"}, expect: "http://example.com/hello"}, - {name: "Long URL, one path", url: "http://example.com/but/first", paths: []string{"slurm"}, expect: "http://example.com/but/first/slurm"}, - {name: "URL, two paths", url: "http://example.com", paths: []string{"hello", "world"}, expect: "http://example.com/hello/world"}, - {name: "URL, no paths", url: "http://example.com", paths: []string{}, expect: "http://example.com"}, - {name: "basepath, two paths", url: "../example.com", paths: []string{"hello", "world"}, expect: "../example.com/hello/world"}, - } - - for _, tt := range tests { - if got, err := URLJoin(tt.url, tt.paths...); err != nil { - t.Errorf("%s: error %q", tt.name, err) - } else if got != tt.expect { - t.Errorf("%s: expected %q, got %q", tt.name, tt.expect, got) - } - } -} - -func TestEqual(t *testing.T) { - for _, tt := range []struct { - a, b string - match bool - }{ - {"http://example.com", "http://example.com", true}, - {"http://example.com", "http://another.example.com", false}, - {"https://example.com", "https://example.com", true}, - {"http://example.com/", "http://example.com", true}, - {"https://example.com", "http://example.com", false}, - {"http://example.com/foo", "http://example.com/foo/", true}, - {"http://example.com/foo//", "http://example.com/foo/", true}, - {"http://example.com/./foo/", "http://example.com/foo/", true}, - {"http://example.com/bar/../foo/", "http://example.com/foo/", true}, - {"/foo", "/foo", true}, - {"/foo", "/foo/", true}, - {"/foo/.", "/foo/", true}, - {"%/1234", "%/1234", true}, - {"%/1234", "%/123", false}, - {"/1234", "%/1234", false}, - } { - if tt.match != Equal(tt.a, tt.b) { - t.Errorf("Expected %q==%q to be %t", tt.a, tt.b, tt.match) - } - } -} - -func TestExtractHostname(t *testing.T) { - tests := map[string]string{ - "http://example.com": "example.com", - "https://example.com/foo": "example.com", - - "https://example.com:31337/not/with/a/bang/but/a/whimper": "example.com", - } - for start, expect := range tests { - if got, _ := ExtractHostname(start); got != expect { - t.Errorf("Got %q, expected %q", got, expect) - } - } -} diff --git a/internal/version/version.go b/internal/version/version.go deleted file mode 100644 index bf2af39bb..000000000 --- a/internal/version/version.go +++ /dev/null @@ -1,81 +0,0 @@ -/* -Copyright The Helm 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 version // import "helm.sh/helm/v3/internal/version" - -import ( - "flag" - "runtime" - "strings" -) - -var ( - // version is the current version of Helm. - // Update this whenever making a new release. - // The version is of the format Major.Minor.Patch[-Prerelease][+BuildMetadata] - // - // Increment major number for new feature additions and behavioral changes. - // Increment minor number for bug fixes and performance enhancements. - version = "v3.10" - - // metadata is extra build time data - metadata = "" - // gitCommit is the git sha1 - gitCommit = "" - // gitTreeState is the state of the git tree - gitTreeState = "" -) - -// BuildInfo describes the compile time information. -type BuildInfo struct { - // Version is the current semver. - Version string `json:"version,omitempty"` - // GitCommit is the git sha1. - GitCommit string `json:"git_commit,omitempty"` - // GitTreeState is the state of the git tree. - GitTreeState string `json:"git_tree_state,omitempty"` - // GoVersion is the version of the Go compiler used. - GoVersion string `json:"go_version,omitempty"` -} - -// GetVersion returns the semver string of the version -func GetVersion() string { - if metadata == "" { - return version - } - return version + "+" + metadata -} - -// GetUserAgent returns a user agent for user with an HTTP client -func GetUserAgent() string { - return "Helm/" + strings.TrimPrefix(GetVersion(), "v") -} - -// Get returns build info -func Get() BuildInfo { - v := BuildInfo{ - Version: GetVersion(), - GitCommit: gitCommit, - GitTreeState: gitTreeState, - GoVersion: runtime.Version(), - } - - // HACK(bacongobbler): strip out GoVersion during a test run for consistent test output - if flag.Lookup("test.v") != nil { - v.GoVersion = "" - } - return v -}