mirror of https://github.com/helm/helm
Includes client creation and search Signed-off-by: Matt Farina <matt@mattfarina.com>pull/6187/head
parent
d677952165
commit
2613c3cda3
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
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"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"helm.sh/helm/internal/version"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 user agent to identify as when making requests
|
||||||
|
UserAgent string
|
||||||
|
|
||||||
|
// 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{
|
||||||
|
UserAgent: "Helm/" + strings.TrimPrefix(version.GetVersion(), "v"),
|
||||||
|
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
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
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 (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"helm.sh/helm/internal/version"
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
ua := "Helm/" + strings.TrimPrefix(version.GetVersion(), "v")
|
||||||
|
if c.UserAgent != ua {
|
||||||
|
t.Errorf("incorrect user agent. Expected %q but got %q", ua, c.UserAgent)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
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 monocular instances
|
||||||
|
// like the Helm Hub.
|
||||||
|
//
|
||||||
|
// This is a library for interacting with monocular
|
||||||
|
package monocular
|
@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
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/pkg/chart"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The structs below represent the structure of the response from the monocular
|
||||||
|
// search API.
|
||||||
|
|
||||||
|
// SearchResult represents an individual chart result
|
||||||
|
type SearchResult struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Attributes Attributes `json:"attributes"`
|
||||||
|
Links Links `json:"links"`
|
||||||
|
Relationships Relationships `json:"relationships"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attributes is the attributes for the chart
|
||||||
|
type Attributes 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 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 Data `json:"data"`
|
||||||
|
Links Links `json:"links"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Data provides the specific data on the chart version
|
||||||
|
type Data 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, "api/chartsvc/v1/charts/search")
|
||||||
|
|
||||||
|
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", c.UserAgent)
|
||||||
|
|
||||||
|
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"`
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
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://kubernetes-charts.storage.googleapis.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/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://kubernetes-charts.storage.googleapis.com/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")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue