<commit_before>package api

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"net/url"
	"time"

	"github.com/grafana/grafana/pkg/api/cloudwatch"
	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/log"
	"github.com/grafana/grafana/pkg/metrics"
	"github.com/grafana/grafana/pkg/middleware"
	m "github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/setting"
	"github.com/grafana/grafana/pkg/util"
)

var (
	dataproxyLogger log.Logger = log.New("data-proxy-log")
)

func NewReverseProxy(ds *m.DataSource, proxyPath string, targetUrl *url.URL) *httputil.ReverseProxy {
	director := func(req *http.Request) {
		req.URL.Scheme = targetUrl.Scheme
		req.URL.Host = targetUrl.Host
		req.Host = targetUrl.Host

		reqQueryVals := req.URL.Query()

		if ds.Type == m.DS_INFLUXDB_08 {
			req.URL.Path = util.JoinUrlFragments(targetUrl.Path, "db/"+ds.Database+"/"+proxyPath)
			reqQueryVals.Add("u", ds.User)
			reqQueryVals.Add("p", ds.Password)
			req.URL.RawQuery = reqQueryVals.Encode()
		} else if ds.Type == m.DS_INFLUXDB {
			req.URL.Path = util.JoinUrlFragments(targetUrl.Path, proxyPath)
			req.URL.RawQuery = reqQueryVals.Encode()
			if !ds.BasicAuth {
				req.Header.Del("Authorization")
				req.Header.Add("Authorization", util.GetBasicAuthHeader(ds.User, ds.Password))
			}
		} else {
			req.URL.Path = util.JoinUrlFragments(targetUrl.Path, proxyPath)
		}

		if ds.BasicAuth {
			req.Header.Del("Authorization")
			req.Header.Add("Authorization", util.GetBasicAuthHeader(ds.BasicAuthUser, ds.BasicAuthPassword))
		}

		dsAuth := req.Header.Get("X-DS-Authorization")
		if len(dsAuth) > 0 {
			req.Header.Del("X-DS-Authorization")
			req.Header.Del("Authorization")
			req.Header.Add("Authorization", dsAuth)
		}

		// clear cookie headers
		req.Header.Del("Cookie")
		req.Header.Del("Set-Cookie")
	}

	return &httputil.ReverseProxy{Director: director, FlushInterval: time.Millisecond * 200}
}

func getDatasource(id int64, orgId int64) (*m.DataSource, error) {
	query := m.GetDataSourceByIdQuery{Id: id, OrgId: orgId}
	if err := bus.Dispatch(&query); err != nil {
		return nil, err
	}

	return query.Result, nil
}

func ProxyDataSourceRequest(c *middleware.Context) {
	c.TimeRequest(metrics.M_DataSource_ProxyReq_Timer)

	ds, err := getDatasource(c.ParamsInt64(":id"), c.OrgId)

	if err != nil {
		c.JsonApiErr(500, "Unable to load datasource meta data", err)
		return
	}

	if ds.Type == m.DS_CLOUDWATCH {
		cloudwatch.HandleRequest(c, ds)
		return
	}

	if ds.Type == m.DS_INFLUXDB {
		if c.Query("db") != ds.Database {
			c.JsonApiErr(403, "Datasource is not configured to allow this database", nil)
			return
		}
	}

	targetUrl, _ := url.Parse(ds.Url)
	if len(setting.DataProxyWhiteList) > 0 {
		if _, exists := setting.DataProxyWhiteList[targetUrl.Host]; !exists {
			c.JsonApiErr(403, "Data proxy hostname and ip are not included in whitelist", nil)
			return
		}
	}

	proxyPath := c.Params("*")

	if ds.Type == m.DS_ES {
		if c.Req.Request.Method == "DELETE" {
			c.JsonApiErr(403, "Deletes not allowed on proxied Elasticsearch datasource", nil)
			return
		}
		if c.Req.Request.Method == "PUT" {
			c.JsonApiErr(403, "Puts not allowed on proxied Elasticsearch datasource", nil)
			return
		}
		if c.Req.Request.Method == "POST" && proxyPath != "_msearch" {
			c.JsonApiErr(403, "Posts not allowed on proxied Elasticsearch datasource except on /_msearch", nil)
			return
		}
	}

	proxy := NewReverseProxy(ds, proxyPath, targetUrl)
	proxy.Transport, err = ds.GetHttpTransport()
	if err != nil {
		c.JsonApiErr(400, "Unable to load TLS certificate", err)
		return
	}

	logProxyRequest(ds.Type, c)
	proxy.ServeHTTP(c.Resp, c.Req.Request)
	c.Resp.Header().Del("Set-Cookie")
}

func logProxyRequest(dataSourceType string, c *middleware.Context) {
	if !setting.DataProxyLogging {
		return
	}

		var body string
		if c.Req.Request.Body != nil {
			buffer, _ := ioutil.ReadAll(c.Req.Request.Body)
			c.Req.Request.Body = ioutil.NopCloser(bytes.NewBuffer(buffer))
			body = string(buffer)
		}
	}

	dataproxyLogger.Info("Proxying incoming request",
		"userid", c.UserId,
		"orgid", c.OrgId,
		"username", c.Login,
		"datasource", dataSourceType,
		"uri", c.Req.RequestURI,
		"method", c.Req.Request.Method,
		"body", body)
}
<commit_msg>tech(dataproxy): make the code a little bit more defensive<commit_after>package api

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"net/url"
	"time"

	"github.com/grafana/grafana/pkg/api/cloudwatch"
	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/log"
	"github.com/grafana/grafana/pkg/metrics"
	"github.com/grafana/grafana/pkg/middleware"
	m "github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/setting"
	"github.com/grafana/grafana/pkg/util"
)

var (
	dataproxyLogger log.Logger = log.New("data-proxy-log")
)

func NewReverseProxy(ds *m.DataSource, proxyPath string, targetUrl *url.URL) *httputil.ReverseProxy {
	director := func(req *http.Request) {
		req.URL.Scheme = targetUrl.Scheme
		req.URL.Host = targetUrl.Host
		req.Host = targetUrl.Host

		reqQueryVals := req.URL.Query()

		if ds.Type == m.DS_INFLUXDB_08 {
			req.URL.Path = util.JoinUrlFragments(targetUrl.Path, "db/"+ds.Database+"/"+proxyPath)
			reqQueryVals.Add("u", ds.User)
			reqQueryVals.Add("p", ds.Password)
			req.URL.RawQuery = reqQueryVals.Encode()
		} else if ds.Type == m.DS_INFLUXDB {
			req.URL.Path = util.JoinUrlFragments(targetUrl.Path, proxyPath)
			req.URL.RawQuery = reqQueryVals.Encode()
			if !ds.BasicAuth {
				req.Header.Del("Authorization")
				req.Header.Add("Authorization", util.GetBasicAuthHeader(ds.User, ds.Password))
			}
		} else {
			req.URL.Path = util.JoinUrlFragments(targetUrl.Path, proxyPath)
		}

		if ds.BasicAuth {
			req.Header.Del("Authorization")
			req.Header.Add("Authorization", util.GetBasicAuthHeader(ds.BasicAuthUser, ds.BasicAuthPassword))
		}

		dsAuth := req.Header.Get("X-DS-Authorization")
		if len(dsAuth) > 0 {
			req.Header.Del("X-DS-Authorization")
			req.Header.Del("Authorization")
			req.Header.Add("Authorization", dsAuth)
		}

		// clear cookie headers
		req.Header.Del("Cookie")
		req.Header.Del("Set-Cookie")
	}

	return &httputil.ReverseProxy{Director: director, FlushInterval: time.Millisecond * 200}
}

func getDatasource(id int64, orgId int64) (*m.DataSource, error) {
	query := m.GetDataSourceByIdQuery{Id: id, OrgId: orgId}
	if err := bus.Dispatch(&query); err != nil {
		return nil, err
	}

	return query.Result, nil
}

func ProxyDataSourceRequest(c *middleware.Context) {
	c.TimeRequest(metrics.M_DataSource_ProxyReq_Timer)

	ds, err := getDatasource(c.ParamsInt64(":id"), c.OrgId)

	if err != nil {
		c.JsonApiErr(500, "Unable to load datasource meta data", err)
		return
	}

	if ds.Type == m.DS_CLOUDWATCH {
		cloudwatch.HandleRequest(c, ds)
		return
	}

	if ds.Type == m.DS_INFLUXDB {
		if c.Query("db") != ds.Database {
			c.JsonApiErr(403, "Datasource is not configured to allow this database", nil)
			return
		}
	}

	targetUrl, _ := url.Parse(ds.Url)
	if len(setting.DataProxyWhiteList) > 0 {
		if _, exists := setting.DataProxyWhiteList[targetUrl.Host]; !exists {
			c.JsonApiErr(403, "Data proxy hostname and ip are not included in whitelist", nil)
			return
		}
	}

	proxyPath := c.Params("*")

	if ds.Type == m.DS_ES {
		if c.Req.Request.Method == "DELETE" {
			c.JsonApiErr(403, "Deletes not allowed on proxied Elasticsearch datasource", nil)
			return
		}
		if c.Req.Request.Method == "PUT" {
			c.JsonApiErr(403, "Puts not allowed on proxied Elasticsearch datasource", nil)
			return
		}
		if c.Req.Request.Method == "POST" && proxyPath != "_msearch" {
			c.JsonApiErr(403, "Posts not allowed on proxied Elasticsearch datasource except on /_msearch", nil)
			return
		}
	}

	proxy := NewReverseProxy(ds, proxyPath, targetUrl)
	proxy.Transport, err = ds.GetHttpTransport()
	if err != nil {
		c.JsonApiErr(400, "Unable to load TLS certificate", err)
		return
	}

	logProxyRequest(ds.Type, c)
	proxy.ServeHTTP(c.Resp, c.Req.Request)
	c.Resp.Header().Del("Set-Cookie")
}

func logProxyRequest(dataSourceType string, c *middleware.Context) {
	if !setting.DataProxyLogging {
		return
	}

	var body string
	if c.Req.Request.Body != nil {
		buffer, err := ioutil.ReadAll(c.Req.Request.Body)
		if err == nil {
			c.Req.Request.Body = ioutil.NopCloser(bytes.NewBuffer(buffer))
			body = string(buffer)
		}
	}

	dataproxyLogger.Info("Proxying incoming request",
		"userid", c.UserId,
		"orgid", c.OrgId,
		"username", c.Login,
		"datasource", dataSourceType,
		"uri", c.Req.RequestURI,
		"method", c.Req.Request.Method,
		"body", body)
}
<|endoftext|>


<commit_before>/*
Copyright 2011 Google Inc.

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 client

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"sync"

	"camlistore.org/pkg/auth"
)

type Client struct {
	server   string // URL prefix before "/camli/"
	authMode auth.AuthMode

	httpClient *http.Client

	statsMutex sync.Mutex
	stats      Stats

	log *log.Logger // not nil
}

type Stats struct {
	// The number of uploads that were requested, but perhaps
	// not actually performed if the server already had the items.
	UploadRequests ByCountAndBytes

	// The uploads which were actually sent to the blobserver
	// due to the server not having the blobs
	Uploads ByCountAndBytes
}

func (s *Stats) String() string {
	return "[uploadRequests=" + s.UploadRequests.String() + " uploads=" + s.Uploads.String() + "]"
}

type ByCountAndBytes struct {
	Blobs int
	Bytes int64
}

func (bb *ByCountAndBytes) String() string {
	return fmt.Sprintf("[blobs=%d bytes=%d]", bb.Blobs, bb.Bytes)
}

func New(server string) *Client {
	return &Client{
		server:     server,
		httpClient: http.DefaultClient,
	}
}

func (c *Client) SetHttpClient(client *http.Client) {
	c.httpClient = client
}

func NewOrFail() *Client {
	log := log.New(os.Stderr, "", log.Ldate|log.Ltime)
	c := &Client{
		server:     blobServerOrDie(),
		httpClient: http.DefaultClient,
		log:        log,
	}
	err := c.SetupAuth()
	if err != nil {
		log.Fatal(err)
	}
	return c
}

type devNullWriter struct{}

func (_ *devNullWriter) Write(p []byte) (int, error) {
	return len(p), nil
}

func (c *Client) SetLogger(logger *log.Logger) {
	if logger == nil {
		c.log = log.New(&devNullWriter{}, "", 0)
	} else {
		c.log = logger
	}
}

func (c *Client) Stats() Stats {
	c.statsMutex.Lock()
	defer c.statsMutex.Unlock()
	return c.stats // copy
}

func (c *Client) newRequest(method, url string) *http.Request {
	req, err := http.NewRequest(method, url, nil)
	if err != nil {
		panic(err.Error())
	}
	c.authMode.AddAuthHeader(req)
	return req
}
<commit_msg>client: use ioutil.Discard<commit_after>/*
Copyright 2011 Google Inc.

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 client

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"sync"

	"camlistore.org/pkg/auth"
)

type Client struct {
	server   string // URL prefix before "/camli/"
	authMode auth.AuthMode

	httpClient *http.Client

	statsMutex sync.Mutex
	stats      Stats

	log *log.Logger // not nil
}

type Stats struct {
	// The number of uploads that were requested, but perhaps
	// not actually performed if the server already had the items.
	UploadRequests ByCountAndBytes

	// The uploads which were actually sent to the blobserver
	// due to the server not having the blobs
	Uploads ByCountAndBytes
}

func (s *Stats) String() string {
	return "[uploadRequests=" + s.UploadRequests.String() + " uploads=" + s.Uploads.String() + "]"
}

type ByCountAndBytes struct {
	Blobs int
	Bytes int64
}

func (bb *ByCountAndBytes) String() string {
	return fmt.Sprintf("[blobs=%d bytes=%d]", bb.Blobs, bb.Bytes)
}

func New(server string) *Client {
	return &Client{
		server:     server,
		httpClient: http.DefaultClient,
	}
}

func (c *Client) SetHttpClient(client *http.Client) {
	c.httpClient = client
}

func NewOrFail() *Client {
	log := log.New(os.Stderr, "", log.Ldate|log.Ltime)
	c := &Client{
		server:     blobServerOrDie(),
		httpClient: http.DefaultClient,
		log:        log,
	}
	err := c.SetupAuth()
	if err != nil {
		log.Fatal(err)
	}
	return c
}

func (c *Client) SetLogger(logger *log.Logger) {
	if logger == nil {
		c.log = log.New(ioutil.Discard, "", 0)
	} else {
		c.log = logger
	}
}

func (c *Client) Stats() Stats {
	c.statsMutex.Lock()
	defer c.statsMutex.Unlock()
	return c.stats // copy
}

func (c *Client) newRequest(method, url string) *http.Request {
	req, err := http.NewRequest(method, url, nil)
	if err != nil {
		panic(err.Error())
	}
	c.authMode.AddAuthHeader(req)
	return req
}
<|endoftext|>


<commit_before>package config

type RubyType interface {
	GetRubyTypeTag() string
}

type Dimg struct {
	Docker      DockerDimg  `yaml:"_docker"`
	Builder     string      `yaml:"_builder"`
	Shell       ShellDimg   `yaml:"_shell"`
	Chef        Chef        `yaml:"_chef"`
	Artifact    []Artifact  `yaml:"_artifact"`
	GitArtifact GitArtifact `yaml:"_git_artifact"`
	Mount       []Mount     `yaml:"_mount"`
}

func (cfg *Dimg) GetRubyTypeTag() string {
	return "ruby/object:Dapp::Dimg::Config::Directive::Dimg"
}

type ArtifactDimg struct {
	Dimg
	Docker DockerArtifact `yaml:"_docker"`
	Shell  ShellArtifact  `yaml:"_shell"`
}

func (cfg *ArtifactDimg) GetRubyTypeTag() string {
	return "ruby/hash:Dapp::Dimg::Config::Directive::ArtifactDimg"
}

type DockerDimg struct {
	DockerBase
	Volume     []string          `yaml:"_volume"`
	Expose     []string          `yaml:"_expose"`
	Env        map[string]string `yaml:"_env"`
	Label      map[string]string `yaml:"_label"`
	Cmd        []string          `yaml:"_cmd"`
	Onbuild    []string          `yaml:"_onbuild"`
	Workdir    string            `yaml:"_workdir"`
	User       string            `yaml:"_user"`
	Entrypoint []string          `yaml:"_entrypoint"`
}

func (cfg *DockerDimg) GetRubyTypeTag() string {
	return "ruby/object:Dapp::Dimg::Config::Directive::Docker::Dimg"
}

type DockerArtifact struct {
	DockerBase
}

func (cfg *DockerArtifact) GetRubyTypeTag() string {
	return "ruby/object:Dapp::Dimg::Config::Directive::Docker::Artifact"
}

type DockerBase struct {
	From             string `yaml:"_from"`
	FromCacheVersion string `yaml:"_from_cache_version"`
}

type ShellDimg struct {
	Version       string       `yaml:"_version"`
	BeforeInstall StageCommand `yaml:"_before_install"`
	BeforeSetup   StageCommand `yaml:"_before_setup"`
	Install       StageCommand `yaml:"_install"`
	Setup         StageCommand `yaml:"_setup"`
}

func (cfg *ShellDimg) GetRubyTypeTag() string {
	return "ruby/object:Dapp::Dimg::Config::Directive::Shell::Dimg"
}

type ShellArtifact struct {
	ShellDimg
	BuildArtifact StageCommand `yaml:"_build_artifact"`
}

func (cfg *ShellArtifact) GetRubyTypeTag() string {
	return "ruby/object:Dapp::Dimg::Config::Directive::Shell::Artifact"
}

type StageCommand struct {
	Version string   `yaml:"_version"`
	Run     []string `yaml:"_run"`
}

func (cfg *StageCommand) GetRubyTypeTag() string {
	return "ruby/object:Dapp::Dimg::Config::Directive::GitArtifactLocal::Export::StageDependencies"
}

type Chef struct {
	Dimod      []string         `yaml:"_dimod"`
	Recipe     []string         `yaml:"_recipe"`
	Attributes []ChefAttributes `yaml:"_attributes"`
}

func (cfg *Chef) GetRubyTypeTag() string {
	return "ruby/object:Dapp::Dimg::Config::Directive::Chef"
}

type ChefAttributes map[interface{}]interface{}

func (cfg *ChefAttributes) GetRubyTypeTag() string {
	return "ruby/hash:Dapp::Dimg::Config::Directive::Chef::Attributes"
}

type Artifact struct {
	ArtifactBase
	Config ArtifactDimg `yaml:"_config"`
	Before string       `yaml:"_before"`
	After  string       `yaml:"_after"`
}

func (cfg *Artifact) GetRubyTypeTag() string {
	return "ruby/hash:Dapp::Dimg::Config::Directive::Artifact::Export"
}

type GitArtifact struct {
	Local  []GitArtifactLocal  `yaml:"_local"`
	Remote []GitArtifactRemote `yaml:"_remote"`
}

func (cfg *GitArtifact) GetRubyTypeTag() string {
	return "ruby/hash:Dapp::Dimg::Config::Directive::Dimg::InstanceMethods::GitArtifact"
}

type GitArtifactLocal struct {
	ArtifactBase
	As                string            `yaml:"_as"`
	StageDependencies StageDependencies `yaml:"_stage_dependencies"`
}

func (cfg *GitArtifactLocal) GetRubyTypeTag() string {
	return "ruby/hash:Dapp::Dimg::Config::Directive::GitArtifactLocal"
}

type StageDependencies struct {
	Install       []string `yaml:"_install"`
	Setup         []string `yaml:"_setup"`
	BeforeSetup   []string `yaml:"_before_setup"`
	BuildArtifact []string `yaml:"_build_artifact"`
}

func (cfg *StageDependencies) GetRubyTypeTag() string {
	return "ruby/hash:Dapp::Dimg::Config::Directive::Shell::Dimg::StageCommand"
}

type GitArtifactRemote struct {
	GitArtifactLocal
	Url    string `yaml:"_url"`
	Name   string `yaml:"_name"`
	Branch string `yaml:"_branch"`
	Commit string `yaml:"_commit"`
}

func (cfg *GitArtifactRemote) GetRubyTypeTag() string {
	return "ruby/hash:Dapp::Dimg::Config::Directive::GitArtifactRemote"
}

type ArtifactBase struct {
	Cwd          string   `yaml:"_cwd"`
	To           string   `yaml:"_to"`
	IncludePaths []string `yaml:"_include_paths"`
	ExcludePaths []string `yaml:"_exclude_paths"`
	Owner        string   `yaml:"_owner"`
	Group        string   `yaml:"_group"`
}

type Mount struct {
	To   string `yaml:"_to"`
	From string `yaml:"_from"`
	Type string `yaml:"_type"`
}

func (cfg *Mount) GetRubyTypeTag() string {
	return "ruby/object:Dapp::Dimg::Config::Directive::Mount"
}
<commit_msg>Config: отказ от встраивания<commit_after>package config

type RubyType interface {
	GetRubyTypeTag() string
}

type Config struct {
	Dimg []Dimg `yaml:"_dimg"`
}

func (cfg *Config) GetRubyTypeTag() string {
	return "ruby/object:Dapp::Config::Config"
}

type Dimg struct {
	Name        string      `yaml:"_name"`
	Docker      DockerDimg  `yaml:"_docker"`
	Builder     string      `yaml:"_builder"`
	Shell       ShellDimg   `yaml:"_shell"`
	Chef        Chef        `yaml:"_chef"`
	Artifact    []Artifact  `yaml:"_artifact"`
	GitArtifact GitArtifact `yaml:"_git_artifact"`
	Mount       []Mount     `yaml:"_mount"`
}

func (cfg *Dimg) GetRubyTypeTag() string {
	return "ruby/object:Dapp::Dimg::Config::Directive::Dimg"
}

type ArtifactDimg struct {
	Name        string         `yaml:"_name"`
	Docker      DockerArtifact `yaml:"_docker"`
	Builder     string         `yaml:"_builder"`
	Shell       ShellArtifact  `yaml:"_shell"`
	Chef        Chef           `yaml:"_chef"`
	Artifact    []Artifact     `yaml:"_artifact"`
	GitArtifact GitArtifact    `yaml:"_git_artifact"`
	Mount       []Mount        `yaml:"_mount"`
}

func (cfg *ArtifactDimg) GetRubyTypeTag() string {
	return "ruby/hash:Dapp::Dimg::Config::Directive::ArtifactDimg"
}

type DockerDimg struct {
	From             string            `yaml:"_from"`
	FromCacheVersion string            `yaml:"_from_cache_version"`
	Volume           []string          `yaml:"_volume"`
	Expose           []string          `yaml:"_expose"`
	Env              map[string]string `yaml:"_env"`
	Label            map[string]string `yaml:"_label"`
	Cmd              []string          `yaml:"_cmd"`
	Onbuild          []string          `yaml:"_onbuild"`
	Workdir          string            `yaml:"_workdir"`
	User             string            `yaml:"_user"`
	Entrypoint       []string          `yaml:"_entrypoint"`
}

func (cfg *DockerDimg) GetRubyTypeTag() string {
	return "ruby/object:Dapp::Dimg::Config::Directive::Docker::Dimg"
}

type DockerArtifact struct {
	From             string `yaml:"_from"`
	FromCacheVersion string `yaml:"_from_cache_version"`
}

func (cfg *DockerArtifact) GetRubyTypeTag() string {
	return "ruby/object:Dapp::Dimg::Config::Directive::Docker::Artifact"
}

type ShellDimg struct {
	Version       string       `yaml:"_version"`
	BeforeInstall StageCommand `yaml:"_before_install"`
	BeforeSetup   StageCommand `yaml:"_before_setup"`
	Install       StageCommand `yaml:"_install"`
	Setup         StageCommand `yaml:"_setup"`
}

func (cfg *ShellDimg) GetRubyTypeTag() string {
	return "ruby/object:Dapp::Dimg::Config::Directive::Shell::Dimg"
}

type ShellArtifact struct {
	Version       string       `yaml:"_version"`
	BeforeInstall StageCommand `yaml:"_before_install"`
	BeforeSetup   StageCommand `yaml:"_before_setup"`
	Install       StageCommand `yaml:"_install"`
	Setup         StageCommand `yaml:"_setup"`
	BuildArtifact StageCommand `yaml:"_build_artifact"`
}

func (cfg *ShellArtifact) GetRubyTypeTag() string {
	return "ruby/object:Dapp::Dimg::Config::Directive::Shell::Artifact"
}

type StageCommand struct {
	Version string   `yaml:"_version"`
	Run     []string `yaml:"_run"`
}

func (cfg *StageCommand) GetRubyTypeTag() string {
	return "ruby/object:Dapp::Dimg::Config::Directive::GitArtifactLocal::Export::StageDependencies"
}

type Chef struct {
	Dimod      []string       `yaml:"_dimod"`
	Recipe     []string       `yaml:"_recipe"`
	Attributes ChefAttributes `yaml:"_attributes"`
}

func (cfg *Chef) GetRubyTypeTag() string {
	return "ruby/object:Dapp::Dimg::Config::Directive::Chef"
}

type ChefAttributes map[interface{}]interface{}

func (cfg *ChefAttributes) GetRubyTypeTag() string {
	return "ruby/hash:Dapp::Dimg::Config::Directive::Chef::Attributes"
}

type Artifact struct {
	Cwd          string       `yaml:"_cwd"`
	To           string       `yaml:"_to"`
	IncludePaths []string     `yaml:"_include_paths"`
	ExcludePaths []string     `yaml:"_exclude_paths"`
	Owner        string       `yaml:"_owner"`
	Group        string       `yaml:"_group"`
	Config       ArtifactDimg `yaml:"_config"`
	Before       string       `yaml:"_before"`
	After        string       `yaml:"_after"`
}

func (cfg *Artifact) GetRubyTypeTag() string {
	return "ruby/hash:Dapp::Dimg::Config::Directive::Artifact::Export"
}

type GitArtifact struct {
	Local  []GitArtifactLocal  `yaml:"_local"`
	Remote []GitArtifactRemote `yaml:"_remote"`
}

func (cfg *GitArtifact) GetRubyTypeTag() string {
	return "ruby/hash:Dapp::Dimg::Config::Directive::Dimg::InstanceMethods::GitArtifact"
}

type GitArtifactLocal struct {
	Export []GitArtifactLocalExport `yaml:"_export"`
}

func (cfg *GitArtifactLocal) GetRubyTypeTag() string {
	return "ruby/hash:Dapp::Dimg::Config::Directive::GitArtifactLocal"
}

type GitArtifactLocalExport struct {
	Cwd               string            `yaml:"_cwd"`
	To                string            `yaml:"_to"`
	IncludePaths      []string          `yaml:"_include_paths"`
	ExcludePaths      []string          `yaml:"_exclude_paths"`
	Owner             string            `yaml:"_owner"`
	Group             string            `yaml:"_group"`
	As                string            `yaml:"_as"`
	StageDependencies StageDependencies `yaml:"_stage_dependencies"`
}

func (cfg *GitArtifactLocalExport) GetRubyTypeTag() string {
	return "ruby/hash:Dapp::Dimg::Config::Directive::GitArtifactLocal::Export"
}

type StageDependencies struct {
	Install       []string `yaml:"_install"`
	Setup         []string `yaml:"_setup"`
	BeforeSetup   []string `yaml:"_before_setup"`
	BuildArtifact []string `yaml:"_build_artifact"`
}

func (cfg *StageDependencies) GetRubyTypeTag() string {
	return "ruby/hash:Dapp::Dimg::Config::Directive::Shell::Dimg::StageCommand"
}

type GitArtifactRemote struct {
	Export []GitArtifactRemoteExport `yaml:"_export"`
}

func (cfg *GitArtifactRemote) GetRubyTypeTag() string {
	return "ruby/hash:Dapp::Dimg::Config::Directive::GitArtifactRemote"
}

type GitArtifactRemoteExport struct {
	Cwd               string            `yaml:"_cwd"`
	To                string            `yaml:"_to"`
	IncludePaths      []string          `yaml:"_include_paths"`
	ExcludePaths      []string          `yaml:"_exclude_paths"`
	Owner             string            `yaml:"_owner"`
	Group             string            `yaml:"_group"`
	As                string            `yaml:"_as"`
	StageDependencies StageDependencies `yaml:"_stage_dependencies"`
	Url               string            `yaml:"_url"`
	Name              string            `yaml:"_name"`
	Branch            string            `yaml:"_branch"`
	Commit            string            `yaml:"_commit"`
}

func (cfg *GitArtifactRemoteExport) GetRubyTypeTag() string {
	return "ruby/hash:Dapp::Dimg::Config::Directive::GitArtifactRemote::Export"
}

type Mount struct {
	To   string `yaml:"_to"`
	From string `yaml:"_from"`
	Type string `yaml:"_type"`
}

func (cfg *Mount) GetRubyTypeTag() string {
	return "ruby/object:Dapp::Dimg::Config::Directive::Mount"
}
<|endoftext|>


<commit_before>package crank

import (
	"../devnull"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"strings"
	"syscall"
	"time"
)

const (
	PROCESS_NEW = ProcessState(iota)
	PROCESS_STARTING
	PROCESS_READY
	PROCESS_STOPPING
	PROCESS_STOPPED
)

type ProcessState int

type ExitStatus struct {
	code int
	err  error
}

type Process struct {
	*os.Process
	state    ProcessState
	config   *ProcessConfig
	socket   *os.File
	notify   *os.File
	onReady  chan bool
	onExited chan *Process
	shutdown chan bool
}

func NewProcess(config *ProcessConfig, socket *os.File, ready chan bool, exited chan *Process) *Process {
	return &Process{
		state:    PROCESS_NEW,
		config:   config,
		socket:   socket,
		onReady:  ready,
		onExited: exited,
		shutdown: make(chan bool),
	}
}

func (p *Process) String() string {
	return fmt.Sprintf("[%v] ", p.Pid)
}

func (p *Process) Log(format string, v ...interface{}) {
	log.Print(p.String(), fmt.Sprintf(format, v...))
}

func (p *Process) Start() {
	fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM, 0)
	if err != nil {
		log.Fatal("Process start failed: ", err)
	}
	notifyRcv := os.NewFile(uintptr(fds[0]), "<-|->") // File name is arbitrary
	notifySnd := os.NewFile(uintptr(fds[1]), "--({_O_})--")

	command := exec.Command(p.config.Command)

	// Inherit the environment with which crank was run
	command.Env = os.Environ()
	command.Env = append(command.Env, "LISTEN_FDS=1")
	command.Env = append(command.Env, "NOTIFY_FD=4")

	// Pass file descriptors to the process
	command.ExtraFiles = append(command.ExtraFiles, p.socket)  // 3: accept socket
	command.ExtraFiles = append(command.ExtraFiles, notifySnd) // 4: notify socket

	stdout, _ := command.StdoutPipe()
	stderr, _ := command.StderrPipe()
	command.Stdin = devnull.File

	// Start process
	if err = command.Start(); err != nil {
		p.state = PROCESS_STOPPED
		log.Fatal("Process start failed: ", err)
	}
	p.state = PROCESS_STARTING
	p.Process = command.Process
	p.Log("Process started")

	// Write stdout & stderr to the
	processLog := NewProcessLog(os.Stdout, p.Pid)
	go processLog.Copy(stdout)
	go processLog.Copy(stderr)

	// Close unused pipe-ends
	notifySnd.Close()

	ready := make(chan bool)
	exited := make(chan *ExitStatus)
	never := make(chan time.Time)

	// Read on pipe from child, and process commands
	go func() {
		defer notifyRcv.Close()

		var err error
		var command string
		var n int
		data := make([]byte, 4096)

		for {
			n, err = notifyRcv.Read(data)
			if err == io.EOF {
				return
			}
			if err != nil {
				p.Log("Error reading on pipe: %v", err)
				return
			}

			command = strings.TrimSpace(string(data[:n]))

			p.Log("Received command on pipe: %v", command)

			switch command {
			case "READY=1":
				ready <- true
			default:
				p.Log("Unknown command received: %v", command)
			}
		}
	}()

	// Goroutine catches process exit
	go func() {
		err := command.Wait()
		exited <- getExitStatusCode(err)
	}()

	go func() {
		// TODO handle timeouts correctly - don't reset on each for loop iteration
		for {
			var timeout <-chan time.Time
			switch p.state {
			case PROCESS_STARTING:
				if p.config.StartTimeout > 0 {
					timeout = time.After(p.config.StartTimeout * time.Millisecond)
				} else {
					timeout = never
				}

				select {
				case <-timeout:
					p.Log("Process did not start in time, killing")
					p.Kill()
				case <-ready:
					p.Log("Process transitioning to ready")
					p.state = PROCESS_READY
					p.onReady <- true
				case <-exited:
					p.Log("Process exited while starting")
					p.state = PROCESS_STOPPED
				case <-p.shutdown:
					p.Log("Stopping in the starting state, sending SIGTERM")
					p.sendSignal(syscall.SIGTERM)
					p.state = PROCESS_STOPPING
				}

			case PROCESS_READY:
				select {
				case <-ready:
					p.Log("Process started twice")
				case <-exited:
					p.Log("Process exited while running")
					p.state = PROCESS_STOPPED
				case <-p.shutdown:
					p.Log("Stopping in the running state, sending SIGTERM")
					p.sendSignal(syscall.SIGTERM)
					p.state = PROCESS_STOPPING
				}

			case PROCESS_STOPPING:
				if p.config.StopTimeout > 0 {
					timeout = time.After(p.config.StopTimeout * time.Millisecond)
				} else {
					timeout = never
				}

				select {
				case <-timeout:
					p.Log("Process did not stop in time, killing")
					p.Kill()
				case <-exited:
					p.state = PROCESS_STOPPED
				case <-p.shutdown:
					p.Log("Stopping in the stopping state, noop")
				}

			case PROCESS_STOPPED:
				p.Log("Process stopped")
				p.onExited <- p
				return

			default:
				panic(fmt.Sprintf("BUG, unknown state %v", p.state))
			}
		}
	}()
}

func (p *Process) Kill() {
	p.sendSignal(syscall.SIGKILL)
}

// Stop stops the process with increased aggressiveness
func (p *Process) Shutdown() {
	p.shutdown <- true
}

func (p *Process) sendSignal(sig syscall.Signal) {
	p.Log("Sending signal: %v", sig)
	p.Signal(sig)
}

func getExitStatusCode(err error) (s *ExitStatus) {
	s = &ExitStatus{-1, err}
	if err == nil {
		return
	}

	exiterr, ok := err.(*exec.ExitError)
	if !ok {
		return
	}
	status, ok := exiterr.Sys().(syscall.WaitStatus)
	if !ok {
		return
	}

	s.code = status.ExitStatus()
	s.err = nil

	return
}
<commit_msg>Handle timeout correctly in the process' state machine<commit_after>package crank

import (
	"../devnull"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"strings"
	"syscall"
	"time"
)

const (
	PROCESS_NEW = ProcessState(iota)
	PROCESS_STARTING
	PROCESS_READY
	PROCESS_STOPPING
	PROCESS_STOPPED
)

type ProcessState int

type ExitStatus struct {
	code int
	err  error
}

type Process struct {
	*os.Process
	state    ProcessState
	config   *ProcessConfig
	socket   *os.File
	notify   *os.File
	onReady  chan bool
	onExited chan *Process
	shutdown chan bool
}

func NewProcess(config *ProcessConfig, socket *os.File, ready chan bool, exited chan *Process) *Process {
	return &Process{
		state:    PROCESS_NEW,
		config:   config,
		socket:   socket,
		onReady:  ready,
		onExited: exited,
		shutdown: make(chan bool),
	}
}

func (p *Process) String() string {
	return fmt.Sprintf("[%v] ", p.Pid)
}

func (p *Process) Log(format string, v ...interface{}) {
	log.Print(p.String(), fmt.Sprintf(format, v...))
}

func (p *Process) Start() {
	fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM, 0)
	if err != nil {
		log.Fatal("Process start failed: ", err)
	}
	notifyRcv := os.NewFile(uintptr(fds[0]), "<-|->") // File name is arbitrary
	notifySnd := os.NewFile(uintptr(fds[1]), "--({_O_})--")

	command := exec.Command(p.config.Command)

	// Inherit the environment with which crank was run
	command.Env = os.Environ()
	command.Env = append(command.Env, "LISTEN_FDS=1")
	command.Env = append(command.Env, "NOTIFY_FD=4")

	// Pass file descriptors to the process
	command.ExtraFiles = append(command.ExtraFiles, p.socket)  // 3: accept socket
	command.ExtraFiles = append(command.ExtraFiles, notifySnd) // 4: notify socket

	stdout, _ := command.StdoutPipe()
	stderr, _ := command.StderrPipe()
	command.Stdin = devnull.File

	// Start process
	if err = command.Start(); err != nil {
		p.state = PROCESS_STOPPED
		log.Fatal("Process start failed: ", err)
	}
	p.state = PROCESS_STARTING
	p.Process = command.Process
	p.Log("Process started")

	// Write stdout & stderr to the
	processLog := NewProcessLog(os.Stdout, p.Pid)
	go processLog.Copy(stdout)
	go processLog.Copy(stderr)

	// Close unused pipe-ends
	notifySnd.Close()

	ready := make(chan bool)
	exited := make(chan *ExitStatus)
	never := make(chan time.Time)

	// Read on pipe from child, and process commands
	go func() {
		defer notifyRcv.Close()

		var err error
		var command string
		var n int
		data := make([]byte, 4096)

		for {
			n, err = notifyRcv.Read(data)
			if err == io.EOF {
				return
			}
			if err != nil {
				p.Log("Error reading on pipe: %v", err)
				return
			}

			command = strings.TrimSpace(string(data[:n]))

			p.Log("Received command on pipe: %v", command)

			switch command {
			case "READY=1":
				ready <- true
			default:
				p.Log("Unknown command received: %v", command)
			}
		}
	}()

	// Goroutine catches process exit
	go func() {
		err := command.Wait()
		exited <- getExitStatusCode(err)
	}()

	go func() {
		lastStateChange := time.Now()
		changeState := func(newState ProcessState) {
			lastStateChange = time.Now()
			p.state = newState
		}

		for {
			var timeout <-chan time.Time
			switch p.state {
			case PROCESS_STARTING:
				if p.config.StartTimeout > 0 {
					delay := (p.config.StartTimeout * time.Millisecond) -
						time.Now().Sub(lastStateChange)
					timeout = time.After(delay)
				} else {
					timeout = never
				}

				select {
				case <-timeout:
					p.Log("Process did not start in time, killing")
					p.Kill()
				case <-ready:
					p.Log("Process transitioning to ready")
					changeState(PROCESS_READY)
					p.onReady <- true
				case <-exited:
					p.Log("Process exited while starting")
					changeState(PROCESS_STOPPED)
				case <-p.shutdown:
					p.Log("Stopping in the starting state, sending SIGTERM")
					p.sendSignal(syscall.SIGTERM)
					changeState(PROCESS_STOPPING)
				}

			case PROCESS_READY:
				select {
				case <-ready:
					p.Log("Process started twice")
				case <-exited:
					p.Log("Process exited while running")
					changeState(PROCESS_STOPPED)
				case <-p.shutdown:
					p.Log("Stopping in the running state, sending SIGTERM")
					p.sendSignal(syscall.SIGTERM)
					changeState(PROCESS_STOPPING)
				}

			case PROCESS_STOPPING:
				if p.config.StopTimeout > 0 {
					delay := (p.config.StopTimeout * time.Millisecond) -
						time.Now().Sub(lastStateChange)
					timeout = time.After(delay)
				} else {
					timeout = never
				}

				select {
				case <-timeout:
					p.Log("Process did not stop in time, killing")
					p.Kill()
				case <-exited:
					changeState(PROCESS_STOPPED)
				case <-p.shutdown:
					p.Log("Stopping in the stopping state, noop")
				}

			case PROCESS_STOPPED:
				p.Log("Process stopped")
				p.onExited <- p
				return

			default:
				panic(fmt.Sprintf("BUG, unknown state %v", p.state))
			}
		}
	}()
}

func (p *Process) Kill() {
	p.sendSignal(syscall.SIGKILL)
}

// Stop stops the process with increased aggressiveness
func (p *Process) Shutdown() {
	p.shutdown <- true
}

func (p *Process) sendSignal(sig syscall.Signal) {
	p.Log("Sending signal: %v", sig)
	p.Signal(sig)
}

func getExitStatusCode(err error) (s *ExitStatus) {
	s = &ExitStatus{-1, err}
	if err == nil {
		return
	}

	exiterr, ok := err.(*exec.ExitError)
	if !ok {
		return
	}
	status, ok := exiterr.Sys().(syscall.WaitStatus)
	if !ok {
		return
	}

	s.code = status.ExitStatus()
	s.err = nil

	return
}
<|endoftext|>


<commit_before>/*
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 crinit

import (
	"io"

	"k8s.io/apiserver/pkg/util/flag"
	"k8s.io/client-go/tools/clientcmd"
	crinitinit "k8s.io/cluster-registry/pkg/crinit/init"

	"github.com/spf13/cobra"
)

// NewClusterregistryCommand creates the `clusterregistry` command.
func NewClusterregistryCommand(out io.Writer, defaultServerImage, defaultEtcdImage string) *cobra.Command {
	cmds := &cobra.Command{
		Use:   "clusterregistry",
		Short: "clusterregistry runs a cluster registry",
		Long:  "clusterregistry runs a cluster registry.",
		Run:   runHelp,
	}

	// From this point and forward we get warnings on flags that contain "_" separators
	cmds.SetGlobalNormalizationFunc(flag.WarnWordSepNormalizeFunc)

	cmds.AddCommand(crinitinit.NewCmdInit(out, clientcmd.NewDefaultPathOptions(), defaultServerImage, defaultEtcdImage))

	return cmds
}

func runHelp(cmd *cobra.Command, args []string) {
	cmd.Help()
}
<commit_msg>Fix the crinit command descriptions.<commit_after>/*
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 crinit

import (
	"io"

	"k8s.io/apiserver/pkg/util/flag"
	"k8s.io/client-go/tools/clientcmd"
	crinitinit "k8s.io/cluster-registry/pkg/crinit/init"

	"github.com/spf13/cobra"
)

// NewClusterregistryCommand creates the `clusterregistry` command.
func NewClusterregistryCommand(out io.Writer, defaultServerImage, defaultEtcdImage string) *cobra.Command {
	cmds := &cobra.Command{
		Use:   "crinit",
		Short: "crinit runs a cluster registry in a Kubernetes cluster",
		Long:  "crinit bootstraps and runs a cluster registry as a Deployment in an existing Kubernetes cluster.",
		Run:   runHelp,
	}

	// From this point and forward we get warnings on flags that contain "_" separators
	cmds.SetGlobalNormalizationFunc(flag.WarnWordSepNormalizeFunc)

	cmds.AddCommand(crinitinit.NewCmdInit(out, clientcmd.NewDefaultPathOptions(), defaultServerImage, defaultEtcdImage))

	return cmds
}

func runHelp(cmd *cobra.Command, args []string) {
	cmd.Help()
}
<|endoftext|>


<commit_before>// +build darwin freebsd dragonfly openbsd

/*
 * Minio Cloud Storage, (C) 2017 Minio, Inc.
 *
 * 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 disk

// getFSType returns the filesystem type of the underlying mounted filesystem
func getFSType(fstype [16]int8) string {
	return b2s(fstype[:])
}
<commit_msg>Add b2s method on pkg/disk/type_bsd.go (#5036)<commit_after>// +build darwin freebsd dragonfly openbsd

/*
 * Minio Cloud Storage, (C) 2017 Minio, Inc.
 *
 * 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 disk

// getFSType returns the filesystem type of the underlying mounted filesystem
func getFSType(fstype [16]int8) string {
	b := make([]byte, len(fstype[:]))
	for i, v := range fstype[:] {
		b[i] = byte(v)
	}
	return string(b)
}
<|endoftext|>


<commit_before>/*
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 engine

import (
	"fmt"
	"log"
	"path"
	"path/filepath"
	"regexp"
	"sort"
	"strings"
	"text/template"

	"github.com/pkg/errors"

	"helm.sh/helm/pkg/chart"
	"helm.sh/helm/pkg/chartutil"
)

// Engine is an implementation of 'cmd/tiller/environment'.Engine that uses Go templates.
type Engine struct {
	// If strict is enabled, template rendering will fail if a template references
	// a value that was not passed in.
	Strict bool
	// In LintMode, some 'required' template values may be missing, so don't fail
	LintMode bool
}

// Render takes a chart, optional values, and value overrides, and attempts to render the Go templates.
//
// Render can be called repeatedly on the same engine.
//
// This will look in the chart's 'templates' data (e.g. the 'templates/' directory)
// and attempt to render the templates there using the values passed in.
//
// Values are scoped to their templates. A dependency template will not have
// access to the values set for its parent. If chart "foo" includes chart "bar",
// "bar" will not have access to the values for "foo".
//
// Values should be prepared with something like `chartutils.ReadValues`.
//
// Values are passed through the templates according to scope. If the top layer
// chart includes the chart foo, which includes the chart bar, the values map
// will be examined for a table called "foo". If "foo" is found in vals,
// that section of the values will be passed into the "foo" chart. And if that
// section contains a value named "bar", that value will be passed on to the
// bar chart during render time.
func (e Engine) Render(chrt *chart.Chart, values chartutil.Values) (map[string]string, error) {
	tmap := allTemplates(chrt, values)
	return e.render(tmap)
}

// Render takes a chart, optional values, and value overrides, and attempts to
// render the Go templates using the default options.
func Render(chrt *chart.Chart, values chartutil.Values) (map[string]string, error) {
	return new(Engine).Render(chrt, values)
}

// renderable is an object that can be rendered.
type renderable struct {
	// tpl is the current template.
	tpl string
	// vals are the values to be supplied to the template.
	vals chartutil.Values
	// namespace prefix to the templates of the current chart
	basePath string
}

var warnRegex = regexp.MustCompile(`HELM\[(.*)\]HELM`)

func warnWrap(warn string) string {
	return fmt.Sprintf("HELM[%s]HELM", warn)
}

// initFunMap creates the Engine's FuncMap and adds context-specific functions.
func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]renderable) {
	funcMap := funcMap()

	// Add the 'include' function here so we can close over t.
	funcMap["include"] = func(name string, data interface{}) (string, error) {
		var buf strings.Builder
		err := t.ExecuteTemplate(&buf, name, data)
		return buf.String(), err
	}

	// Add the 'tpl' function here
	funcMap["tpl"] = func(tpl string, vals chartutil.Values) (string, error) {
		basePath, err := vals.PathValue("Template.BasePath")
		if err != nil {
			return "", errors.Wrapf(err, "cannot retrieve Template.Basepath from values inside tpl function: %s", tpl)
		}

		templateName, err := vals.PathValue("Template.Name")
		if err != nil {
			return "", errors.Wrapf(err, "cannot retrieve Template.Name from values inside tpl function: %s", tpl)
		}

		templates := map[string]renderable{
			templateName.(string): {
				tpl:      tpl,
				vals:     vals,
				basePath: basePath.(string),
			},
		}

		result, err := e.renderWithReferences(templates, referenceTpls)
		if err != nil {
			return "", errors.Wrapf(err, "error during tpl function execution for %q", tpl)
		}
		return result[templateName.(string)], nil
	}

	// Add the `required` function here so we can use lintMode
	funcMap["required"] = func(warn string, val interface{}) (interface{}, error) {
		if val == nil {
			if e.LintMode {
				// Don't fail on missing required values when linting
				log.Printf("[INFO] Missing required value: %s", warn)
				return "", nil
			}
			return val, errors.Errorf(warnWrap(warn))
		} else if _, ok := val.(string); ok {
			if val == "" {
				if e.LintMode {
					// Don't fail on missing required values when linting
					log.Printf("[INFO] Missing required value: %s", warn)
					return "", nil
				}
				return val, errors.Errorf(warnWrap(warn))
			}
		}
		return val, nil
	}

	t.Funcs(funcMap)
}

// render takes a map of templates/values and renders them.
func (e Engine) render(tpls map[string]renderable) (map[string]string, error) {
	return e.renderWithReferences(tpls, tpls)
}

// renderWithReferences takes a map of templates/values to render, and a map of
// templates which can be referenced within them.
func (e Engine) renderWithReferences(tpls, referenceTpls map[string]renderable) (rendered map[string]string, err error) {
	// Basically, what we do here is start with an empty parent template and then
	// build up a list of templates -- one for each file. Once all of the templates
	// have been parsed, we loop through again and execute every template.
	//
	// The idea with this process is to make it possible for more complex templates
	// to share common blocks, but to make the entire thing feel like a file-based
	// template engine.
	defer func() {
		if r := recover(); r != nil {
			err = errors.Errorf("rendering template failed: %v", r)
		}
	}()
	t := template.New("gotpl")
	if e.Strict {
		t.Option("missingkey=error")
	} else {
		// Not that zero will attempt to add default values for types it knows,
		// but will still emit <no value> for others. We mitigate that later.
		t.Option("missingkey=zero")
	}

	e.initFunMap(t, referenceTpls)

	// We want to parse the templates in a predictable order. The order favors
	// higher-level (in file system) templates over deeply nested templates.
	keys := sortTemplates(tpls)

	for _, filename := range keys {
		r := tpls[filename]
		if _, err := t.New(filename).Parse(r.tpl); err != nil {
			return map[string]string{}, cleanupParseError(filename, err)
		}
	}

	// Adding the reference templates to the template context
	// so they can be referenced in the tpl function
	for filename, r := range referenceTpls {
		if t.Lookup(filename) == nil {
			if _, err := t.New(filename).Parse(r.tpl); err != nil {
				return map[string]string{}, cleanupParseError(filename, err)
			}
		}
	}

	rendered = make(map[string]string, len(keys))
	for _, filename := range keys {
		// Don't render partials. We don't care out the direct output of partials.
		// They are only included from other templates.
		if strings.HasPrefix(path.Base(filename), "_") {
			continue
		}
		// At render time, add information about the template that is being rendered.
		vals := tpls[filename].vals
		vals["Template"] = chartutil.Values{"Name": filename, "BasePath": tpls[filename].basePath}
		var buf strings.Builder
		if err := t.ExecuteTemplate(&buf, filename, vals); err != nil {
			return map[string]string{}, cleanupExecError(filename, err)
		}

		// Work around the issue where Go will emit "<no value>" even if Options(missing=zero)
		// is set. Since missing=error will never get here, we do not need to handle
		// the Strict case.
		f := &chart.File{
			Name: strings.ReplaceAll(filename, "/templates", "/manifests"),
			Data: []byte(strings.ReplaceAll(buf.String(), "<no value>", "")),
		}
		rendered[filename] = string(f.Data)
	}

	return rendered, nil
}

func cleanupParseError(filename string, err error) error {
	tokens := strings.Split(err.Error(), ": ")
	if len(tokens) == 1 {
		// This might happen if a non-templating error occurs
		return fmt.Errorf("parse error in (%s): %s", filename, err)
	}
	// The first token is "template"
	// The second token is either "filename:lineno" or "filename:lineNo:columnNo"
	location := tokens[1]
	// The remaining tokens make up a stacktrace-like chain, ending with the relevant error
	errMsg := tokens[len(tokens)-1]
	return fmt.Errorf("parse error at (%s): %s", string(location), errMsg)
}

func cleanupExecError(filename string, err error) error {
	if _, isExecError := err.(template.ExecError); !isExecError {
		return err
	}

	tokens := strings.SplitN(err.Error(), ": ", 3)
	if len(tokens) != 3 {
		// This might happen if a non-templating error occurs
		return fmt.Errorf("execution error in (%s): %s", filename, err)
	}

	// The first token is "template"
	// The second token is either "filename:lineno" or "filename:lineNo:columnNo"
	location := tokens[1]

	parts := warnRegex.FindStringSubmatch(tokens[2])
	if len(parts) >= 2 {
		return fmt.Errorf("execution error at (%s): %s", string(location), parts[1])
	}

	return err
}

func sortTemplates(tpls map[string]renderable) []string {
	keys := make([]string, len(tpls))
	i := 0
	for key := range tpls {
		keys[i] = key
		i++
	}
	sort.Sort(sort.Reverse(byPathLen(keys)))
	return keys
}

type byPathLen []string

func (p byPathLen) Len() int      { return len(p) }
func (p byPathLen) Swap(i, j int) { p[j], p[i] = p[i], p[j] }
func (p byPathLen) Less(i, j int) bool {
	a, b := p[i], p[j]
	ca, cb := strings.Count(a, "/"), strings.Count(b, "/")
	if ca == cb {
		return strings.Compare(a, b) == -1
	}
	return ca < cb
}

// allTemplates returns all templates for a chart and its dependencies.
//
// As it goes, it also prepares the values in a scope-sensitive manner.
func allTemplates(c *chart.Chart, vals chartutil.Values) map[string]renderable {
	templates := make(map[string]renderable)
	recAllTpls(c, templates, vals)
	return templates
}

// recAllTpls recurses through the templates in a chart.
//
// As it recurses, it also sets the values to be appropriate for the template
// scope.
func recAllTpls(c *chart.Chart, templates map[string]renderable, vals chartutil.Values) {
	next := map[string]interface{}{
		"Chart":        c.Metadata,
		"Files":        newFiles(c.Files),
		"Release":      vals["Release"],
		"Capabilities": vals["Capabilities"],
		"Values":       make(chartutil.Values),
	}

	// If there is a {{.Values.ThisChart}} in the parent metadata,
	// copy that into the {{.Values}} for this template.
	if c.IsRoot() {
		next["Values"] = vals["Values"]
	} else if vs, err := vals.Table("Values." + c.Name()); err == nil {
		next["Values"] = vs
	}

	for _, child := range c.Dependencies() {
		recAllTpls(child, templates, next)
	}

	newParentID := c.ChartFullPath()
	for _, t := range c.Templates {
		if !isTemplateValid(c, t.Name) {
			continue
		}
		templates[path.Join(newParentID, t.Name)] = renderable{
			tpl:      string(t.Data),
			vals:     next,
			basePath: path.Join(newParentID, "templates"),
		}
	}
}

// isTemplateValid returns true if the template is valid for the chart type
func isTemplateValid(ch *chart.Chart, templateName string) bool {
	if isLibraryChart(ch) {
		return strings.HasPrefix(filepath.Base(templateName), "_")
	}
	return true
}

// isLibraryChart returns true if the chart is a library chart
func isLibraryChart(c *chart.Chart) bool {
	return strings.EqualFold(c.Metadata.Type, "library")
}
<commit_msg>Switch to a more unique delimiter for template execution errors<commit_after>/*
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 engine

import (
	"fmt"
	"log"
	"path"
	"path/filepath"
	"regexp"
	"sort"
	"strings"
	"text/template"

	"github.com/pkg/errors"

	"helm.sh/helm/pkg/chart"
	"helm.sh/helm/pkg/chartutil"
)

// Engine is an implementation of 'cmd/tiller/environment'.Engine that uses Go templates.
type Engine struct {
	// If strict is enabled, template rendering will fail if a template references
	// a value that was not passed in.
	Strict bool
	// In LintMode, some 'required' template values may be missing, so don't fail
	LintMode bool
}

// Render takes a chart, optional values, and value overrides, and attempts to render the Go templates.
//
// Render can be called repeatedly on the same engine.
//
// This will look in the chart's 'templates' data (e.g. the 'templates/' directory)
// and attempt to render the templates there using the values passed in.
//
// Values are scoped to their templates. A dependency template will not have
// access to the values set for its parent. If chart "foo" includes chart "bar",
// "bar" will not have access to the values for "foo".
//
// Values should be prepared with something like `chartutils.ReadValues`.
//
// Values are passed through the templates according to scope. If the top layer
// chart includes the chart foo, which includes the chart bar, the values map
// will be examined for a table called "foo". If "foo" is found in vals,
// that section of the values will be passed into the "foo" chart. And if that
// section contains a value named "bar", that value will be passed on to the
// bar chart during render time.
func (e Engine) Render(chrt *chart.Chart, values chartutil.Values) (map[string]string, error) {
	tmap := allTemplates(chrt, values)
	return e.render(tmap)
}

// Render takes a chart, optional values, and value overrides, and attempts to
// render the Go templates using the default options.
func Render(chrt *chart.Chart, values chartutil.Values) (map[string]string, error) {
	return new(Engine).Render(chrt, values)
}

// renderable is an object that can be rendered.
type renderable struct {
	// tpl is the current template.
	tpl string
	// vals are the values to be supplied to the template.
	vals chartutil.Values
	// namespace prefix to the templates of the current chart
	basePath string
}

const warnStartDelim = "HELM_ERR_START"
const warnEndDelim = "HELM_ERR_END"
var warnRegex = regexp.MustCompile(warnStartDelim + `(.*)` + warnEndDelim)

func warnWrap(warn string) string {
	return warnStartDelim + warn + warnEndDelim
}

// initFunMap creates the Engine's FuncMap and adds context-specific functions.
func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]renderable) {
	funcMap := funcMap()

	// Add the 'include' function here so we can close over t.
	funcMap["include"] = func(name string, data interface{}) (string, error) {
		var buf strings.Builder
		err := t.ExecuteTemplate(&buf, name, data)
		return buf.String(), err
	}

	// Add the 'tpl' function here
	funcMap["tpl"] = func(tpl string, vals chartutil.Values) (string, error) {
		basePath, err := vals.PathValue("Template.BasePath")
		if err != nil {
			return "", errors.Wrapf(err, "cannot retrieve Template.Basepath from values inside tpl function: %s", tpl)
		}

		templateName, err := vals.PathValue("Template.Name")
		if err != nil {
			return "", errors.Wrapf(err, "cannot retrieve Template.Name from values inside tpl function: %s", tpl)
		}

		templates := map[string]renderable{
			templateName.(string): {
				tpl:      tpl,
				vals:     vals,
				basePath: basePath.(string),
			},
		}

		result, err := e.renderWithReferences(templates, referenceTpls)
		if err != nil {
			return "", errors.Wrapf(err, "error during tpl function execution for %q", tpl)
		}
		return result[templateName.(string)], nil
	}

	// Add the `required` function here so we can use lintMode
	funcMap["required"] = func(warn string, val interface{}) (interface{}, error) {
		if val == nil {
			if e.LintMode {
				// Don't fail on missing required values when linting
				log.Printf("[INFO] Missing required value: %s", warn)
				return "", nil
			}
			return val, errors.Errorf(warnWrap(warn))
		} else if _, ok := val.(string); ok {
			if val == "" {
				if e.LintMode {
					// Don't fail on missing required values when linting
					log.Printf("[INFO] Missing required value: %s", warn)
					return "", nil
				}
				return val, errors.Errorf(warnWrap(warn))
			}
		}
		return val, nil
	}

	t.Funcs(funcMap)
}

// render takes a map of templates/values and renders them.
func (e Engine) render(tpls map[string]renderable) (map[string]string, error) {
	return e.renderWithReferences(tpls, tpls)
}

// renderWithReferences takes a map of templates/values to render, and a map of
// templates which can be referenced within them.
func (e Engine) renderWithReferences(tpls, referenceTpls map[string]renderable) (rendered map[string]string, err error) {
	// Basically, what we do here is start with an empty parent template and then
	// build up a list of templates -- one for each file. Once all of the templates
	// have been parsed, we loop through again and execute every template.
	//
	// The idea with this process is to make it possible for more complex templates
	// to share common blocks, but to make the entire thing feel like a file-based
	// template engine.
	defer func() {
		if r := recover(); r != nil {
			err = errors.Errorf("rendering template failed: %v", r)
		}
	}()
	t := template.New("gotpl")
	if e.Strict {
		t.Option("missingkey=error")
	} else {
		// Not that zero will attempt to add default values for types it knows,
		// but will still emit <no value> for others. We mitigate that later.
		t.Option("missingkey=zero")
	}

	e.initFunMap(t, referenceTpls)

	// We want to parse the templates in a predictable order. The order favors
	// higher-level (in file system) templates over deeply nested templates.
	keys := sortTemplates(tpls)

	for _, filename := range keys {
		r := tpls[filename]
		if _, err := t.New(filename).Parse(r.tpl); err != nil {
			return map[string]string{}, cleanupParseError(filename, err)
		}
	}

	// Adding the reference templates to the template context
	// so they can be referenced in the tpl function
	for filename, r := range referenceTpls {
		if t.Lookup(filename) == nil {
			if _, err := t.New(filename).Parse(r.tpl); err != nil {
				return map[string]string{}, cleanupParseError(filename, err)
			}
		}
	}

	rendered = make(map[string]string, len(keys))
	for _, filename := range keys {
		// Don't render partials. We don't care out the direct output of partials.
		// They are only included from other templates.
		if strings.HasPrefix(path.Base(filename), "_") {
			continue
		}
		// At render time, add information about the template that is being rendered.
		vals := tpls[filename].vals
		vals["Template"] = chartutil.Values{"Name": filename, "BasePath": tpls[filename].basePath}
		var buf strings.Builder
		if err := t.ExecuteTemplate(&buf, filename, vals); err != nil {
			return map[string]string{}, cleanupExecError(filename, err)
		}

		// Work around the issue where Go will emit "<no value>" even if Options(missing=zero)
		// is set. Since missing=error will never get here, we do not need to handle
		// the Strict case.
		f := &chart.File{
			Name: strings.ReplaceAll(filename, "/templates", "/manifests"),
			Data: []byte(strings.ReplaceAll(buf.String(), "<no value>", "")),
		}
		rendered[filename] = string(f.Data)
	}

	return rendered, nil
}

func cleanupParseError(filename string, err error) error {
	tokens := strings.Split(err.Error(), ": ")
	if len(tokens) == 1 {
		// This might happen if a non-templating error occurs
		return fmt.Errorf("parse error in (%s): %s", filename, err)
	}
	// The first token is "template"
	// The second token is either "filename:lineno" or "filename:lineNo:columnNo"
	location := tokens[1]
	// The remaining tokens make up a stacktrace-like chain, ending with the relevant error
	errMsg := tokens[len(tokens)-1]
	return fmt.Errorf("parse error at (%s): %s", string(location), errMsg)
}

func cleanupExecError(filename string, err error) error {
	if _, isExecError := err.(template.ExecError); !isExecError {
		return err
	}

	tokens := strings.SplitN(err.Error(), ": ", 3)
	if len(tokens) != 3 {
		// This might happen if a non-templating error occurs
		return fmt.Errorf("execution error in (%s): %s", filename, err)
	}

	// The first token is "template"
	// The second token is either "filename:lineno" or "filename:lineNo:columnNo"
	location := tokens[1]

	parts := warnRegex.FindStringSubmatch(tokens[2])
	if len(parts) >= 2 {
		return fmt.Errorf("execution error at (%s): %s", string(location), parts[1])
	}

	return err
}

func sortTemplates(tpls map[string]renderable) []string {
	keys := make([]string, len(tpls))
	i := 0
	for key := range tpls {
		keys[i] = key
		i++
	}
	sort.Sort(sort.Reverse(byPathLen(keys)))
	return keys
}

type byPathLen []string

func (p byPathLen) Len() int      { return len(p) }
func (p byPathLen) Swap(i, j int) { p[j], p[i] = p[i], p[j] }
func (p byPathLen) Less(i, j int) bool {
	a, b := p[i], p[j]
	ca, cb := strings.Count(a, "/"), strings.Count(b, "/")
	if ca == cb {
		return strings.Compare(a, b) == -1
	}
	return ca < cb
}

// allTemplates returns all templates for a chart and its dependencies.
//
// As it goes, it also prepares the values in a scope-sensitive manner.
func allTemplates(c *chart.Chart, vals chartutil.Values) map[string]renderable {
	templates := make(map[string]renderable)
	recAllTpls(c, templates, vals)
	return templates
}

// recAllTpls recurses through the templates in a chart.
//
// As it recurses, it also sets the values to be appropriate for the template
// scope.
func recAllTpls(c *chart.Chart, templates map[string]renderable, vals chartutil.Values) {
	next := map[string]interface{}{
		"Chart":        c.Metadata,
		"Files":        newFiles(c.Files),
		"Release":      vals["Release"],
		"Capabilities": vals["Capabilities"],
		"Values":       make(chartutil.Values),
	}

	// If there is a {{.Values.ThisChart}} in the parent metadata,
	// copy that into the {{.Values}} for this template.
	if c.IsRoot() {
		next["Values"] = vals["Values"]
	} else if vs, err := vals.Table("Values." + c.Name()); err == nil {
		next["Values"] = vs
	}

	for _, child := range c.Dependencies() {
		recAllTpls(child, templates, next)
	}

	newParentID := c.ChartFullPath()
	for _, t := range c.Templates {
		if !isTemplateValid(c, t.Name) {
			continue
		}
		templates[path.Join(newParentID, t.Name)] = renderable{
			tpl:      string(t.Data),
			vals:     next,
			basePath: path.Join(newParentID, "templates"),
		}
	}
}

// isTemplateValid returns true if the template is valid for the chart type
func isTemplateValid(ch *chart.Chart, templateName string) bool {
	if isLibraryChart(ch) {
		return strings.HasPrefix(filepath.Base(templateName), "_")
	}
	return true
}

// isLibraryChart returns true if the chart is a library chart
func isLibraryChart(c *chart.Chart) bool {
	return strings.EqualFold(c.Metadata.Type, "library")
}
<|endoftext|>


<commit_before>// Copyright 2015 CNI 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 ip

import (
	"crypto/rand"
	"errors"
	"fmt"
	"net"
	"os"

	"github.com/containernetworking/plugins/pkg/ns"
	"github.com/containernetworking/plugins/pkg/utils/hwaddr"
	"github.com/vishvananda/netlink"
)

var (
	ErrLinkNotFound = errors.New("link not found")
)

func makeVethPair(name, peer string, mtu int) (netlink.Link, error) {
	veth := &netlink.Veth{
		LinkAttrs: netlink.LinkAttrs{
			Name:  name,
			Flags: net.FlagUp,
			MTU:   mtu,
		},
		PeerName: peer,
	}
	if err := netlink.LinkAdd(veth); err != nil {
		return nil, err
	}

	return veth, nil
}

func peerExists(name string) bool {
	if _, err := netlink.LinkByName(name); err != nil {
		return false
	}
	return true
}

func makeVeth(name string, mtu int) (peerName string, veth netlink.Link, err error) {
	for i := 0; i < 10; i++ {
		peerName, err = RandomVethName()
		if err != nil {
			return
		}

		veth, err = makeVethPair(name, peerName, mtu)
		switch {
		case err == nil:
			return

		case os.IsExist(err):
			if peerExists(peerName) {
				continue
			}
			err = fmt.Errorf("container veth name provided (%v) already exists", name)
			return

		default:
			err = fmt.Errorf("failed to make veth pair: %v", err)
			return
		}
	}

	// should really never be hit
	err = fmt.Errorf("failed to find a unique veth name")
	return
}

// RandomVethName returns string "veth" with random prefix (hashed from entropy)
func RandomVethName() (string, error) {
	entropy := make([]byte, 4)
	_, err := rand.Reader.Read(entropy)
	if err != nil {
		return "", fmt.Errorf("failed to generate random veth name: %v", err)
	}

	// NetworkManager (recent versions) will ignore veth devices that start with "veth"
	return fmt.Sprintf("veth%x", entropy), nil
}

func RenameLink(curName, newName string) error {
	link, err := netlink.LinkByName(curName)
	if err == nil {
		err = netlink.LinkSetName(link, newName)
	}
	return err
}

func ifaceFromNetlinkLink(l netlink.Link) net.Interface {
	a := l.Attrs()
	return net.Interface{
		Index:        a.Index,
		MTU:          a.MTU,
		Name:         a.Name,
		HardwareAddr: a.HardwareAddr,
		Flags:        a.Flags,
	}
}

// SetupVeth sets up a pair of virtual ethernet devices.
// Call SetupVeth from inside the container netns.  It will create both veth
// devices and move the host-side veth into the provided hostNS namespace.
// On success, SetupVeth returns (hostVeth, containerVeth, nil)
func SetupVeth(contVethName string, mtu int, hostNS ns.NetNS) (net.Interface, net.Interface, error) {
	hostVethName, contVeth, err := makeVeth(contVethName, mtu)
	if err != nil {
		return net.Interface{}, net.Interface{}, err
	}

	if err = netlink.LinkSetUp(contVeth); err != nil {
		return net.Interface{}, net.Interface{}, fmt.Errorf("failed to set %q up: %v", contVethName, err)
	}

	hostVeth, err := netlink.LinkByName(hostVethName)
	if err != nil {
		return net.Interface{}, net.Interface{}, fmt.Errorf("failed to lookup %q: %v", hostVethName, err)
	}

	if err = netlink.LinkSetNsFd(hostVeth, int(hostNS.Fd())); err != nil {
		return net.Interface{}, net.Interface{}, fmt.Errorf("failed to move veth to host netns: %v", err)
	}

	err = hostNS.Do(func(_ ns.NetNS) error {
		hostVeth, err = netlink.LinkByName(hostVethName)
		if err != nil {
			return fmt.Errorf("failed to lookup %q in %q: %v", hostVethName, hostNS.Path(), err)
		}

		if err = netlink.LinkSetUp(hostVeth); err != nil {
			return fmt.Errorf("failed to set %q up: %v", hostVethName, err)
		}
		return nil
	})
	if err != nil {
		return net.Interface{}, net.Interface{}, err
	}
	return ifaceFromNetlinkLink(hostVeth), ifaceFromNetlinkLink(contVeth), nil
}

// DelLinkByName removes an interface link.
func DelLinkByName(ifName string) error {
	iface, err := netlink.LinkByName(ifName)
	if err != nil {
		if err.Error() == "Link not found" {
			return ErrLinkNotFound
		}
		return fmt.Errorf("failed to lookup %q: %v", ifName, err)
	}

	if err = netlink.LinkDel(iface); err != nil {
		return fmt.Errorf("failed to delete %q: %v", ifName, err)
	}

	return nil
}

// DelLinkByNameAddr remove an interface and returns its addresses
func DelLinkByNameAddr(ifName string) ([]*net.IPNet, error) {
	iface, err := netlink.LinkByName(ifName)
	if err != nil {
		if err != nil && err.Error() == "Link not found" {
			return nil, ErrLinkNotFound
		}
		return nil, fmt.Errorf("failed to lookup %q: %v", ifName, err)
	}

	addrs, err := netlink.AddrList(iface, netlink.FAMILY_ALL)
	if err != nil {
		return nil, fmt.Errorf("failed to get IP addresses for %q: %v", ifName, err)
	}

	if err = netlink.LinkDel(iface); err != nil {
		return nil, fmt.Errorf("failed to delete %q: %v", ifName, err)
	}

	out := []*net.IPNet{}
	for _, addr := range addrs {
		if addr.IP.IsGlobalUnicast() {
			out = append(out, addr.IPNet)
		}
	}

	return out, nil
}

func SetHWAddrByIP(ifName string, ip4 net.IP, ip6 net.IP) error {
	iface, err := netlink.LinkByName(ifName)
	if err != nil {
		return fmt.Errorf("failed to lookup %q: %v", ifName, err)
	}

	switch {
	case ip4 == nil && ip6 == nil:
		return fmt.Errorf("neither ip4 or ip6 specified")

	case ip4 != nil:
		{
			hwAddr, err := hwaddr.GenerateHardwareAddr4(ip4, hwaddr.PrivateMACPrefix)
			if err != nil {
				return fmt.Errorf("failed to generate hardware addr: %v", err)
			}
			if err = netlink.LinkSetHardwareAddr(iface, hwAddr); err != nil {
				return fmt.Errorf("failed to add hardware addr to %q: %v", ifName, err)
			}
		}
	case ip6 != nil:
		// TODO: IPv6
	}

	return nil
}
<commit_msg>pkg/ip: re-fetch the created link to return creation-time parameters<commit_after>// Copyright 2015 CNI 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 ip

import (
	"crypto/rand"
	"errors"
	"fmt"
	"net"
	"os"

	"github.com/containernetworking/plugins/pkg/ns"
	"github.com/containernetworking/plugins/pkg/utils/hwaddr"
	"github.com/vishvananda/netlink"
)

var (
	ErrLinkNotFound = errors.New("link not found")
)

func makeVethPair(name, peer string, mtu int) (netlink.Link, error) {
	veth := &netlink.Veth{
		LinkAttrs: netlink.LinkAttrs{
			Name:  name,
			Flags: net.FlagUp,
			MTU:   mtu,
		},
		PeerName: peer,
	}
	if err := netlink.LinkAdd(veth); err != nil {
		return nil, err
	}
	// Re-fetch the link to get its creation-time parameters, e.g. index and mac
	veth2, err := netlink.LinkByName(name)
	if err != nil {
		netlink.LinkDel(veth) // try and clean up the link if possible.
		return nil, err
	}

	return veth2, nil
}

func peerExists(name string) bool {
	if _, err := netlink.LinkByName(name); err != nil {
		return false
	}
	return true
}

func makeVeth(name string, mtu int) (peerName string, veth netlink.Link, err error) {
	for i := 0; i < 10; i++ {
		peerName, err = RandomVethName()
		if err != nil {
			return
		}

		veth, err = makeVethPair(name, peerName, mtu)
		switch {
		case err == nil:
			return

		case os.IsExist(err):
			if peerExists(peerName) {
				continue
			}
			err = fmt.Errorf("container veth name provided (%v) already exists", name)
			return

		default:
			err = fmt.Errorf("failed to make veth pair: %v", err)
			return
		}
	}

	// should really never be hit
	err = fmt.Errorf("failed to find a unique veth name")
	return
}

// RandomVethName returns string "veth" with random prefix (hashed from entropy)
func RandomVethName() (string, error) {
	entropy := make([]byte, 4)
	_, err := rand.Reader.Read(entropy)
	if err != nil {
		return "", fmt.Errorf("failed to generate random veth name: %v", err)
	}

	// NetworkManager (recent versions) will ignore veth devices that start with "veth"
	return fmt.Sprintf("veth%x", entropy), nil
}

func RenameLink(curName, newName string) error {
	link, err := netlink.LinkByName(curName)
	if err == nil {
		err = netlink.LinkSetName(link, newName)
	}
	return err
}

func ifaceFromNetlinkLink(l netlink.Link) net.Interface {
	a := l.Attrs()
	return net.Interface{
		Index:        a.Index,
		MTU:          a.MTU,
		Name:         a.Name,
		HardwareAddr: a.HardwareAddr,
		Flags:        a.Flags,
	}
}

// SetupVeth sets up a pair of virtual ethernet devices.
// Call SetupVeth from inside the container netns.  It will create both veth
// devices and move the host-side veth into the provided hostNS namespace.
// On success, SetupVeth returns (hostVeth, containerVeth, nil)
func SetupVeth(contVethName string, mtu int, hostNS ns.NetNS) (net.Interface, net.Interface, error) {
	hostVethName, contVeth, err := makeVeth(contVethName, mtu)
	if err != nil {
		return net.Interface{}, net.Interface{}, err
	}

	if err = netlink.LinkSetUp(contVeth); err != nil {
		return net.Interface{}, net.Interface{}, fmt.Errorf("failed to set %q up: %v", contVethName, err)
	}

	hostVeth, err := netlink.LinkByName(hostVethName)
	if err != nil {
		return net.Interface{}, net.Interface{}, fmt.Errorf("failed to lookup %q: %v", hostVethName, err)
	}

	if err = netlink.LinkSetNsFd(hostVeth, int(hostNS.Fd())); err != nil {
		return net.Interface{}, net.Interface{}, fmt.Errorf("failed to move veth to host netns: %v", err)
	}

	err = hostNS.Do(func(_ ns.NetNS) error {
		hostVeth, err = netlink.LinkByName(hostVethName)
		if err != nil {
			return fmt.Errorf("failed to lookup %q in %q: %v", hostVethName, hostNS.Path(), err)
		}

		if err = netlink.LinkSetUp(hostVeth); err != nil {
			return fmt.Errorf("failed to set %q up: %v", hostVethName, err)
		}
		return nil
	})
	if err != nil {
		return net.Interface{}, net.Interface{}, err
	}
	return ifaceFromNetlinkLink(hostVeth), ifaceFromNetlinkLink(contVeth), nil
}

// DelLinkByName removes an interface link.
func DelLinkByName(ifName string) error {
	iface, err := netlink.LinkByName(ifName)
	if err != nil {
		if err.Error() == "Link not found" {
			return ErrLinkNotFound
		}
		return fmt.Errorf("failed to lookup %q: %v", ifName, err)
	}

	if err = netlink.LinkDel(iface); err != nil {
		return fmt.Errorf("failed to delete %q: %v", ifName, err)
	}

	return nil
}

// DelLinkByNameAddr remove an interface and returns its addresses
func DelLinkByNameAddr(ifName string) ([]*net.IPNet, error) {
	iface, err := netlink.LinkByName(ifName)
	if err != nil {
		if err != nil && err.Error() == "Link not found" {
			return nil, ErrLinkNotFound
		}
		return nil, fmt.Errorf("failed to lookup %q: %v", ifName, err)
	}

	addrs, err := netlink.AddrList(iface, netlink.FAMILY_ALL)
	if err != nil {
		return nil, fmt.Errorf("failed to get IP addresses for %q: %v", ifName, err)
	}

	if err = netlink.LinkDel(iface); err != nil {
		return nil, fmt.Errorf("failed to delete %q: %v", ifName, err)
	}

	out := []*net.IPNet{}
	for _, addr := range addrs {
		if addr.IP.IsGlobalUnicast() {
			out = append(out, addr.IPNet)
		}
	}

	return out, nil
}

func SetHWAddrByIP(ifName string, ip4 net.IP, ip6 net.IP) error {
	iface, err := netlink.LinkByName(ifName)
	if err != nil {
		return fmt.Errorf("failed to lookup %q: %v", ifName, err)
	}

	switch {
	case ip4 == nil && ip6 == nil:
		return fmt.Errorf("neither ip4 or ip6 specified")

	case ip4 != nil:
		{
			hwAddr, err := hwaddr.GenerateHardwareAddr4(ip4, hwaddr.PrivateMACPrefix)
			if err != nil {
				return fmt.Errorf("failed to generate hardware addr: %v", err)
			}
			if err = netlink.LinkSetHardwareAddr(iface, hwAddr); err != nil {
				return fmt.Errorf("failed to add hardware addr to %q: %v", ifName, err)
			}
		}
	case ip6 != nil:
		// TODO: IPv6
	}

	return nil
}
<|endoftext|>


<commit_before>package subs

import "github.com/bitfinexcom/bitfinex-api-go/pkg/models/event"

type Subs struct {
	Subs map[event.Subscribe]bool
}

const subsLimit = 30

// New returns pointer to instacne of Subscriptions
func New() *Subs {
	return &Subs{
		Subs: make(map[event.Subscribe]bool),
	}
}

// LimitReached returns true if number of subs > subsLimit
func (s *Subs) LimitReached() bool {
	return len(s.Subs) == subsLimit
}

// Added checks if given subscription is already added. Used to
// avoid duplicate subscriptions per client
func (s *Subs) Added(sub event.Subscribe) (res bool) {
	_, res = s.Subs[sub]
	return
}

// Add adds new subscription to the list
func (s *Subs) Add(sub event.Subscribe) {
	s.Subs[sub] = true
}

// Remove adds new subscription to the list
func (s *Subs) Remove(sub event.Subscribe) {
	delete(s.Subs, sub)
}

// GetAll returns all subscriptions
func (s *Subs) GetAll() (res []event.Subscribe) {
	for sub := range s.Subs {
		res = append(res, sub)
	}
	return
}
<commit_msg>making subs limit dynamic<commit_after>package subs

import "github.com/bitfinexcom/bitfinex-api-go/pkg/models/event"

type Subs struct {
	Subs      map[event.Subscribe]bool
	SubsLimit int
}

// New returns pointer to instacne of Subscriptions
func New() *Subs {
	return &Subs{
		Subs: make(map[event.Subscribe]bool),
	}
}

// LimitReached returns true if number of subs > subsLimit
func (s *Subs) LimitReached() bool {
	return len(s.Subs) == s.SubsLimit
}

// Added checks if given subscription is already added. Used to
// avoid duplicate subscriptions per client
func (s *Subs) Added(sub event.Subscribe) (res bool) {
	_, res = s.Subs[sub]
	return
}

// Add adds new subscription to the list
func (s *Subs) Add(sub event.Subscribe) {
	s.Subs[sub] = true
}

// Remove adds new subscription to the list
func (s *Subs) Remove(sub event.Subscribe) {
	delete(s.Subs, sub)
}

// GetAll returns all subscriptions
func (s *Subs) GetAll() (res []event.Subscribe) {
	for sub := range s.Subs {
		res = append(res, sub)
	}
	return
}
<|endoftext|>


<commit_before>package nodeps

import (
	"sort"
)

// Providers
//TODO: This should be removed as many providers will now be valid
const (
	// ProviderDefault contains the name of the default provider which will be used if one is not otherwise specified.
	ProviderDefault = "default"
)

// Database Types
const (
	MariaDB  = "mariadb"
	MySQL    = "mysql"
	Postgres = "postgres"
)

// Container types used with ddev
const (
	DdevSSHAgentContainer = "ddev-ssh-agent"
	DBAContainer          = "dba"
	DBContainer           = "db"
	WebContainer          = "web"
	RouterContainer       = "ddev-router"
)

// Webserver types
const (
	WebserverNginxFPM  = "nginx-fpm"
	WebserverApacheFPM = "apache-fpm"
)

// ValidOmitContainers is the list of things that can be omitted
var ValidOmitContainers = map[string]bool{
	DBContainer:           true,
	DdevSSHAgentContainer: true,
	DBAContainer:          true,
}

// DdevFileSignature is the text we use to detect whether a settings file is managed by us.
// If this string is found, we assume we can replace/update the file.
const DdevFileSignature = "#ddev-generated"

// WebserverDefault is the default webserver type, overridden by $DDEV_WEBSERVER_TYPE
var WebserverDefault = WebserverNginxFPM

// MutagenEnabledDefault is default value for app.MutagenEnabled
var MutagenEnabledDefault = false

// NFSMountEnabledDefault is default value for app.NFSMountEnabled
var NFSMountEnabledDefault = false

const NodeJSDefault = "16"

// NoBindMountsDefault is default value for globalconfig.DDEVGlobalConfig.NoBindMounts
var NoBindMountsDefault = false

// SimpleFormatting is turned on by DDEV_USE_SIMPLE_FORMATTING
// and makes ddev list and describe, etc. use simpler formatting
var SimpleFormatting = false

// FailOnHookFailDefault is the default value for app.FailOnHookFail
var FailOnHookFailDefault = false

// ValidWebserverTypes should be updated whenever supported webserver types are added or
// removed, and should be used to ensure user-supplied values are valid.
var ValidWebserverTypes = map[string]bool{
	WebserverNginxFPM:  true,
	WebserverApacheFPM: true,
}

var ValidNodeJSVersions = []string{"12", "14", "16", "17", "18"}

// App types
const (
	AppTypeBackdrop  = "backdrop"
	AppTypeDrupal6   = "drupal6"
	AppTypeDrupal7   = "drupal7"
	AppTypeDrupal8   = "drupal8"
	AppTypeDrupal9   = "drupal9"
	AppTypeDrupal10  = "drupal10"
	AppTypePHP       = "php"
	AppTypeTYPO3     = "typo3"
	AppTypeWordPress = "wordpress"
	AppTypeMagento   = "magento"
	AppTypeMagento2  = "magento2"
	AppTypeLaravel   = "laravel"
	AppTypeShopware6 = "shopware6"
)

// Ports and other defaults
const (
	// DdevDefaultRouterHTTPPort is the default router HTTP port
	DdevDefaultRouterHTTPPort = "80"

	// DdevDefaultRouterHTTPSPort is the default router HTTPS port
	DdevDefaultRouterHTTPSPort = "443"
	// DdevDefaultPHPMyAdminPort is the default router port for dba/PHPMyadmin
	DdevDefaultPHPMyAdminPort      = "8036"
	DdevDefaultPHPMyAdminHTTPSPort = "8037"
	// DdevDefaultMailhogPort is the default router port for Mailhog
	DdevDefaultMailhogPort      = "8025"
	DdevDefaultMailhogHTTPSPort = "8026"
	// DdevDefaultTLD is the top-level-domain used by default, can be overridden
	DdevDefaultTLD                  = "ddev.site"
	DefaultDefaultContainerTimeout  = "120"
	InternetDetectionTimeoutDefault = 3000
	MinimumDockerSpaceWarning       = 5000000 // 5GB in KB (to compare against df reporting in KB)
)

// IsValidPHPVersion is a helper function to determine if a PHP version is valid, returning
// true if the supplied PHP version is valid and false otherwise.
func IsValidPHPVersion(phpVersion string) bool {
	if _, ok := ValidPHPVersions[phpVersion]; !ok {
		return false
	}

	return true
}

// GetValidPHPVersions is a helper function that returns a list of valid PHP versions.
func GetValidPHPVersions() []string {
	s := make([]string, 0, len(ValidPHPVersions))

	for p := range ValidPHPVersions {
		s = append(s, p)
	}
	sort.Strings(s)
	return s
}

// GetValidNodeVersions is a helper function that returns a list of valid nodejs versions.
func GetValidNodeVersions() []string {
	return ValidNodeJSVersions
}

// IsValidNodeVersion is a helper function to determine if a NodeJS version is valid
func IsValidNodeVersion(v string) bool {
	return ArrayContainsString(GetValidNodeVersions(), v)
}

// IsValidDatabaseVersion checks if the version is valid for the provided database type
func IsValidDatabaseVersion(dbType string, dbVersion string) bool {
	switch dbType {
	case MariaDB:
		return IsValidMariaDBVersion(dbVersion)
	case MySQL:
		return IsValidMySQLVersion(dbVersion)
	case Postgres:
		return IsValidPostgresVersion(dbVersion)
	}
	return false
}

// GetValidDatabaseVersions returns a slice of valid versions with the format
// mariadb:10.5/mysql:5.7/postgres:14
func GetValidDatabaseVersions() []string {
	combos := []string{}
	for _, v := range GetValidMariaDBVersions() {
		combos = append(combos, MariaDB+":"+v)
	}
	for _, v := range GetValidMySQLVersions() {
		combos = append(combos, MySQL+":"+v)
	}
	for _, v := range GetValidPostgresVersions() {
		combos = append(combos, Postgres+":"+v)
	}

	return combos
}

// IsValidMariaDBVersion is a helper function to determine if a MariaDB version is valid, returning
// true if the supplied MariaDB version is valid and false otherwise.
func IsValidMariaDBVersion(MariaDBVersion string) bool {
	if _, ok := ValidMariaDBVersions[MariaDBVersion]; !ok {
		return false
	}

	return true
}

// IsValidMySQLVersion is a helper function to determine if a MySQL version is valid, returning
// true if the supplied version is valid and false otherwise.
func IsValidMySQLVersion(v string) bool {
	if _, ok := ValidMySQLVersions[v]; !ok {
		return false
	}

	return true
}

// GetValidMariaDBVersions is a helper function that returns a list of valid MariaDB versions.
func GetValidMariaDBVersions() []string {
	s := make([]string, 0, len(ValidMariaDBVersions))

	for p := range ValidMariaDBVersions {
		s = append(s, p)
	}
	sort.Strings(s)
	return s
}

// IsValidPostgresVersion is a helper function to determine if a Postgres version is valid, returning
// true if the supplied version is valid and false otherwise.
func IsValidPostgresVersion(v string) bool {
	if _, ok := ValidPostgresVersions[v]; !ok {
		return false
	}

	return true
}

// GetValidMySQLVersions is a helper function that returns a list of valid MySQL versions.
func GetValidMySQLVersions() []string {
	s := make([]string, 0, len(ValidMySQLVersions))

	for p := range ValidMySQLVersions {
		s = append(s, p)
	}
	sort.Strings(s)
	return s
}

// GetValidPostgresVersions is a helper function that returns a list of valid Postgres versions.
func GetValidPostgresVersions() []string {
	s := make([]string, 0, len(ValidPostgresVersions))

	for p := range ValidPostgresVersions {
		s = append(s, p)
	}
	sort.Strings(s)
	return s
}

// IsValidWebserverType is a helper function to determine if a webserver type is valid, returning
// true if the supplied webserver type is valid and false otherwise.
func IsValidWebserverType(webserverType string) bool {
	if _, ok := ValidWebserverTypes[webserverType]; !ok {
		return false
	}

	return true
}

// GetValidWebserverTypes is a helper function that returns a list of valid webserver types.
func GetValidWebserverTypes() []string {
	s := make([]string, 0, len(ValidWebserverTypes))

	for p := range ValidWebserverTypes {
		s = append(s, p)
	}

	return s
}

// IsValidOmitContainers is a helper function to determine if a the OmitContainers array is valid
func IsValidOmitContainers(containerList []string) bool {
	for _, containerName := range containerList {
		if _, ok := ValidOmitContainers[containerName]; !ok {
			return false
		}
	}
	return true
}

// GetValidOmitContainers is a helper function that returns a list of valid containers for OmitContainers.
func GetValidOmitContainers() []string {
	s := make([]string, 0, len(ValidOmitContainers))

	for p := range ValidOmitContainers {
		s = append(s, p)
	}

	return s
}
<commit_msg>Remove nodejs 12 and 17 from allowed values as out of support (#4073)<commit_after>package nodeps

import (
	"sort"
)

// Providers
//TODO: This should be removed as many providers will now be valid
const (
	// ProviderDefault contains the name of the default provider which will be used if one is not otherwise specified.
	ProviderDefault = "default"
)

// Database Types
const (
	MariaDB  = "mariadb"
	MySQL    = "mysql"
	Postgres = "postgres"
)

// Container types used with ddev
const (
	DdevSSHAgentContainer = "ddev-ssh-agent"
	DBAContainer          = "dba"
	DBContainer           = "db"
	WebContainer          = "web"
	RouterContainer       = "ddev-router"
)

// Webserver types
const (
	WebserverNginxFPM  = "nginx-fpm"
	WebserverApacheFPM = "apache-fpm"
)

// ValidOmitContainers is the list of things that can be omitted
var ValidOmitContainers = map[string]bool{
	DBContainer:           true,
	DdevSSHAgentContainer: true,
	DBAContainer:          true,
}

// DdevFileSignature is the text we use to detect whether a settings file is managed by us.
// If this string is found, we assume we can replace/update the file.
const DdevFileSignature = "#ddev-generated"

// WebserverDefault is the default webserver type, overridden by $DDEV_WEBSERVER_TYPE
var WebserverDefault = WebserverNginxFPM

// MutagenEnabledDefault is default value for app.MutagenEnabled
var MutagenEnabledDefault = false

// NFSMountEnabledDefault is default value for app.NFSMountEnabled
var NFSMountEnabledDefault = false

const NodeJSDefault = "16"

// NoBindMountsDefault is default value for globalconfig.DDEVGlobalConfig.NoBindMounts
var NoBindMountsDefault = false

// SimpleFormatting is turned on by DDEV_USE_SIMPLE_FORMATTING
// and makes ddev list and describe, etc. use simpler formatting
var SimpleFormatting = false

// FailOnHookFailDefault is the default value for app.FailOnHookFail
var FailOnHookFailDefault = false

// ValidWebserverTypes should be updated whenever supported webserver types are added or
// removed, and should be used to ensure user-supplied values are valid.
var ValidWebserverTypes = map[string]bool{
	WebserverNginxFPM:  true,
	WebserverApacheFPM: true,
}

var ValidNodeJSVersions = []string{"14", "16", "18"}

// App types
const (
	AppTypeBackdrop  = "backdrop"
	AppTypeDrupal6   = "drupal6"
	AppTypeDrupal7   = "drupal7"
	AppTypeDrupal8   = "drupal8"
	AppTypeDrupal9   = "drupal9"
	AppTypeDrupal10  = "drupal10"
	AppTypePHP       = "php"
	AppTypeTYPO3     = "typo3"
	AppTypeWordPress = "wordpress"
	AppTypeMagento   = "magento"
	AppTypeMagento2  = "magento2"
	AppTypeLaravel   = "laravel"
	AppTypeShopware6 = "shopware6"
)

// Ports and other defaults
const (
	// DdevDefaultRouterHTTPPort is the default router HTTP port
	DdevDefaultRouterHTTPPort = "80"

	// DdevDefaultRouterHTTPSPort is the default router HTTPS port
	DdevDefaultRouterHTTPSPort = "443"
	// DdevDefaultPHPMyAdminPort is the default router port for dba/PHPMyadmin
	DdevDefaultPHPMyAdminPort      = "8036"
	DdevDefaultPHPMyAdminHTTPSPort = "8037"
	// DdevDefaultMailhogPort is the default router port for Mailhog
	DdevDefaultMailhogPort      = "8025"
	DdevDefaultMailhogHTTPSPort = "8026"
	// DdevDefaultTLD is the top-level-domain used by default, can be overridden
	DdevDefaultTLD                  = "ddev.site"
	DefaultDefaultContainerTimeout  = "120"
	InternetDetectionTimeoutDefault = 3000
	MinimumDockerSpaceWarning       = 5000000 // 5GB in KB (to compare against df reporting in KB)
)

// IsValidPHPVersion is a helper function to determine if a PHP version is valid, returning
// true if the supplied PHP version is valid and false otherwise.
func IsValidPHPVersion(phpVersion string) bool {
	if _, ok := ValidPHPVersions[phpVersion]; !ok {
		return false
	}

	return true
}

// GetValidPHPVersions is a helper function that returns a list of valid PHP versions.
func GetValidPHPVersions() []string {
	s := make([]string, 0, len(ValidPHPVersions))

	for p := range ValidPHPVersions {
		s = append(s, p)
	}
	sort.Strings(s)
	return s
}

// GetValidNodeVersions is a helper function that returns a list of valid nodejs versions.
func GetValidNodeVersions() []string {
	return ValidNodeJSVersions
}

// IsValidNodeVersion is a helper function to determine if a NodeJS version is valid
func IsValidNodeVersion(v string) bool {
	return ArrayContainsString(GetValidNodeVersions(), v)
}

// IsValidDatabaseVersion checks if the version is valid for the provided database type
func IsValidDatabaseVersion(dbType string, dbVersion string) bool {
	switch dbType {
	case MariaDB:
		return IsValidMariaDBVersion(dbVersion)
	case MySQL:
		return IsValidMySQLVersion(dbVersion)
	case Postgres:
		return IsValidPostgresVersion(dbVersion)
	}
	return false
}

// GetValidDatabaseVersions returns a slice of valid versions with the format
// mariadb:10.5/mysql:5.7/postgres:14
func GetValidDatabaseVersions() []string {
	combos := []string{}
	for _, v := range GetValidMariaDBVersions() {
		combos = append(combos, MariaDB+":"+v)
	}
	for _, v := range GetValidMySQLVersions() {
		combos = append(combos, MySQL+":"+v)
	}
	for _, v := range GetValidPostgresVersions() {
		combos = append(combos, Postgres+":"+v)
	}

	return combos
}

// IsValidMariaDBVersion is a helper function to determine if a MariaDB version is valid, returning
// true if the supplied MariaDB version is valid and false otherwise.
func IsValidMariaDBVersion(MariaDBVersion string) bool {
	if _, ok := ValidMariaDBVersions[MariaDBVersion]; !ok {
		return false
	}

	return true
}

// IsValidMySQLVersion is a helper function to determine if a MySQL version is valid, returning
// true if the supplied version is valid and false otherwise.
func IsValidMySQLVersion(v string) bool {
	if _, ok := ValidMySQLVersions[v]; !ok {
		return false
	}

	return true
}

// GetValidMariaDBVersions is a helper function that returns a list of valid MariaDB versions.
func GetValidMariaDBVersions() []string {
	s := make([]string, 0, len(ValidMariaDBVersions))

	for p := range ValidMariaDBVersions {
		s = append(s, p)
	}
	sort.Strings(s)
	return s
}

// IsValidPostgresVersion is a helper function to determine if a Postgres version is valid, returning
// true if the supplied version is valid and false otherwise.
func IsValidPostgresVersion(v string) bool {
	if _, ok := ValidPostgresVersions[v]; !ok {
		return false
	}

	return true
}

// GetValidMySQLVersions is a helper function that returns a list of valid MySQL versions.
func GetValidMySQLVersions() []string {
	s := make([]string, 0, len(ValidMySQLVersions))

	for p := range ValidMySQLVersions {
		s = append(s, p)
	}
	sort.Strings(s)
	return s
}

// GetValidPostgresVersions is a helper function that returns a list of valid Postgres versions.
func GetValidPostgresVersions() []string {
	s := make([]string, 0, len(ValidPostgresVersions))

	for p := range ValidPostgresVersions {
		s = append(s, p)
	}
	sort.Strings(s)
	return s
}

// IsValidWebserverType is a helper function to determine if a webserver type is valid, returning
// true if the supplied webserver type is valid and false otherwise.
func IsValidWebserverType(webserverType string) bool {
	if _, ok := ValidWebserverTypes[webserverType]; !ok {
		return false
	}

	return true
}

// GetValidWebserverTypes is a helper function that returns a list of valid webserver types.
func GetValidWebserverTypes() []string {
	s := make([]string, 0, len(ValidWebserverTypes))

	for p := range ValidWebserverTypes {
		s = append(s, p)
	}

	return s
}

// IsValidOmitContainers is a helper function to determine if a the OmitContainers array is valid
func IsValidOmitContainers(containerList []string) bool {
	for _, containerName := range containerList {
		if _, ok := ValidOmitContainers[containerName]; !ok {
			return false
		}
	}
	return true
}

// GetValidOmitContainers is a helper function that returns a list of valid containers for OmitContainers.
func GetValidOmitContainers() []string {
	s := make([]string, 0, len(ValidOmitContainers))

	for p := range ValidOmitContainers {
		s = append(s, p)
	}

	return s
}
<|endoftext|>