mirror of
https://github.com/alibaba/higress.git
synced 2026-03-15 22:30:47 +08:00
feat:add installation for higress standalone in local docker environment (#606)
Co-authored-by: 澄潭 <zty98751@alibaba-inc.com>
This commit is contained in:
@@ -26,15 +26,40 @@ import (
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
// ReadYamlProfile gets the overlay yaml file from list of files and return profile value from file overlay and set overlay.
|
||||
func ReadYamlProfile(inFilenames []string, setFlags []string) (string, string, error) {
|
||||
// GetProfileFromFlags get profile name from flags.
|
||||
func GetProfileFromFlags(setFlags []string) (string, error) {
|
||||
profileName := DefaultProfileName
|
||||
// The profile coming from --set flag has the highest precedence.
|
||||
psf := GetValueForSetFlag(setFlags, "profile")
|
||||
if psf != "" {
|
||||
profileName = psf
|
||||
}
|
||||
return "", profileName, nil
|
||||
return profileName, nil
|
||||
}
|
||||
|
||||
func GetValuesOverylayFromFiles(inFilenames []string) (string, error) {
|
||||
// Convert layeredYamls under values node in profile file to support helm values
|
||||
overLayYamls := ""
|
||||
// Get Overlays from files
|
||||
if len(inFilenames) > 0 {
|
||||
layeredYamls, err := ReadLayeredYAMLs(inFilenames)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
vals := make(map[string]any)
|
||||
if err := yaml.Unmarshal([]byte(layeredYamls), &vals); err != nil {
|
||||
return "", fmt.Errorf("%s:\n\nYAML:\n%s", err, layeredYamls)
|
||||
}
|
||||
values := make(map[string]any)
|
||||
values["values"] = vals
|
||||
out, err := yaml.Marshal(values)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
overLayYamls = string(out)
|
||||
}
|
||||
|
||||
return overLayYamls, nil
|
||||
}
|
||||
|
||||
func GetUninstallProfileName() string {
|
||||
@@ -101,12 +126,17 @@ func GenerateConfig(inFilenames []string, setFlags []string) (string, *Profile,
|
||||
return "", nil, "", err
|
||||
}
|
||||
|
||||
fy, profileName, err := ReadYamlProfile(inFilenames, setFlags)
|
||||
profileName, err := GetProfileFromFlags(setFlags)
|
||||
if err != nil {
|
||||
return "", nil, "", err
|
||||
}
|
||||
|
||||
profileString, profile, err := GenProfile(profileName, fy, setFlags)
|
||||
valuesOverlay, err := GetValuesOverylayFromFiles(inFilenames)
|
||||
if err != nil {
|
||||
return "", nil, "", err
|
||||
}
|
||||
|
||||
profileString, profile, err := GenProfile(profileName, valuesOverlay, setFlags)
|
||||
|
||||
if err != nil {
|
||||
return "", nil, "", err
|
||||
|
||||
@@ -17,7 +17,9 @@ package helm
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"istio.io/istio/operator/pkg/util"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
@@ -43,83 +45,72 @@ type Profile struct {
|
||||
}
|
||||
|
||||
type ProfileGlobal struct {
|
||||
Install InstallMode `json:"install,omitempty"`
|
||||
IngressClass string `json:"ingressClass,omitempty"`
|
||||
WatchNamespace string `json:"watchNamespace,omitempty"`
|
||||
DisableAlpnH2 bool `json:"disableAlpnH2,omitempty"`
|
||||
EnableStatus bool `json:"enableStatus,omitempty"`
|
||||
EnableIstioAPI bool `json:"enableIstioAPI,omitempty"`
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
IstioNamespace string `json:"istioNamespace,omitempty"`
|
||||
Install InstallMode `json:"install,omitempty"`
|
||||
IngressClass string `json:"ingressClass,omitempty"`
|
||||
EnableIstioAPI bool `json:"enableIstioAPI,omitempty"`
|
||||
EnableGatewayAPI bool `json:"enableGatewayAPI,omitempty"`
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
}
|
||||
|
||||
func (p ProfileGlobal) SetFlags(install InstallMode) ([]string, error) {
|
||||
sets := make([]string, 0)
|
||||
sets = append(sets, fmt.Sprintf("global.ingressClass=%s", p.IngressClass))
|
||||
sets = append(sets, fmt.Sprintf("global.watchNamespace=%s", p.WatchNamespace))
|
||||
sets = append(sets, fmt.Sprintf("global.disableAlpnH2=%t", p.DisableAlpnH2))
|
||||
sets = append(sets, fmt.Sprintf("global.enableStatus=%t", p.EnableStatus))
|
||||
sets = append(sets, fmt.Sprintf("global.enableIstioAPI=%t", p.EnableIstioAPI))
|
||||
sets = append(sets, fmt.Sprintf("global.istioNamespace=%s", p.IstioNamespace))
|
||||
if install == InstallLocalK8s {
|
||||
sets = append(sets, fmt.Sprintf("global.local=%t", true))
|
||||
if install == InstallK8s || install == InstallLocalK8s {
|
||||
sets = append(sets, fmt.Sprintf("global.ingressClass=%s", p.IngressClass))
|
||||
sets = append(sets, fmt.Sprintf("global.enableIstioAPI=%t", p.EnableIstioAPI))
|
||||
sets = append(sets, fmt.Sprintf("global.enableGatewayAPI=%t", p.EnableGatewayAPI))
|
||||
if install == InstallLocalK8s {
|
||||
sets = append(sets, fmt.Sprintf("global.local=%t", true))
|
||||
}
|
||||
}
|
||||
return sets, nil
|
||||
}
|
||||
|
||||
func (p ProfileGlobal) Validate(install InstallMode) []error {
|
||||
errs := make([]error, 0)
|
||||
// now only support k8s and local-k8s installation mode
|
||||
if p.Install != InstallK8s && p.Install != InstallLocalK8s {
|
||||
errs = append(errs, errors.New("global.install only can be set to k8s or local-k8s"))
|
||||
// now only support k8s, local-k8s, local-docker installation mode
|
||||
if install != InstallK8s && install != InstallLocalK8s && install != InstallLocalDocker {
|
||||
errs = append(errs, errors.New("global.install only can be set to k8s, local-k8s or local-docker"))
|
||||
}
|
||||
if len(p.IngressClass) == 0 {
|
||||
errs = append(errs, errors.New("global.ingressClass can't be empty"))
|
||||
}
|
||||
if len(p.Namespace) == 0 {
|
||||
errs = append(errs, errors.New("global.namespace can't be empty"))
|
||||
}
|
||||
if len(p.IstioNamespace) == 0 {
|
||||
errs = append(errs, errors.New("global.istioNamespace can't be empty"))
|
||||
if install == InstallK8s || install == InstallLocalK8s {
|
||||
if len(p.IngressClass) == 0 {
|
||||
errs = append(errs, errors.New("global.ingressClass can't be empty"))
|
||||
}
|
||||
if len(p.Namespace) == 0 {
|
||||
errs = append(errs, errors.New("global.namespace can't be empty"))
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
type ProfileConsole struct {
|
||||
Port uint32 `json:"port,omitempty"`
|
||||
Replicas uint32 `json:"replicas,omitempty"`
|
||||
ServiceType string `json:"serviceType,omitempty"`
|
||||
Domain string `json:"domain,omitempty"`
|
||||
TlsSecretName string `json:"tlsSecretName,omitempty"`
|
||||
WebLoginPrompt string `json:"webLoginPrompt,omitempty"`
|
||||
AdminPasswordValue string `json:"adminPasswordValue,omitempty"`
|
||||
AdminPasswordLength uint32 `json:"adminPasswordLength,omitempty"`
|
||||
O11yEnabled bool `json:"o11YEnabled,omitempty"`
|
||||
PvcRwxSupported bool `json:"pvcRwxSupported,omitempty"`
|
||||
Port uint32 `json:"port,omitempty"`
|
||||
Replicas uint32 `json:"replicas,omitempty"`
|
||||
AdminPasswordValue string `json:"adminPasswordValue,omitempty"`
|
||||
O11yEnabled bool `json:"o11YEnabled,omitempty"`
|
||||
}
|
||||
|
||||
func (p ProfileConsole) SetFlags(install InstallMode) ([]string, error) {
|
||||
sets := make([]string, 0)
|
||||
sets = append(sets, fmt.Sprintf("higress-console.replicaCount=%d", p.Replicas))
|
||||
sets = append(sets, fmt.Sprintf("higress-console.service.type=%s", p.ServiceType))
|
||||
sets = append(sets, fmt.Sprintf("higress-console.domain=%s", p.Domain))
|
||||
sets = append(sets, fmt.Sprintf("higress-console.tlsSecretName=%s", p.TlsSecretName))
|
||||
sets = append(sets, fmt.Sprintf("higress-console.web.login.prompt=%s", p.WebLoginPrompt))
|
||||
sets = append(sets, fmt.Sprintf("higress-console.admin.password.value=%s", p.AdminPasswordValue))
|
||||
sets = append(sets, fmt.Sprintf("higress-console.admin.password.length=%d", p.AdminPasswordLength))
|
||||
sets = append(sets, fmt.Sprintf("higress-console.o11y.enabled=%t", p.O11yEnabled))
|
||||
sets = append(sets, fmt.Sprintf("higress-console.pvc.rwxSupported=%t", p.PvcRwxSupported))
|
||||
if install == InstallK8s || install == InstallLocalK8s {
|
||||
sets = append(sets, fmt.Sprintf("higress-console.replicaCount=%d", p.Replicas))
|
||||
sets = append(sets, fmt.Sprintf("higress-console.admin.password.value=%s", p.AdminPasswordValue))
|
||||
sets = append(sets, fmt.Sprintf("higress-console.o11y.enabled=%t", p.O11yEnabled))
|
||||
}
|
||||
return sets, nil
|
||||
}
|
||||
|
||||
func (p ProfileConsole) Validate(install InstallMode) []error {
|
||||
errs := make([]error, 0)
|
||||
if p.Replicas <= 0 {
|
||||
errs = append(errs, errors.New("console.replica need be large than zero"))
|
||||
if install == InstallK8s || install == InstallLocalK8s {
|
||||
if p.Replicas <= 0 {
|
||||
errs = append(errs, errors.New("console.replica need be large than zero"))
|
||||
}
|
||||
}
|
||||
|
||||
if p.ServiceType != "ClusterIP" && p.ServiceType != "NodePort" && p.ServiceType != "LoadBalancer" {
|
||||
errs = append(errs, errors.New("console.serviceType can only be set to ClusterIP, NodePort or LoadBalancer"))
|
||||
if install == InstallLocalDocker {
|
||||
if p.Port <= 0 {
|
||||
errs = append(errs, errors.New("console.port need be large than zero"))
|
||||
}
|
||||
}
|
||||
|
||||
return errs
|
||||
@@ -134,16 +125,31 @@ type ProfileGateway struct {
|
||||
|
||||
func (p ProfileGateway) SetFlags(install InstallMode) ([]string, error) {
|
||||
sets := make([]string, 0)
|
||||
sets = append(sets, fmt.Sprintf("higress-core.gateway.replicas=%d", p.Replicas))
|
||||
if install == InstallK8s || install == InstallLocalK8s {
|
||||
sets = append(sets, fmt.Sprintf("higress-core.gateway.replicas=%d", p.Replicas))
|
||||
}
|
||||
return sets, nil
|
||||
}
|
||||
|
||||
func (p ProfileGateway) Validate(install InstallMode) []error {
|
||||
errs := make([]error, 0)
|
||||
if p.Replicas <= 0 {
|
||||
errs = append(errs, errors.New("gateway.replica need be large than zero"))
|
||||
if install == InstallK8s || install == InstallLocalK8s {
|
||||
if p.Replicas <= 0 {
|
||||
errs = append(errs, errors.New("gateway.replica need be large than zero"))
|
||||
}
|
||||
}
|
||||
|
||||
if install == InstallLocalDocker {
|
||||
if p.HttpPort <= 0 {
|
||||
errs = append(errs, errors.New("gateway.httpPort need be large than zero"))
|
||||
}
|
||||
if p.HttpsPort <= 0 {
|
||||
errs = append(errs, errors.New("gateway.httpsPort need be large than zero"))
|
||||
}
|
||||
if p.MetricsPort <= 0 {
|
||||
errs = append(errs, errors.New("gateway.MetricsPort need be large than zero"))
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
@@ -153,16 +159,19 @@ type ProfileController struct {
|
||||
|
||||
func (p ProfileController) SetFlags(install InstallMode) ([]string, error) {
|
||||
sets := make([]string, 0)
|
||||
sets = append(sets, fmt.Sprintf("higress-core.controller.replicas=%d", p.Replicas))
|
||||
if install == InstallK8s || install == InstallLocalK8s {
|
||||
sets = append(sets, fmt.Sprintf("higress-core.controller.replicas=%d", p.Replicas))
|
||||
}
|
||||
return sets, nil
|
||||
}
|
||||
|
||||
func (p ProfileController) Validate(install InstallMode) []error {
|
||||
errs := make([]error, 0)
|
||||
if p.Replicas <= 0 {
|
||||
errs = append(errs, errors.New("controller.replica need be large than zero"))
|
||||
if install == InstallK8s || install == InstallLocalK8s {
|
||||
if p.Replicas <= 0 {
|
||||
errs = append(errs, errors.New("controller.replica need be large than zero"))
|
||||
}
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
@@ -176,6 +185,31 @@ type ProfileStorage struct {
|
||||
|
||||
func (p ProfileStorage) Validate(install InstallMode) []error {
|
||||
errs := make([]error, 0)
|
||||
if install == InstallLocalDocker {
|
||||
if len(p.Url) == 0 {
|
||||
errs = append(errs, errors.New("storage.url can't be empty"))
|
||||
}
|
||||
if len(p.Ns) == 0 {
|
||||
errs = append(errs, errors.New("storage.ns can't be empty"))
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(p.Url, "nacos://") && !strings.HasPrefix(p.Url, "file://") {
|
||||
errs = append(errs, fmt.Errorf("invalid storage url: %s", p.Url))
|
||||
} else {
|
||||
// check localhost or 127.0.0.0
|
||||
if strings.Contains(p.Url, "localhost") || strings.Contains(p.Url, "/127.") {
|
||||
errs = append(errs, errors.New("localhost or loopback addresses in nacos url won't work"))
|
||||
}
|
||||
}
|
||||
|
||||
if len(p.DataEncKey) > 0 && len(p.DataEncKey) != 32 {
|
||||
errs = append(errs, fmt.Errorf("expecting 32 characters for dataEncKey, but got %d length", len(p.DataEncKey)))
|
||||
}
|
||||
|
||||
if len(p.Username) > 0 && len(p.Password) == 0 || len(p.Username) == 0 && len(p.Password) > 0 {
|
||||
errs = append(errs, errors.New("both nacos username and password should be provided"))
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
@@ -186,8 +220,8 @@ type Chart struct {
|
||||
}
|
||||
|
||||
type ProfileCharts struct {
|
||||
Higress Chart `json:"higress,omitempty"`
|
||||
Istio Chart `json:"istio,omitempty"`
|
||||
Higress Chart `json:"higress,omitempty"`
|
||||
Standalone Chart `json:"standalone,omitempty"`
|
||||
}
|
||||
|
||||
func (p ProfileCharts) Validate(install InstallMode) []error {
|
||||
@@ -222,8 +256,13 @@ func (p *Profile) ValuesYaml() (string, error) {
|
||||
}
|
||||
valueOverlayYAML = string(out)
|
||||
}
|
||||
|
||||
flagsYAML, err := overlaySetFlagValues("", setFlags)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// merge values and setFlags
|
||||
overlayYAML, err := overlaySetFlagValues(valueOverlayYAML, setFlags)
|
||||
overlayYAML, err := util.OverlayYAML(flagsYAML, valueOverlayYAML)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -237,6 +276,26 @@ func (p *Profile) IstioEnabled() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *Profile) GatewayAPIEnabled() bool {
|
||||
if (p.Global.Install == InstallK8s || p.Global.Install == InstallLocalK8s) && p.Global.EnableGatewayAPI {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *Profile) GetIstioNamespace() string {
|
||||
if valuesGlobal, ok1 := p.Values["global"]; ok1 {
|
||||
if global, ok2 := valuesGlobal.(map[string]any); ok2 {
|
||||
if istioNamespace, ok3 := global["istioNamespace"]; ok3 {
|
||||
if namespace, ok4 := istioNamespace.(string); ok4 {
|
||||
return namespace
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p *Profile) Validate() error {
|
||||
errs := make([]error, 0)
|
||||
errsGlobal := p.Global.Validate(p.Global.Install)
|
||||
@@ -256,7 +315,7 @@ func (p *Profile) Validate() error {
|
||||
errs = append(errs, errsController...)
|
||||
}
|
||||
errsStorage := p.Storage.Validate(p.Global.Install)
|
||||
if len(errsController) > 0 {
|
||||
if len(errsStorage) > 0 {
|
||||
errs = append(errs, errsStorage...)
|
||||
}
|
||||
errsCharts := p.Charts.Validate(p.Global.Install)
|
||||
|
||||
@@ -127,7 +127,7 @@ type RendererOptions struct {
|
||||
Name string
|
||||
Namespace string
|
||||
|
||||
// fields for LocalRenderer
|
||||
// fields for LocalChartRenderer and LocalFileRenderer
|
||||
FS fs.FS
|
||||
Dir string
|
||||
|
||||
@@ -174,14 +174,84 @@ func WithRepoURL(repo string) RendererOption {
|
||||
}
|
||||
}
|
||||
|
||||
// LocalRenderer load chart from local file system
|
||||
type LocalRenderer struct {
|
||||
// LocalFileRenderer load yaml files from local file system
|
||||
type LocalFileRenderer struct {
|
||||
Opts *RendererOptions
|
||||
filesMap map[string]string
|
||||
Started bool
|
||||
}
|
||||
|
||||
func NewLocalFileRenderer(opts ...RendererOption) (Renderer, error) {
|
||||
newOpts := &RendererOptions{}
|
||||
for _, opt := range opts {
|
||||
opt(newOpts)
|
||||
}
|
||||
|
||||
return &LocalFileRenderer{
|
||||
Opts: newOpts,
|
||||
filesMap: make(map[string]string),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *LocalFileRenderer) Init() error {
|
||||
fileNames, err := getFileNames(l.Opts.FS, l.Opts.Dir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return fmt.Errorf("chart of component %s doesn't exist", l.Opts.Name)
|
||||
}
|
||||
return fmt.Errorf("getFileNames err: %s", err)
|
||||
}
|
||||
for _, fileName := range fileNames {
|
||||
data, err := fs.ReadFile(l.Opts.FS, fileName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ReadFile %s err: %s", fileName, err)
|
||||
}
|
||||
|
||||
l.filesMap[fileName] = string(data)
|
||||
}
|
||||
l.Started = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *LocalFileRenderer) RenderManifest(valsYaml string) (string, error) {
|
||||
if !l.Started {
|
||||
return "", errors.New("LocalFileRenderer has not been init")
|
||||
}
|
||||
keys := make([]string, 0, len(l.filesMap))
|
||||
for key := range l.filesMap {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
// to ensure that every manifest rendered by same values are the same
|
||||
sort.Strings(keys)
|
||||
|
||||
var builder strings.Builder
|
||||
for i := 0; i < len(keys); i++ {
|
||||
file := l.filesMap[keys[i]]
|
||||
file = util.ApplyFilters(file, DefaultFilters...)
|
||||
// ignore empty manifest
|
||||
if file == "" {
|
||||
continue
|
||||
}
|
||||
if !strings.HasSuffix(file, YAMLSeparator) {
|
||||
file += YAMLSeparator
|
||||
}
|
||||
builder.WriteString(file)
|
||||
}
|
||||
return builder.String(), nil
|
||||
}
|
||||
|
||||
func (l *LocalFileRenderer) SetVersion(version string) {
|
||||
l.Opts.Version = version
|
||||
}
|
||||
|
||||
// LocalChartRenderer load chart from local file system
|
||||
type LocalChartRenderer struct {
|
||||
Opts *RendererOptions
|
||||
Chart *chart.Chart
|
||||
Started bool
|
||||
}
|
||||
|
||||
func (lr *LocalRenderer) Init() error {
|
||||
func (lr *LocalChartRenderer) Init() error {
|
||||
fileNames, err := getFileNames(lr.Opts.FS, lr.Opts.Dir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
@@ -212,18 +282,18 @@ func (lr *LocalRenderer) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (lr *LocalRenderer) RenderManifest(valsYaml string) (string, error) {
|
||||
func (lr *LocalChartRenderer) RenderManifest(valsYaml string) (string, error) {
|
||||
if !lr.Started {
|
||||
return "", errors.New("LocalRenderer has not been init")
|
||||
return "", errors.New("LocalChartRenderer has not been init")
|
||||
}
|
||||
return renderManifest(valsYaml, lr.Chart, true, lr.Opts, DefaultFilters...)
|
||||
}
|
||||
|
||||
func (lr *LocalRenderer) SetVersion(version string) {
|
||||
func (lr *LocalChartRenderer) SetVersion(version string) {
|
||||
lr.Opts.Version = version
|
||||
}
|
||||
|
||||
func NewLocalRenderer(opts ...RendererOption) (Renderer, error) {
|
||||
func NewLocalChartRenderer(opts ...RendererOption) (Renderer, error) {
|
||||
newOpts := &RendererOptions{}
|
||||
for _, opt := range opts {
|
||||
opt(newOpts)
|
||||
@@ -232,7 +302,7 @@ func NewLocalRenderer(opts ...RendererOption) (Renderer, error) {
|
||||
if err := verifyRendererOptions(newOpts); err != nil {
|
||||
return nil, fmt.Errorf("verify err: %s", err)
|
||||
}
|
||||
return &LocalRenderer{
|
||||
return &LocalChartRenderer{
|
||||
Opts: newOpts,
|
||||
}, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user