Commit dd4263a0 authored by Jack Greenfield's avatar Jack Greenfield
Browse files

Merge pull request #573 from jackgr/pluggable-services

Pluggable services
parents 2daaae1a 1a64a499
main Release add-codeql dependabot/go_modules/github.com/docker/distribution-2.8.2incompatible dependabot/go_modules/github.com/lib/pq-1.10.9 dependabot/go_modules/github.com/rubenv/sql-migrate-1.4.0 dependabot/go_modules/golang.org/x/crypto-0.9.0 dependabot/go_modules/golang.org/x/term-0.8.0 dependabot/go_modules/k8s.io/klog/v2-2.100.1 dev-v2 feat-v3/event-emitter-lua kube-update-test release-2.0 release-2.1 release-2.10 release-2.11 release-2.12 release-2.13 release-2.14 release-2.15 release-2.16 release-2.17 release-2.2 release-2.3 release-2.4 release-2.5 release-2.6 release-2.7 release-2.8 release-2.9 release-3.0 release-3.1 release-3.10 release-3.11 release-3.12 release-3.2 release-3.3 release-3.4 release-3.5 release-3.6 release-3.6.1 release-3.6.2 release-3.7 release-3.8 release-3.9 release-v3.0.0-beta.4 1.999.0 v3.12.0 v3.12.0-rc.1 v3.12.0-dev.1 v3.11.3 v3.11.2 v3.11.1 v3.11.0 v3.11.0-rc.2 v3.11.0-rc.1 v3.10.3 v3.10.2 v3.10.1 v3.10.0 v3.10.0-rc.1 v3.9.4 v3.9.3 v3.9.2 v3.9.1 v3.9.0 v3.9.0-rc.1 v3.8.2 v3.8.1 v3.8.0 v3.8.0-rc.2 v3.8.0-rc.1 v3.7.2 v3.7.1 v3.7.0 v3.7.0-rc.3 v3.7.0-rc.2 v3.7.0-rc.1 v3.6.3 v3.6.2 v3.6.1 v3.6.0 v3.6.0-rc.1 v3.5.4 v3.5.3 v3.5.2 v3.5.1 v3.5.0 v3.5.0-rc.2 v3.5.0-rc.1 v3.4.2 v3.4.1 v3.4.0 v3.4.0-rc.1 v3.3.4 v3.3.3 v3.3.2 v3.3.1 v3.3.0 v3.3.0-rc.2 v3.3.0-rc.1 v3.2.4 v3.2.3 v3.2.2 v3.2.1 v3.2.0 v3.2.0-rc.1 v3.1.3 v3.1.2 v3.1.1 v3.1.0 v3.1.0-rc.3 v3.1.0-rc.2 v3.1.0-rc.1 v3.0.3 v3.0.2 v3.0.1 v3.0.0 v3.0.0-rc.4 v3.0.0-rc.3 v3.0.0-rc.2 v3.0.0-rc.1 v3.0.0-beta.5 v3.0.0-beta.4 v3.0.0-beta.3 v3.0.0-beta.2 v3.0.0-beta.1 v3.0.0-alpha.2 v3.0.0-alpha.1 v2.17.0 v2.17.0-rc.1 v2.16.12 v2.16.11 v2.16.10 v2.16.9 v2.16.8 v2.16.7 v2.16.6 v2.16.5 v2.16.4 v2.16.3 v2.16.2 v2.16.1 v2.16.0 v2.16.0-rc.2 v2.16.0-rc.1 v2.15.2 v2.15.1 v2.15.0 v2.15.0-rc.2 v2.15.0-rc.1 v2.14.3 v2.14.2 v2.14.1 v2.14.0 v2.14.0-rc.2 v2.14.0-rc.1 v2.13.1 v2.13.1-rc.1 v2.13.0 v2.13.0-rc.2 v2.13.0-rc.1 v2.12.3 v2.12.2 v2.12.1 v2.12.0 v2.12.0-rc.2 v2.12.0-rc.1 v2.11.0 v2.11.0-rc.4 v2.11.0-rc.3 v2.11.0-rc.2 v2.11.0-rc.1 v2.10.0 v2.10.0-rc.3 v2.10.0-rc.2 v2.10.0-rc.1 v2.9.1 v2.9.0 v2.9.0-rc5 v2.9.0-rc4 v2.9.0-rc3 v2.9.0-rc2 v2.9.0-rc1 v2.8.2 v2.8.2-rc1 v2.8.1 v2.8.0 v2.8.0-rc.1 v2.7.2 v2.7.1 v2.7.0 v2.7.0-rc1 v2.6.2 v2.6.1 v2.6.0 v2.5.1 v2.5.0 v2.4.2 v2.4.1 v2.4.0 v2.3.1 v2.3.0 v2.2.3 v2.2.2 v2.2.1 v2.2.0 v2.1.3 v2.1.2 v2.1.1 v2.1.0 v2.0.2 v2.0.1 v2.0.0 v2.0.0-rc.2 v2.0.0-rc.1 v2.0.0-beta.2 v2.0.0-beta.1 v2.0.0-alpha.5 v2.0.0-alpha.4 v2.0.0-alpha.3 v2.0.0-alpha.2 v2.0.0-alpha.1
No related merge requests found
Showing with 97 additions and 198 deletions
+97 -198
...@@ -20,9 +20,7 @@ import ( ...@@ -20,9 +20,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"log" "log"
"net"
"net/http" "net/http"
"os"
"strings" "strings"
"github.com/kubernetes/helm/cmd/manager/manager" "github.com/kubernetes/helm/cmd/manager/manager"
...@@ -85,17 +83,14 @@ func setupDependencies(c *router.Context) error { ...@@ -85,17 +83,14 @@ func setupDependencies(c *router.Context) error {
return nil return nil
} }
const expanderPort = "8080"
const deployerPort = "8080"
func newManager(c *router.Context) manager.Manager { func newManager(c *router.Context) manager.Manager {
cfg := c.Config cfg := c.Config
service := repo.NewInmemRepoService() service := repo.NewInmemRepoService()
cp := c.CredentialProvider cp := c.CredentialProvider
rp := repo.NewRepoProvider(service, repo.NewGCSRepoProvider(cp), cp) rp := repo.NewRepoProvider(service, repo.NewGCSRepoProvider(cp), cp)
expander := manager.NewExpander(getServiceURL(cfg.ExpanderURL, cfg.ExpanderName, expanderPort), rp) expander := manager.NewExpander(cfg.ExpanderPort, cfg.ExpanderURL, rp)
deployer := manager.NewDeployer(getServiceURL(cfg.DeployerURL, cfg.DeployerName, deployerPort)) deployer := manager.NewDeployer(util.GetServiceURLOrDie(cfg.DeployerName, cfg.DeployerPort, cfg.DeployerURL))
address := strings.TrimPrefix(getServiceURL(cfg.MongoAddress, cfg.MongoName, cfg.MongoPort), "http://") address := strings.TrimPrefix(util.GetServiceURLOrDie(cfg.MongoName, cfg.MongoPort, cfg.MongoAddress), "http://")
repository := createRepository(address) repository := createRepository(address)
return manager.NewManager(expander, deployer, repository, rp, service, c.CredentialProvider) return manager.NewManager(expander, deployer, repository, rp, service, c.CredentialProvider)
} }
...@@ -109,41 +104,6 @@ func createRepository(address string) repository.Repository { ...@@ -109,41 +104,6 @@ func createRepository(address string) repository.Repository {
return r return r
} }
func getServiceURL(serviceURL, serviceName, servicePort string) string {
if serviceURL == "" {
serviceURL = makeEnvVariableURL(serviceName)
if serviceURL == "" {
addrs, err := net.LookupHost(serviceName)
if err != nil || len(addrs) < 1 {
log.Fatalf("cannot resolve service:%v. environment:%v\n", serviceName, os.Environ())
}
serviceURL = fmt.Sprintf("http://%s:%s", addrs[0], servicePort)
}
}
return serviceURL
}
// makeEnvVariableURL takes a service name and returns the value of the
// environment variable that identifies its URL, if it exists, or the empty
// string, if it doesn't.
func makeEnvVariableURL(str string) string {
prefix := makeEnvVariableName(str)
url := os.Getenv(prefix + "_PORT")
return strings.Replace(url, "tcp", "http", 1)
}
// makeEnvVariableName is copied from the Kubernetes source,
// which is referenced by the documentation for service environment variables.
func makeEnvVariableName(str string) string {
// TODO: If we simplify to "all names are DNS1123Subdomains" this
// will need two tweaks:
// 1) Handle leading digits
// 2) Handle dots
return strings.ToUpper(strings.Replace(str, "-", "_", -1))
}
func listDeploymentsHandlerFunc(w http.ResponseWriter, r *http.Request, c *router.Context) error { func listDeploymentsHandlerFunc(w http.ResponseWriter, r *http.Request, c *router.Context) error {
handler := "manager: list deployments" handler := "manager: list deployments"
util.LogHandlerEntry(handler, r) util.LogHandlerEntry(handler, r)
......
...@@ -31,9 +31,10 @@ import ( ...@@ -31,9 +31,10 @@ import (
var ( var (
port = flag.Int("port", 8080, "The port to listen on") port = flag.Int("port", 8080, "The port to listen on")
maxLength = flag.Int64("maxLength", 1024, "The maximum length (KB) of a template.") maxLength = flag.Int64("maxLength", 1024, "The maximum length (KB) of a template.")
expanderName = flag.String("expander", "expandybird-service", "The DNS name of the expander service.") expanderPort = flag.String("expanderPort", "8081", "The IP port of the default expander service.")
expanderURL = flag.String("expanderURL", "", "The URL for the expander service.") expanderURL = flag.String("expanderURL", "", "The URL for the default expander service.")
deployerName = flag.String("deployer", "resourcifier-service", "The DNS name of the deployer service.") deployerName = flag.String("deployer", "resourcifier-service", "The DNS name of the deployer service.")
deployerPort = flag.String("deployerPort", "8082", "The IP port of the deployer service.")
deployerURL = flag.String("deployerURL", "", "The URL for the deployer service.") deployerURL = flag.String("deployerURL", "", "The URL for the deployer service.")
credentialFile = flag.String("credentialFile", "", "Local file to use for credentials.") credentialFile = flag.String("credentialFile", "", "Local file to use for credentials.")
credentialSecrets = flag.Bool("credentialSecrets", true, "Use secrets for credentials.") credentialSecrets = flag.Bool("credentialSecrets", true, "Use secrets for credentials.")
...@@ -73,9 +74,10 @@ func parseFlags() *router.Config { ...@@ -73,9 +74,10 @@ func parseFlags() *router.Config {
return &router.Config{ return &router.Config{
Address: fmt.Sprintf(":%d", *port), Address: fmt.Sprintf(":%d", *port),
MaxTemplateLength: *maxLength, MaxTemplateLength: *maxLength,
ExpanderName: *expanderName, ExpanderPort: *expanderPort,
ExpanderURL: *expanderURL, ExpanderURL: *expanderURL,
DeployerName: *deployerName, DeployerName: *deployerName,
DeployerPort: *deployerPort,
DeployerURL: *deployerURL, DeployerURL: *deployerURL,
CredentialFile: *credentialFile, CredentialFile: *credentialFile,
CredentialSecrets: *credentialSecrets, CredentialSecrets: *credentialSecrets,
......
...@@ -20,21 +20,17 @@ import ( ...@@ -20,21 +20,17 @@ import (
"github.com/kubernetes/helm/pkg/common" "github.com/kubernetes/helm/pkg/common"
"github.com/kubernetes/helm/pkg/expansion" "github.com/kubernetes/helm/pkg/expansion"
"github.com/kubernetes/helm/pkg/repo" "github.com/kubernetes/helm/pkg/repo"
"github.com/kubernetes/helm/pkg/util"
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/url"
"strings"
) )
/*
const (
// TODO (iantw): Align this with a character not allowed to show up in resource names.
layoutNodeKeySeparator = "#"
)
*/
// ExpandedConfiguration is the structure returned by the expansion service. // ExpandedConfiguration is the structure returned by the expansion service.
type ExpandedConfiguration struct { type ExpandedConfiguration struct {
Config *common.Configuration `json:"config"` Config *common.Configuration `json:"config"`
...@@ -47,23 +43,20 @@ type Expander interface { ...@@ -47,23 +43,20 @@ type Expander interface {
} }
// NewExpander returns a new initialized Expander. // NewExpander returns a new initialized Expander.
func NewExpander(URL string, rp repo.IRepoProvider) Expander { func NewExpander(port, URL string, rp repo.IRepoProvider) Expander {
if rp == nil { if rp == nil {
rp = repo.NewRepoProvider(nil, nil, nil) rp = repo.NewRepoProvider(nil, nil, nil)
} }
return &expander{expanderURL: URL, repoProvider: rp} return &expander{expanderPort: port, expanderURL: URL, repoProvider: rp}
} }
type expander struct { type expander struct {
repoProvider repo.IRepoProvider repoProvider repo.IRepoProvider
expanderPort string
expanderURL string expanderURL string
} }
func (e *expander) getBaseURL() string {
return fmt.Sprintf("%s/expand", e.expanderURL)
}
// ExpandConfiguration expands the supplied configuration and returns // ExpandConfiguration expands the supplied configuration and returns
// an expanded configuration. // an expanded configuration.
func (e *expander) ExpandConfiguration(conf *common.Configuration) (*ExpandedConfiguration, error) { func (e *expander) ExpandConfiguration(conf *common.Configuration) (*ExpandedConfiguration, error) {
...@@ -81,80 +74,81 @@ func (e *expander) expandConfiguration(conf *common.Configuration) (*ExpandedCon ...@@ -81,80 +74,81 @@ func (e *expander) expandConfiguration(conf *common.Configuration) (*ExpandedCon
// Iterate over all of the resources in the unexpanded configuration // Iterate over all of the resources in the unexpanded configuration
for _, resource := range conf.Resources { for _, resource := range conf.Resources {
// A primitive layout resource captures only the name and type additions := []*common.Resource{resource}
layout := &common.LayoutResource{ layout := &common.LayoutResource{
Resource: common.Resource{ Resource: common.Resource{
Name: resource.Name, Type: resource.Type, Name: resource.Name, Type: resource.Type,
}, },
} }
// If the type is not a chart reference, then it must be primitive // If the type is a chart reference
if !repo.IsChartReference(resource.Type) { if repo.IsChartReference(resource.Type) {
// Add it to the flat list of exapnded resources // Fetch, decompress and unpack
resources = append(resources, resource) cbr, _, err := e.repoProvider.GetChartByReference(resource.Type)
if err != nil {
// Add its layout to the list of layouts at this level return nil, err
layouts = append(layouts, layout) }
continue
} defer cbr.Close()
expander := cbr.Chartfile().Expander
// It is a chart, so go fetch it, decompress it and unpack it if expander != nil && expander.Name != "" {
cbr, _, err := e.repoProvider.GetChartByReference(resource.Type) // Load the charts contents into strings that we can pass to exapnsion
if err != nil { content, err := cbr.LoadContent()
return nil, err if err != nil {
} return nil, err
}
defer cbr.Close()
// Build a request to the expansion service and call it to do the expansion
// Now, load the charts contents into strings that we can pass to exapnsion svcReq := &expansion.ServiceRequest{
content, err := cbr.LoadContent() ChartInvocation: resource,
if err != nil { Chart: content,
return nil, err }
}
svcResp, err := e.callService(expander.Name, svcReq)
// Build a request to the expansion service and call it to do the expansion if err != nil {
svcReq := &expansion.ServiceRequest{ return nil, err
ChartInvocation: resource, }
Chart: content,
} // Call ourselves recursively with the list of resources returned by expansion
expConf, err := e.expandConfiguration(svcResp)
svcResp, err := e.callService(svcReq) if err != nil {
if err != nil { return nil, err
return nil, err }
// Append the reources returned by the recursion to the flat list of resources
additions = expConf.Config.Resources
// This was not a primitive resource, so add its properties to the layout
// Then add the all of the layout resources returned by the recursion to the layout
layout.Properties = resource.Properties
layout.Resources = expConf.Layout.Resources
}
} }
// Call ourselves recursively with the list of resources returned by expansion resources = append(resources, additions...)
expConf, err := e.expandConfiguration(svcResp)
if err != nil {
return nil, err
}
// Append the reources returned by the recursion to the flat list of resources
resources = append(resources, expConf.Config.Resources...)
// This was not a primitive resource, so add its properties to the layout
layout.Properties = resource.Properties
// Now add the all of the layout resources returned by the recursion to the layout
layout.Resources = expConf.Layout.Resources
layouts = append(layouts, layout) layouts = append(layouts, layout)
} }
// All done with this level, so return the espanded configuration // All done with this level, so return the expanded configuration
return &ExpandedConfiguration{ return &ExpandedConfiguration{
Config: &common.Configuration{Resources: resources}, Config: &common.Configuration{Resources: resources},
Layout: &common.Layout{Resources: layouts}, Layout: &common.Layout{Resources: layouts},
}, nil }, nil
} }
func (e *expander) callService(svcReq *expansion.ServiceRequest) (*common.Configuration, error) { func (e *expander) callService(svcName string, svcReq *expansion.ServiceRequest) (*common.Configuration, error) {
svcURL, err := e.getServiceURL(svcName)
if err != nil {
return nil, err
}
j, err := json.Marshal(svcReq) j, err := json.Marshal(svcReq)
if err != nil { if err != nil {
return nil, err return nil, err
} }
reader := ioutil.NopCloser(bytes.NewReader(j)) reader := ioutil.NopCloser(bytes.NewReader(j))
request, err := http.NewRequest("POST", e.getBaseURL(), reader) request, err := http.NewRequest("POST", svcURL, reader)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -188,3 +182,21 @@ func (e *expander) callService(svcReq *expansion.ServiceRequest) (*common.Config ...@@ -188,3 +182,21 @@ func (e *expander) callService(svcReq *expansion.ServiceRequest) (*common.Config
return svcResp, nil return svcResp, nil
} }
func (e *expander) getServiceURL(svcName string) (string, error) {
if !strings.HasPrefix(svcName, "http:") && !strings.HasPrefix(svcName, "https:") {
var err error
svcName, err = util.GetServiceURL(svcName, e.expanderPort, e.expanderURL)
if err != nil {
return "", err
}
}
u, err := url.Parse(svcName)
if err != nil {
return "", err
}
u.Path = fmt.Sprintf("%s/expand", u.Path)
return u.String(), nil
}
...@@ -27,7 +27,6 @@ import ( ...@@ -27,7 +27,6 @@ import (
"testing" "testing"
"github.com/ghodss/yaml" "github.com/ghodss/yaml"
"github.com/kubernetes/helm/pkg/chart"
"github.com/kubernetes/helm/pkg/common" "github.com/kubernetes/helm/pkg/common"
"github.com/kubernetes/helm/pkg/expansion" "github.com/kubernetes/helm/pkg/expansion"
"github.com/kubernetes/helm/pkg/repo" "github.com/kubernetes/helm/pkg/repo"
...@@ -219,21 +218,6 @@ var roundTripResponses = []*ExpandedConfiguration{ ...@@ -219,21 +218,6 @@ var roundTripResponses = []*ExpandedConfiguration{
} }
*/ */
type mockRepoProvider struct {
}
func (m *mockRepoProvider) GetChartByReference(reference string) (*chart.Chart, repo.IChartRepo, error) {
return &chart.Chart{}, nil, nil
}
func (m *mockRepoProvider) GetRepoByChartURL(URL string) (repo.IChartRepo, error) {
return nil, nil
}
func (m *mockRepoProvider) GetRepoByURL(URL string) (repo.IChartRepo, error) {
return nil, nil
}
type ExpanderTestCase struct { type ExpanderTestCase struct {
Description string Description string
Error string Error string
...@@ -266,7 +250,7 @@ func TestExpandTemplate(t *testing.T) { ...@@ -266,7 +250,7 @@ func TestExpandTemplate(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(etc.Handler)) ts := httptest.NewServer(http.HandlerFunc(etc.Handler))
defer ts.Close() defer ts.Close()
expander := NewExpander(ts.URL, getTestRepoProvider(t)) expander := NewExpander("8081", ts.URL, getTestRepoProvider(t))
resource := &common.Resource{ resource := &common.Resource{
Name: "test_invocation", Name: "test_invocation",
Type: TestResourceType, Type: TestResourceType,
......
...@@ -31,12 +31,14 @@ type Config struct { ...@@ -31,12 +31,14 @@ type Config struct {
Address string Address string
// MaxTemplateLength is the maximum length of a template. // MaxTemplateLength is the maximum length of a template.
MaxTemplateLength int64 MaxTemplateLength int64
// ExpanderName is the DNS name of the expansion service. // ExpanderPort is the default expander's IP port
ExpanderName string ExpanderPort string
// ExpanderURL is the expander service's URL. // ExpanderURL is the default expander's URL
ExpanderURL string ExpanderURL string
// DeployerName is the deployer's DNS name // DeployerName is the deployer's DNS name
DeployerName string DeployerName string
// DeployerPort is the deployer's IP port
DeployerPort string
// DeployerURL is the deployer's URL // DeployerURL is the deployer's URL
DeployerURL string DeployerURL string
// CredentialFile is the file to the credentials. // CredentialFile is the file to the credentials.
......
...@@ -3,5 +3,5 @@ description: Port of the replicatedservice template from kubernetes/charts ...@@ -3,5 +3,5 @@ description: Port of the replicatedservice template from kubernetes/charts
version: "2" version: "2"
home: "" home: ""
expander: expander:
name: Expandybird name: expandybird-service
entrypoint: templates/redis.jinja entrypoint: templates/redis.jinja
...@@ -3,5 +3,5 @@ description: Port of the replicatedservice template from kubernetes/charts ...@@ -3,5 +3,5 @@ description: Port of the replicatedservice template from kubernetes/charts
version: "3" version: "3"
home: "" home: ""
expander: expander:
name: Expandybird name: expandybird-service
entrypoint: templates/replicatedservice.py entrypoint: templates/replicatedservice.py
...@@ -61,7 +61,7 @@ type EnvConstraint struct { ...@@ -61,7 +61,7 @@ type EnvConstraint struct {
// Expander controls how template/ is evaluated. // Expander controls how template/ is evaluated.
type Expander struct { type Expander struct {
// Currently just Expandybird or GoTemplate // Kubernetes service name to look up in DNS.
Name string `json:"name"` Name string `json:"name"`
// During evaluation, which file to start from. // During evaluation, which file to start from.
Entrypoint string `json:"entrypoint"` Entrypoint string `json:"entrypoint"`
......
...@@ -55,8 +55,8 @@ func TestLoadChartfile(t *testing.T) { ...@@ -55,8 +55,8 @@ func TestLoadChartfile(t *testing.T) {
if expander == nil { if expander == nil {
t.Errorf("No expander found in %s", testfile) t.Errorf("No expander found in %s", testfile)
} else { } else {
if expander.Name != "Expandybird" { if expander.Name != "expandybird-service" {
t.Errorf("Expected expander name Expandybird, got %s", expander.Name) t.Errorf("Expected expander name expandybird-service, got %s", expander.Name)
} }
if expander.Entrypoint != "templates/wordpress.jinja" { if expander.Entrypoint != "templates/wordpress.jinja" {
......
No preview for this file type
...@@ -27,7 +27,7 @@ environment: ...@@ -27,7 +27,7 @@ environment:
apiGroups: apiGroups:
- 3rdParty - 3rdParty
expander: expander:
name: Expandybird name: expandybird-service
entrypoint: templates/wordpress.jinja entrypoint: templates/wordpress.jinja
schema: wordpress.jinja.schema schema: wordpress.jinja.schema
\ No newline at end of file
...@@ -30,7 +30,7 @@ var ( ...@@ -30,7 +30,7 @@ var (
TestChartName = "frobnitz" TestChartName = "frobnitz"
TestChartVersion = "0.0.1" TestChartVersion = "0.0.1"
TestArchiveName = TestChartName + "-" + TestChartVersion + ".tgz" TestArchiveName = TestChartName + "-" + TestChartVersion + ".tgz"
TestChartFile = "testdata/frobnitz/Chart.yaml" TestChartFile = "../chart/testdata/frobnitz/Chart.yaml"
TestShouldFindRegex = regexp.MustCompile(TestArchiveName) TestShouldFindRegex = regexp.MustCompile(TestArchiveName)
TestShouldNotFindRegex = regexp.MustCompile("foobar") TestShouldNotFindRegex = regexp.MustCompile("foobar")
TestName = "bucket-name" TestName = "bucket-name"
......
The testdata directory here holds charts that match the specification.
The `fromnitz/` directory contains a chart that matches the chart
specification.
The `frobnitz-0.0.1.tgz` file is an archive of the `frobnitz` directory.
File deleted
#helm:generate foo
name: frobnitz
description: This is a frobniz.
version: "1.2.3-alpha.1+12345"
keywords:
- frobnitz
- sprocket
- dodad
maintainers:
- name: The Helm Team
email: helm@example.com
- name: Someone Else
email: nobody@example.com
source:
- https://example.com/foo/bar
home: http://example.com
dependencies:
- name: thingerbob
location: https://example.com/charts/thingerbob-3.2.1.tgz
version: ^3
environment:
- name: Kubernetes
version: ~1.1
extensions:
- extensions/v1beta1
- extensions/v1beta1/daemonset
apiGroups:
- 3rdParty
expander:
name: Expandybird
entrypoint: templates/wordpress.jinja
schema: wordpress.jinja.schema
\ No newline at end of file
LICENSE placeholder.
# Frobnitz
This is an example chart.
## Usage
This is an example. It has no usage.
## Development
For developer info, see the top-level repository.
This is a placeholder for documentation.
# Placeholder.
<?xml version="1.0"?>
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.0" width="256" height="256" id="test">
<desc>Example icon</desc>
<rect id="first" x="2" y="2" width="40" height="60" fill="navy"/>
<rect id="second" x="15" y="4" width="40" height="60" fill="red"/>
</svg>
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment