feat: add hgctl manifest support (#554)

Signed-off-by: bitliu <bitliu@tencent.com>
This commit is contained in:
Xunzhuo
2023-09-22 15:51:55 +08:00
committed by GitHub
parent f23e26374f
commit ed36a4989f
17 changed files with 424 additions and 199 deletions

View File

@@ -15,10 +15,6 @@
package installer
import (
"fmt"
"io"
"os"
"github.com/alibaba/higress/pkg/cmd/hgctl/helm"
"github.com/alibaba/higress/pkg/cmd/hgctl/util"
"sigs.k8s.io/yaml"
@@ -26,14 +22,9 @@ import (
type ComponentName string
const (
Higress ComponentName = "higress"
Istio ComponentName = "istio"
)
var ComponentMap = map[string]ComponentName{
"higress": Higress,
"istio": Istio,
var ComponentMap = map[ComponentName]struct{}{
Higress: {},
Istio: {},
}
type Component interface {
@@ -57,6 +48,7 @@ type ComponentOptions struct {
RepoURL string
ChartName string
Version string
Quiet bool
}
type ComponentOption func(*ComponentOptions)
@@ -91,186 +83,10 @@ func WithComponentVersion(version string) ComponentOption {
}
}
type HigressComponent struct {
profile *helm.Profile
started bool
opts *ComponentOptions
renderer helm.Renderer
writer io.Writer
}
func (h *HigressComponent) ComponentName() ComponentName {
return Higress
}
func (h *HigressComponent) Namespace() string {
return h.opts.Namespace
}
func (h *HigressComponent) Enabled() bool {
return true
}
func (h *HigressComponent) Run() error {
// Parse latest version
if h.opts.Version == helm.RepoLatestVersion {
latestVersion, err := helm.ParseLatestVersion(h.opts.RepoURL, h.opts.Version)
if err != nil {
return err
}
fmt.Fprintf(h.writer, "⚡️ Fetching Higress Helm Chart latest version \"%s\" \n", latestVersion)
// Reset Helm Chart version
h.opts.Version = latestVersion
h.renderer.SetVersion(latestVersion)
func WithQuiet() ComponentOption {
return func(opts *ComponentOptions) {
opts.Quiet = true
}
fmt.Fprintf(h.writer, "🏄 Downloading Higress Helm Chart version: %s, url: %s\n", h.opts.Version, h.opts.RepoURL)
if err := h.renderer.Init(); err != nil {
return err
}
h.started = true
return nil
}
func (h *HigressComponent) RenderManifest() (string, error) {
if !h.started {
return "", nil
}
fmt.Fprintf(h.writer, "📦 Rendering Higress Helm Chart\n")
valsYaml, err := h.profile.ValuesYaml()
if err != nil {
return "", err
}
manifest, err2 := renderComponentManifest(valsYaml, h.renderer, true, h.ComponentName(), h.opts.Namespace)
if err2 != nil {
return "", err
}
return manifest, nil
}
func NewHigressComponent(profile *helm.Profile, writer io.Writer, opts ...ComponentOption) (Component, error) {
newOpts := &ComponentOptions{}
for _, opt := range opts {
opt(newOpts)
}
var renderer helm.Renderer
var err error
if newOpts.RepoURL != "" {
renderer, err = helm.NewRemoteRenderer(
helm.WithName(newOpts.ChartName),
helm.WithNamespace(newOpts.Namespace),
helm.WithRepoURL(newOpts.RepoURL),
helm.WithVersion(newOpts.Version),
)
if err != nil {
return nil, err
}
} else {
renderer, err = helm.NewLocalRenderer(
helm.WithName(newOpts.ChartName),
helm.WithNamespace(newOpts.Namespace),
helm.WithVersion(newOpts.Version),
helm.WithFS(os.DirFS(newOpts.ChartPath)),
helm.WithDir(string(Higress)),
)
if err != nil {
return nil, err
}
}
higressComponent := &HigressComponent{
profile: profile,
renderer: renderer,
opts: newOpts,
writer: writer,
}
return higressComponent, nil
}
type IstioCRDComponent struct {
profile *helm.Profile
started bool
opts *ComponentOptions
renderer helm.Renderer
writer io.Writer
}
func NewIstioCRDComponent(profile *helm.Profile, writer io.Writer, opts ...ComponentOption) (Component, error) {
newOpts := &ComponentOptions{}
for _, opt := range opts {
opt(newOpts)
}
var renderer helm.Renderer
var err error
if newOpts.RepoURL != "" {
renderer, err = helm.NewRemoteRenderer(
helm.WithName(newOpts.ChartName),
helm.WithNamespace(newOpts.Namespace),
helm.WithRepoURL(newOpts.RepoURL),
helm.WithVersion(newOpts.Version),
)
if err != nil {
return nil, err
}
} else {
renderer, err = helm.NewLocalRenderer(
helm.WithName(newOpts.ChartName),
helm.WithNamespace(newOpts.Namespace),
helm.WithVersion(newOpts.Version),
helm.WithFS(os.DirFS(newOpts.ChartPath)),
helm.WithDir(string(Istio)),
)
if err != nil {
return nil, err
}
}
istioComponent := &IstioCRDComponent{
profile: profile,
renderer: renderer,
opts: newOpts,
writer: writer,
}
return istioComponent, nil
}
func (i *IstioCRDComponent) ComponentName() ComponentName {
return Istio
}
func (i *IstioCRDComponent) Namespace() string {
return i.opts.Namespace
}
func (i *IstioCRDComponent) Enabled() bool {
return true
}
func (i *IstioCRDComponent) Run() error {
fmt.Fprintf(i.writer, "🏄 Downloading Istio Helm Chart version: %s, url: %s\n", i.opts.Version, i.opts.RepoURL)
if err := i.renderer.Init(); err != nil {
return err
}
i.started = true
return nil
}
func (i *IstioCRDComponent) RenderManifest() (string, error) {
if !i.started {
return "", nil
}
fmt.Fprintf(i.writer, "📦 Rendering Istio Helm Chart\n")
values := make(map[string]any)
manifest, err := renderComponentManifest(values, i.renderer, false, i.ComponentName(), i.opts.Namespace)
if err != nil {
return "", err
}
return manifest, nil
}
func renderComponentManifest(spec any, renderer helm.Renderer, addOn bool, name ComponentName, namespace string) (string, error) {

View File

@@ -0,0 +1,131 @@
// Copyright (c) 2022 Alibaba Group Holding Ltd.
//
// 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 installer
import (
"fmt"
"io"
"os"
"github.com/alibaba/higress/pkg/cmd/hgctl/helm"
)
const (
Higress ComponentName = "higress"
)
type HigressComponent struct {
profile *helm.Profile
started bool
opts *ComponentOptions
renderer helm.Renderer
writer io.Writer
}
func (h *HigressComponent) ComponentName() ComponentName {
return Higress
}
func (h *HigressComponent) Namespace() string {
return h.opts.Namespace
}
func (h *HigressComponent) Enabled() bool {
return true
}
func (h *HigressComponent) Run() error {
// Parse latest version
if h.opts.Version == helm.RepoLatestVersion {
latestVersion, err := helm.ParseLatestVersion(h.opts.RepoURL, h.opts.Version)
if err != nil {
return err
}
if !h.opts.Quiet {
fmt.Fprintf(h.writer, "⚡️ Fetching Higress Helm Chart latest version \"%s\" \n", latestVersion)
}
// Reset Helm Chart version
h.opts.Version = latestVersion
h.renderer.SetVersion(latestVersion)
}
if !h.opts.Quiet {
fmt.Fprintf(h.writer, "🏄 Downloading Higress Helm Chart version: %s, url: %s\n", h.opts.Version, h.opts.RepoURL)
}
if err := h.renderer.Init(); err != nil {
return err
}
h.started = true
return nil
}
func (h *HigressComponent) RenderManifest() (string, error) {
if !h.started {
return "", nil
}
if !h.opts.Quiet {
fmt.Fprintf(h.writer, "📦 Rendering Higress Helm Chart\n")
}
valsYaml, err := h.profile.ValuesYaml()
if err != nil {
return "", err
}
manifest, err2 := renderComponentManifest(valsYaml, h.renderer, true, h.ComponentName(), h.opts.Namespace)
if err2 != nil {
return "", err
}
return manifest, nil
}
func NewHigressComponent(profile *helm.Profile, writer io.Writer, opts ...ComponentOption) (Component, error) {
newOpts := &ComponentOptions{}
for _, opt := range opts {
opt(newOpts)
}
var renderer helm.Renderer
var err error
if newOpts.RepoURL != "" {
renderer, err = helm.NewRemoteRenderer(
helm.WithName(newOpts.ChartName),
helm.WithNamespace(newOpts.Namespace),
helm.WithRepoURL(newOpts.RepoURL),
helm.WithVersion(newOpts.Version),
)
if err != nil {
return nil, err
}
} else {
renderer, err = helm.NewLocalRenderer(
helm.WithName(newOpts.ChartName),
helm.WithNamespace(newOpts.Namespace),
helm.WithVersion(newOpts.Version),
helm.WithFS(os.DirFS(newOpts.ChartPath)),
helm.WithDir(string(Higress)),
)
if err != nil {
return nil, err
}
}
higressComponent := &HigressComponent{
profile: profile,
renderer: renderer,
opts: newOpts,
writer: writer,
}
return higressComponent, nil
}

View File

@@ -65,6 +65,17 @@ func (o *Installer) RenderManifests() (map[ComponentName]string, error) {
return res, nil
}
// GenerateManifests generates component manifests to k8s cluster
func (o *Installer) GenerateManifests(manifestMap map[ComponentName]string) error {
if o.kubeCli == nil {
return errors.New("no injected k8s cli into Installer")
}
for _, manifest := range manifestMap {
fmt.Fprint(o.writer, manifest)
}
return nil
}
// ApplyManifests apply component manifests to k8s cluster
func (o *Installer) ApplyManifests(manifestMap map[ComponentName]string) error {
if o.kubeCli == nil {
@@ -152,32 +163,41 @@ func (o *Installer) isNamespacedObject(obj *object.K8sObject) bool {
return false
}
func NewInstaller(profile *helm.Profile, cli kubernetes.CLIClient, writer io.Writer) (*Installer, error) {
func NewInstaller(profile *helm.Profile, cli kubernetes.CLIClient, writer io.Writer, quiet bool) (*Installer, error) {
if profile == nil {
return nil, errors.New("install profile is empty")
}
// initialize components
components := make(map[ComponentName]Component)
higressComponent, err := NewHigressComponent(profile, writer,
opts := []ComponentOption{
WithComponentNamespace(profile.Global.Namespace),
WithComponentChartPath(profile.InstallPackagePath),
WithComponentVersion(profile.Charts.Higress.Version),
WithComponentRepoURL(profile.Charts.Higress.Url),
WithComponentChartName(profile.Charts.Higress.Name),
)
}
if quiet {
opts = append(opts, WithQuiet())
}
higressComponent, err := NewHigressComponent(profile, writer, opts...)
if err != nil {
return nil, fmt.Errorf("NewHigressComponent failed, err: %s", err)
}
components[Higress] = higressComponent
if profile.IstioEnabled() {
istioCRDComponent, err := NewIstioCRDComponent(profile, writer,
opts := []ComponentOption{
WithComponentNamespace(profile.Global.IstioNamespace),
WithComponentChartPath(profile.InstallPackagePath),
WithComponentVersion(profile.Charts.Istio.Version),
WithComponentRepoURL(profile.Charts.Istio.Url),
WithComponentChartName(profile.Charts.Istio.Name),
)
}
if quiet {
opts = append(opts, WithQuiet())
}
istioCRDComponent, err := NewIstioCRDComponent(profile, writer, opts...)
if err != nil {
return nil, fmt.Errorf("NewIstioCRDComponent failed, err: %s", err)
}

View File

@@ -0,0 +1,113 @@
// Copyright (c) 2022 Alibaba Group Holding Ltd.
//
// 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 installer
import (
"fmt"
"io"
"os"
"github.com/alibaba/higress/pkg/cmd/hgctl/helm"
)
const (
Istio ComponentName = "istio"
)
type IstioCRDComponent struct {
profile *helm.Profile
started bool
opts *ComponentOptions
renderer helm.Renderer
writer io.Writer
}
func NewIstioCRDComponent(profile *helm.Profile, writer io.Writer, opts ...ComponentOption) (Component, error) {
newOpts := &ComponentOptions{}
for _, opt := range opts {
opt(newOpts)
}
var renderer helm.Renderer
var err error
if newOpts.RepoURL != "" {
renderer, err = helm.NewRemoteRenderer(
helm.WithName(newOpts.ChartName),
helm.WithNamespace(newOpts.Namespace),
helm.WithRepoURL(newOpts.RepoURL),
helm.WithVersion(newOpts.Version),
)
if err != nil {
return nil, err
}
} else {
renderer, err = helm.NewLocalRenderer(
helm.WithName(newOpts.ChartName),
helm.WithNamespace(newOpts.Namespace),
helm.WithVersion(newOpts.Version),
helm.WithFS(os.DirFS(newOpts.ChartPath)),
helm.WithDir(string(Istio)),
)
if err != nil {
return nil, err
}
}
istioComponent := &IstioCRDComponent{
profile: profile,
renderer: renderer,
opts: newOpts,
writer: writer,
}
return istioComponent, nil
}
func (i *IstioCRDComponent) ComponentName() ComponentName {
return Istio
}
func (i *IstioCRDComponent) Namespace() string {
return i.opts.Namespace
}
func (i *IstioCRDComponent) Enabled() bool {
return true
}
func (i *IstioCRDComponent) Run() error {
if !i.opts.Quiet {
fmt.Fprintf(i.writer, "🏄 Downloading Istio Helm Chart version: %s, url: %s\n", i.opts.Version, i.opts.RepoURL)
}
if err := i.renderer.Init(); err != nil {
return err
}
i.started = true
return nil
}
func (i *IstioCRDComponent) RenderManifest() (string, error) {
if !i.started {
return "", nil
}
if !i.opts.Quiet {
fmt.Fprintf(i.writer, "📦 Rendering Istio Helm Chart\n")
}
values := make(map[string]any)
manifest, err := renderComponentManifest(values, i.renderer, false, i.ComponentName(), i.opts.Namespace)
if err != nil {
return "", err
}
return manifest, nil
}