This is an automated email from the ASF dual-hosted git repository.

zhongxjian pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/dubbo-kubernetes.git


The following commit(s) were added to refs/heads/master by this push:
     new 2ef144dc [operator] Supplementary code comments v3 (#647)
2ef144dc is described below

commit 2ef144dcd9cd77c01275f3ce79360489c194f0f6
Author: Jian Zhong <[email protected]>
AuthorDate: Wed Mar 19 17:30:49 2025 +0800

    [operator] Supplementary code comments v3 (#647)
---
 operator/cmd/cluster/install.go          |   2 +-
 operator/cmd/cluster/uninstall.go        |   2 +-
 operator/pkg/helm/helm.go                |   2 +-
 operator/pkg/install/installer.go        |   2 +-
 operator/pkg/install/waiter.go           |   4 +-
 operator/pkg/schema/impl.go              |  47 ++++----
 operator/pkg/uninstall/uninstaller.go    |  12 ++
 operator/pkg/util/clog/clog.go           |  17 ++-
 operator/pkg/util/common.go              |  24 ----
 operator/pkg/util/dmultierr/dmultierr.go |   2 +
 operator/pkg/util/errs.go                |  16 +++
 operator/pkg/util/k8s.go                 |   3 +
 operator/pkg/util/label.go               |   1 +
 operator/pkg/util/pointer/pointer.go     |   4 +
 operator/pkg/util/progress/progress.go   |  63 +++++++---
 operator/pkg/values/map.go               | 199 +++++++++++++++++--------------
 16 files changed, 233 insertions(+), 167 deletions(-)

diff --git a/operator/cmd/cluster/install.go b/operator/cmd/cluster/install.go
index 91d7ec26..de41ab33 100644
--- a/operator/cmd/cluster/install.go
+++ b/operator/cmd/cluster/install.go
@@ -123,7 +123,7 @@ func Install(kubeClient kube.CLIClient, rootArgs *RootArgs, 
iArgs *installArgs,
                Kube:         kubeClient,
                Values:       vals,
                WaitTimeout:  iArgs.waitTimeout,
-               ProgressInfo: progress.NewInfo(),
+               ProgressInfo: progress.NewLog(),
                Logger:       cl,
        }
        if err := i.InstallManifests(manifests); err != nil {
diff --git a/operator/cmd/cluster/uninstall.go 
b/operator/cmd/cluster/uninstall.go
index 91987a6f..570fc017 100644
--- a/operator/cmd/cluster/uninstall.go
+++ b/operator/cmd/cluster/uninstall.go
@@ -91,7 +91,7 @@ func Uninstall(cmd *cobra.Command, ctx cli.Context, rootArgs 
*RootArgs, uiArgs *
                return err
        }
 
-       pl := progress.NewInfo()
+       pl := progress.NewLog()
        if uiArgs.remove && uiArgs.filenames != "" {
                cl.LogAndPrint("Purge uninstall will remove all Dubbo 
resources, ignoring the specified revision or operator file")
        }
diff --git a/operator/pkg/helm/helm.go b/operator/pkg/helm/helm.go
index 49a9ce93..54437411 100644
--- a/operator/pkg/helm/helm.go
+++ b/operator/pkg/helm/helm.go
@@ -82,7 +82,7 @@ func renderChart(namespace string, chrtVals values.Map, chrt 
*chart.Chart) ([]st
        }
        crdFiles := chrt.CRDObjects()
        if chrt.Metadata.Name == BaseChartName {
-               values.GetPathHelper[bool](chrtVals, 
"base.enableDubboConfigCRD")
+               values.GetPathAs[bool](chrtVals, "base.enableDubboConfigCRD")
        }
        var warnings Warnings
        keys := make([]string, 0, len(files))
diff --git a/operator/pkg/install/installer.go 
b/operator/pkg/install/installer.go
index 4a4348a3..9b23797b 100644
--- a/operator/pkg/install/installer.go
+++ b/operator/pkg/install/installer.go
@@ -45,7 +45,7 @@ type Installer struct {
        SkipWait     bool
        Kube         kube.CLIClient
        Values       values.Map
-       ProgressInfo *progress.Info
+       ProgressInfo *progress.Log
        WaitTimeout  time.Duration
        Logger       clog.Logger
 }
diff --git a/operator/pkg/install/waiter.go b/operator/pkg/install/waiter.go
index c506111d..279be738 100644
--- a/operator/pkg/install/waiter.go
+++ b/operator/pkg/install/waiter.go
@@ -43,7 +43,7 @@ type deployment struct {
 
 // WaitForResources polls to get the current status of various objects that 
are not immediately ready
 // until all are ready or a timeout is reached.
-func WaitForResources(objects []manifest.Manifest, client kube.CLIClient, 
waitTimeout time.Duration, dryRun bool, l *progress.ManifestInfo) error {
+func WaitForResources(objects []manifest.Manifest, client kube.CLIClient, 
waitTimeout time.Duration, dryRun bool, l *progress.ManifestLog) error {
        if dryRun {
                return nil
        }
@@ -78,7 +78,7 @@ func WaitForResources(objects []manifest.Manifest, client 
kube.CLIClient, waitTi
        return nil
 }
 
-func waitForResources(objects []manifest.Manifest, k kube.Client, l 
*progress.ManifestInfo) (bool, []string, map[string]string, error) {
+func waitForResources(objects []manifest.Manifest, k kube.Client, l 
*progress.ManifestLog) (bool, []string, map[string]string, error) {
        pods := []corev1.Pod{}
        deployments := []deployment{}
        statefulsets := []*appsv1.StatefulSet{}
diff --git a/operator/pkg/schema/impl.go b/operator/pkg/schema/impl.go
index 68af51f3..e2e847c7 100644
--- a/operator/pkg/schema/impl.go
+++ b/operator/pkg/schema/impl.go
@@ -31,7 +31,7 @@ type schemaImpl struct {
        gvk            config.GroupVersionKind
        plural         string
        clusterScoped  bool
-       goPkg          string
+       goPackage      string
        proto          string
        versionAliases []string
        apiVersion     string
@@ -39,6 +39,16 @@ type schemaImpl struct {
        statusType     reflect.Type
 }
 
+// Schema for a resource.
+type Schema interface {
+       fmt.Stringer
+       GroupVersionResource() schema.GroupVersionResource
+       GroupVersionKind() config.GroupVersionKind
+       GroupVersionAliasKinds() []config.GroupVersionKind
+       Validate() error
+       IsClusterScoped() bool
+}
+
 func (s *schemaImpl) GroupVersionAliasKinds() []config.GroupVersionKind {
        gvks := make([]config.GroupVersionKind, len(s.versionAliases))
        for i, va := range s.versionAliases {
@@ -49,8 +59,12 @@ func (s *schemaImpl) GroupVersionAliasKinds() 
[]config.GroupVersionKind {
        return gvks
 }
 
-func (s *schemaImpl) IsClusterScoped() bool {
-       return s.clusterScoped
+func (s *schemaImpl) String() string {
+       return fmt.Sprintf("[Schema](%s, %q, %s)", s.Kind(), s.goPackage, 
s.proto)
+}
+
+func (s *schemaImpl) APIVersion() string {
+       return s.apiVersion
 }
 
 func (s *schemaImpl) Kind() string {
@@ -69,16 +83,12 @@ func (s *schemaImpl) Plural() string {
        return s.plural
 }
 
-func (s *schemaImpl) GroupVersionKind() config.GroupVersionKind {
-       return s.gvk
-}
-
-func (s *schemaImpl) InClusterScoped() bool {
+func (s *schemaImpl) IsClusterScoped() bool {
        return s.clusterScoped
 }
 
-func (s *schemaImpl) String() string {
-       return fmt.Sprintf("[Schema](%s, %q, %s)", s.Kind(), s.goPkg, s.proto)
+func (s *schemaImpl) GroupVersionKind() config.GroupVersionKind {
+       return s.gvk
 }
 
 func (s *schemaImpl) GroupVersionResource() schema.GroupVersionResource {
@@ -96,6 +106,7 @@ func (s *schemaImpl) Validate() (err error) {
        return
 }
 
+// Builder for a Schema.
 type Builder struct {
        Identifier    string
        Plural        string
@@ -111,6 +122,7 @@ type Builder struct {
        Synthetic     bool
 }
 
+// BuildNoValidate builds the Schema without checking the fields.
 func (b Builder) BuildNoValidate() Schema {
        return &schemaImpl{
                gvk: config.GroupVersionKind{
@@ -120,7 +132,7 @@ func (b Builder) BuildNoValidate() Schema {
                },
                plural:        b.Plural,
                clusterScoped: b.ClusterScoped,
-               goPkg:         b.ProtoPackage,
+               goPackage:     b.ProtoPackage,
                proto:         b.Proto,
                apiVersion:    b.Group + "/" + b.Version,
                reflectType:   b.ReflectType,
@@ -128,14 +140,17 @@ func (b Builder) BuildNoValidate() Schema {
        }
 }
 
+// Build a Schema instance.
 func (b Builder) Build() (Schema, error) {
        s := b.BuildNoValidate()
+       // Validate the schema.
        if err := s.Validate(); err != nil {
                return nil, err
        }
        return s, nil
 }
 
+// MustBuild calls Build and panics if it fails.
 func (b Builder) MustBuild() Schema {
        s, err := b.Build()
        if err != nil {
@@ -144,17 +159,9 @@ func (b Builder) MustBuild() Schema {
        return s
 }
 
-type Schema interface {
-       fmt.Stringer
-       GroupVersionResource() schema.GroupVersionResource
-       GroupVersionKind() config.GroupVersionKind
-       GroupVersionAliasKinds() []config.GroupVersionKind
-       Validate() error
-       IsClusterScoped() bool
-}
-
 var protoMessageType = protoregistry.GlobalTypes.FindMessageByName
 
+// getProtoMessageType returns the Go lang type of the proto with the 
specified name.
 func getProtoMessageType(protoMessageName string) reflect.Type {
        t, err := protoMessageType(protoreflect.FullName(protoMessageName))
        if err != nil || t == nil {
diff --git a/operator/pkg/uninstall/uninstaller.go 
b/operator/pkg/uninstall/uninstaller.go
index 5d1809a3..92321a72 100644
--- a/operator/pkg/uninstall/uninstaller.go
+++ b/operator/pkg/uninstall/uninstaller.go
@@ -35,19 +35,29 @@ import (
 )
 
 var (
+       // ClusterResources are resource types the operator prunes, ordered by 
which types should be deleted, first to last.
        ClusterResources = []schema.GroupVersionKind{
                {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: 
"ClusterRole"},
                {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: 
"ClusterRoleBinding"},
+               // Cannot currently prune CRDs because this will also wipe out 
user config.
+               // {Group: "apiextensions.k8s.io", Version: "v1beta1", Kind: 
name.CRDStr},
        }
+       // ClusterControlPlaneResources lists cluster scope resources types 
which should be deleted during uninstall command.
        ClusterControlPlaneResources = []schema.GroupVersionKind{
                {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: 
"ClusterRole"},
                {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: 
"ClusterRoleBinding"},
        }
+       // AllClusterResources lists all cluster scope resources types which 
should be deleted in purge case, including CRD.
        AllClusterResources = append(ClusterResources,
                gvk.CustomResourceDefinition.Kubernetes(),
        )
 )
 
+// GetRemovedResources get the list of resources to be removed
+// 1. if includeClusterResources is false, we list the namespaced resources by 
matching revision and component labels.
+// 2. if includeClusterResources is true, we list the namespaced and cluster 
resources by component labels only.
+// If componentName is not empty, only resources associated with specific 
components would be returned
+// UnstructuredList of objects and corresponding list of name kind hash of 
k8sObjects would be returned
 func GetRemovedResources(kc kube.CLIClient, dopName, dopNamespace string, 
includeClusterResources bool) ([]*unstructured.UnstructuredList, error) {
        var usList []*unstructured.UnstructuredList
        labels := make(map[string]string)
@@ -85,6 +95,7 @@ func GetRemovedResources(kc kube.CLIClient, dopName, 
dopNamespace string, includ
        return usList, nil
 }
 
+// NamespacedResources gets specific pruning resources based on the k8s version
 func NamespacedResources() []schema.GroupVersionKind {
        res := []schema.GroupVersionKind{
                gvk.Deployment.Kubernetes(),
@@ -103,6 +114,7 @@ func PrunedResourcesSchemas() []schema.GroupVersionKind {
        return append(NamespacedResources(), ClusterResources...)
 }
 
+// DeleteObjectsList removed resources that are in the slice of 
UnstructuredList.
 func DeleteObjectsList(c kube.CLIClient, dryRun bool, log clog.Logger, 
objectsList []*unstructured.UnstructuredList) error {
        var errs util.Errors
        for _, ol := range objectsList {
diff --git a/operator/pkg/util/clog/clog.go b/operator/pkg/util/clog/clog.go
index cae20d09..826934df 100644
--- a/operator/pkg/util/clog/clog.go
+++ b/operator/pkg/util/clog/clog.go
@@ -24,12 +24,7 @@ import (
        "os"
 )
 
-type ConsoleLogger struct {
-       stdOut io.Writer
-       stdErr io.Writer
-       scope  *log.Scope
-}
-
+// Logger provides optional log taps for console and test buffer outputs.
 type Logger interface {
        LogAndPrint(v ...any)
        LogAndError(v ...any)
@@ -40,6 +35,15 @@ type Logger interface {
        Print(s string)
 }
 
+// ConsoleLogger is the struct used for mesh command.
+type ConsoleLogger struct {
+       stdOut io.Writer
+       stdErr io.Writer
+       scope  *log.Scope
+}
+
+// NewConsoleLogger creates a new logger and returns a pointer to it.
+// stdOut and stdErr can be used to capture output for testing.
 func NewConsoleLogger(stdOut, stdErr io.Writer, scope *log.Scope) 
*ConsoleLogger {
        s := scope
        if s == nil {
@@ -52,6 +56,7 @@ func NewConsoleLogger(stdOut, stdErr io.Writer, scope 
*log.Scope) *ConsoleLogger
        }
 }
 
+// NewDefaultLogger creates a new logger that outputs to stdout/stderr at 
default scope.
 func NewDefaultLogger() *ConsoleLogger {
        return NewConsoleLogger(os.Stdout, os.Stderr, nil)
 }
diff --git a/operator/pkg/util/common.go b/operator/pkg/util/common.go
deleted file mode 100644
index 658df579..00000000
--- a/operator/pkg/util/common.go
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 util
-
-import "strings"
-
-func IsFilePath(path string) bool {
-       return strings.Contains(path, "/") || strings.Contains(path, ".")
-}
diff --git a/operator/pkg/util/dmultierr/dmultierr.go 
b/operator/pkg/util/dmultierr/dmultierr.go
index 58cf8b0d..2eecd4cd 100644
--- a/operator/pkg/util/dmultierr/dmultierr.go
+++ b/operator/pkg/util/dmultierr/dmultierr.go
@@ -23,6 +23,8 @@ import (
        "strings"
 )
 
+// MultiErrorFormat provides a format for multierrors. This matches the 
default format, but if there
+// is only one error we will not expand to multiple lines.
 func MultiErrorFormat() multierror.ErrorFormatFunc {
        return func(errors []error) string {
                if len(errors) == 1 {
diff --git a/operator/pkg/util/errs.go b/operator/pkg/util/errs.go
index 18e2e5c6..93dc4409 100644
--- a/operator/pkg/util/errs.go
+++ b/operator/pkg/util/errs.go
@@ -23,12 +23,20 @@ const (
        defaultSeparator = ", "
 )
 
+// Errors is a slice of error.
 type Errors []error
 
+// String implements the stringer#String method.
+func (e Errors) String() string {
+       return e.Error()
+}
+
+// Error implements the error#Error method.
 func (e Errors) Error() string {
        return ToString(e, defaultSeparator)
 }
 
+// ToError returns an error from Errors.
 func (e Errors) ToError() error {
        if len(e) == 0 {
                return nil
@@ -36,6 +44,8 @@ func (e Errors) ToError() error {
        return fmt.Errorf("%s", e)
 }
 
+// ToString returns a string representation of errors, with elements separated 
by separator string. Any nil errors in the
+// slice are skipped.
 func ToString(errors []error, separator string) string {
        var out string
        for i, e := range errors {
@@ -50,6 +60,8 @@ func ToString(errors []error, separator string) string {
        return out
 }
 
+// NewErrs returns a slice of error with a single element err.
+// If err is nil, returns nil.
 func NewErrs(err error) Errors {
        if err == nil {
                return nil
@@ -57,6 +69,8 @@ func NewErrs(err error) Errors {
        return []error{err}
 }
 
+// AppendErr appends err to errors if it is not nil and returns the result.
+// If err is nil, it is not appended.
 func AppendErr(errors []error, err error) Errors {
        if err == nil {
                if len(errors) == 0 {
@@ -67,6 +81,8 @@ func AppendErr(errors []error, err error) Errors {
        return append(errors, err)
 }
 
+// AppendErrs appends newErrs to errors and returns the result.
+// If newErrs is empty, nothing is appended.
 func AppendErrs(errors []error, newErrs []error) Errors {
        if len(newErrs) == 0 {
                return errors
diff --git a/operator/pkg/util/k8s.go b/operator/pkg/util/k8s.go
index 6c96c65b..9b63429e 100644
--- a/operator/pkg/util/k8s.go
+++ b/operator/pkg/util/k8s.go
@@ -26,13 +26,16 @@ import (
        "k8s.io/client-go/kubernetes"
 )
 
+// CreateNamespace creates a namespace using the given k8s interface.
 func CreateNamespace(cs kubernetes.Interface, namespace string, dryRun bool) 
error {
        if dryRun {
                return nil
        }
        if namespace == "" {
+               // Setup default namespace.
                namespace = "dubbo-system"
        }
+       // check if the namespace already exists. If yes, do nothing. If no, 
create a new one.
        if _, err := cs.CoreV1().Namespaces().Get(context.TODO(), namespace, 
metav1.GetOptions{}); err != nil {
                if errors.IsNotFound(err) {
                        ns := &v1.Namespace{ObjectMeta: metav1.ObjectMeta{
diff --git a/operator/pkg/util/label.go b/operator/pkg/util/label.go
index 40465cb9..f92db623 100644
--- a/operator/pkg/util/label.go
+++ b/operator/pkg/util/label.go
@@ -22,6 +22,7 @@ import (
        "k8s.io/apimachinery/pkg/runtime"
 )
 
+// SetLabel is a helper function which sets the specified label and value on 
the specified object.
 func SetLabel(resource runtime.Object, label, value string) error {
        resourceAccessor, err := meta.Accessor(resource)
        if err != nil {
diff --git a/operator/pkg/util/pointer/pointer.go 
b/operator/pkg/util/pointer/pointer.go
index 40f73e1c..a0291508 100644
--- a/operator/pkg/util/pointer/pointer.go
+++ b/operator/pkg/util/pointer/pointer.go
@@ -17,15 +17,19 @@
 
 package pointer
 
+// Of returns a pointer to the input. In most cases, callers should just do 
&t. However, in some cases
+// Go cannot take a pointer. For example, `ptr.Of(f())`.
 func Of[T any](t T) *T {
        return &t
 }
 
+// Empty returns an empty T type
 func Empty[T any]() T {
        var empty T
        return empty
 }
 
+// NonEmptyOrDefault returns t if its non-empty, or else def.
 func NonEmptyOrDefault[T comparable](t T, def T) T {
        var empty T
        if t != empty {
diff --git a/operator/pkg/util/progress/progress.go 
b/operator/pkg/util/progress/progress.go
index d84c733e..48422465 100644
--- a/operator/pkg/util/progress/progress.go
+++ b/operator/pkg/util/progress/progress.go
@@ -29,6 +29,8 @@ import (
 
 type InstallState int
 
+const inProgress = `{{ yellow (cycle . "-" "-" "-" " ") }} `
+
 const (
        StateInstalling InstallState = iota
        StatePruning
@@ -36,24 +38,28 @@ const (
        StateUninstallComplete
 )
 
-const inProgress = `{{ yellow (cycle . "-" "-" "-" " ") }} `
-
-type Info struct {
-       components map[string]*ManifestInfo
+// Log records the progress of an installation
+// This aims to provide information about the install of multiple components 
in parallel, while working
+// around the limitations of the pb library, which will only support single 
lines. To do this, we aggregate
+// the current components into a single line, and as components complete there 
final state is persisted to a new line.
+type Log struct {
+       components map[string]*ManifestLog
        state      InstallState
        bar        *pb.ProgressBar
        mu         sync.Mutex
        template   string
 }
 
-func NewInfo() *Info {
-       return &Info{
-               components: map[string]*ManifestInfo{},
+func NewLog() *Log {
+       return &Log{
+               components: map[string]*ManifestLog{},
                bar:        createBar(),
        }
 }
 
-func (i *Info) createStatus(maxWidth int) string {
+// createStatus will return a string to report the current status.
+// ex: - Processing resources for components. Waiting for foo, bar.
+func (i *Log) createStatus(maxWidth int) string {
        comps := make([]string, 0, len(i.components))
        wait := make([]string, 0, len(i.components))
        for c, l := range i.components {
@@ -68,17 +74,20 @@ func (i *Info) createStatus(maxWidth int) string {
        }
        prefix := inProgress
        if !i.bar.GetBool(pb.Terminal) {
+               // If we aren't a terminal, no need to spam extra lines
                prefix = `{{ yellow "-" }} `
        }
+       // reduce by 2 to allow for the "- " that will be added below
        maxWidth -= 2
        if maxWidth > 0 && len(msg) > maxWidth {
                return prefix + msg[:maxWidth-3] + "..."
        }
+       // cycle will alternate between "-" and " ". "-" is given multiple 
times to avoid quick flashing back and forth
        return prefix + msg
 }
 
-func (i *Info) NewComponent(comp string) *ManifestInfo {
-       mi := &ManifestInfo{
+func (i *Log) NewComponent(comp string) *ManifestLog {
+       mi := &ManifestLog{
                report: i.reportProgress(comp),
        }
        i.mu.Lock()
@@ -86,13 +95,20 @@ func (i *Info) NewComponent(comp string) *ManifestInfo {
        i.components[comp] = mi
        return mi
 }
-func (i *Info) reportProgress(componentName string) func() {
+
+// reportProgress will report an update for a given component
+// Because the bar library does not support multiple lines/bars at once, we 
need to aggregate current
+// progress into a single line. For example "Waiting for x, y, z". Once a 
component completes, we want
+// a new line created so the information is not lost. To do this, we spin up a 
new bar with the remaining components
+// on a new line, and create a new bar. For example, this becomes "x 
succeeded", "waiting for y, z".
+func (i *Log) reportProgress(componentName string) func() {
        return func() {
                compName := component.Name(componentName)
                cliName := component.UserFacingCompName(compName)
                i.mu.Lock()
                defer i.mu.Unlock()
                comp := i.components[componentName]
+               // The component has completed
                comp.mu.Lock()
                finished := comp.finished
                compErr := comp.err
@@ -107,7 +123,9 @@ func (i *Info) reportProgress(componentName string) func() {
                        } else {
                                i.SetMessage(fmt.Sprintf(`{{ read "✘" }} %s 
encountered an error: %s`, cliName, compErr), true)
                        }
+                       // Close the bar out, outputting a new line
                        delete(i.components, componentName)
+                       // Now we create a new bar, which will have the 
remaining components
                        i.bar = createBar()
                        return
                }
@@ -115,7 +133,9 @@ func (i *Info) reportProgress(componentName string) func() {
        }
 }
 
-func (i *Info) SetMessage(status string, finish bool) {
+func (i *Log) SetMessage(status string, finish bool) {
+       // if we are not a terminal and there is no change, do not write
+       // This avoids redundant lines
        if !i.bar.GetBool(pb.Terminal) && status == i.template {
                return
        }
@@ -127,7 +147,7 @@ func (i *Info) SetMessage(status string, finish bool) {
        i.bar.Write()
 }
 
-func (i *Info) SetState(state InstallState) {
+func (i *Log) SetState(state InstallState) {
        i.mu.Lock()
        defer i.mu.Unlock()
        i.state = state
@@ -143,22 +163,27 @@ func (i *Info) SetState(state InstallState) {
        }
 }
 
+// For testing only
 var testWriter *io.Writer
 
 func createBar() *pb.ProgressBar {
+       // Don't set a total and use Static so we can explicitly control when 
you write. This is needed
+       // for handling the multiline issues.
        bar := pb.New(0)
        bar.Set(pb.Static, true)
        if testWriter != nil {
                bar.SetWriter(*testWriter)
        }
        bar.Start()
+       // if we aren't a terminal, we will return a new line for each new 
message
        if !bar.GetBool(pb.Terminal) {
                bar.Set(pb.ReturnSymbol, "\n")
        }
        return bar
 }
 
-type ManifestInfo struct {
+// ManifestLog records progress for a single component
+type ManifestLog struct {
        report   func()
        err      string
        waiting  []string
@@ -166,13 +191,13 @@ type ManifestInfo struct {
        mu       sync.Mutex
 }
 
-func (mi *ManifestInfo) ReportProgress() {
+func (mi *ManifestLog) ReportProgress() {
        if mi == nil {
                return
        }
 }
 
-func (mi *ManifestInfo) ReportFinished() {
+func (mi *ManifestLog) ReportFinished() {
        if mi == nil {
                return
        }
@@ -182,7 +207,7 @@ func (mi *ManifestInfo) ReportFinished() {
        mi.report()
 }
 
-func (mi *ManifestInfo) ReportError(err string) {
+func (mi *ManifestLog) ReportError(err string) {
        if mi == nil {
                return
        }
@@ -192,7 +217,7 @@ func (mi *ManifestInfo) ReportError(err string) {
        mi.report()
 }
 
-func (mi *ManifestInfo) ReportWaiting(resources []string) {
+func (mi *ManifestLog) ReportWaiting(resources []string) {
        if mi == nil {
                return
        }
@@ -202,7 +227,7 @@ func (mi *ManifestInfo) ReportWaiting(resources []string) {
        mi.report()
 }
 
-func (p *ManifestInfo) waitingResources() []string {
+func (p *ManifestLog) waitingResources() []string {
        p.mu.Lock()
        defer p.mu.Unlock()
        return p.waiting
diff --git a/operator/pkg/values/map.go b/operator/pkg/values/map.go
index 8b87cc22..33de0b67 100644
--- a/operator/pkg/values/map.go
+++ b/operator/pkg/values/map.go
@@ -28,8 +28,10 @@ import (
        "strings"
 )
 
+// Map is a wrapper around an untyped map, used throughout the operator 
codebase for generic access.
 type Map map[string]any
 
+// JSON serializes a Map to a JSON string.
 func (m Map) JSON() string {
        bytes, err := json.Marshal(m)
        if err != nil {
@@ -38,6 +40,7 @@ func (m Map) JSON() string {
        return string(bytes)
 }
 
+// YAML serializes a Map to a YAML string.
 func (m Map) YAML() string {
        bytes, err := yaml.Marshal(m)
        if err != nil {
@@ -46,6 +49,7 @@ func (m Map) YAML() string {
        return string(bytes)
 }
 
+// MapFromJSON constructs a Map from JSON
 func MapFromJSON(input []byte) (Map, error) {
        m := make(Map)
        err := json.Unmarshal(input, &m)
@@ -55,6 +59,7 @@ func MapFromJSON(input []byte) (Map, error) {
        return m, nil
 }
 
+// MapFromYAML constructs a Map from YAML
 func MapFromYAML(input []byte) (Map, error) {
        m := make(Map)
        err := yaml.Unmarshal(input, &m)
@@ -64,17 +69,6 @@ func MapFromYAML(input []byte) (Map, error) {
        return m, nil
 }
 
-func fromJSON[T any](overlay []byte) (T, error) {
-       v := new(T)
-       err := json.Unmarshal(overlay, &v)
-       if err != nil {
-               return pointer.Empty[T](), err
-       }
-       return *v, nil
-}
-
-func parsePath(key string) []string { return strings.Split(key, ".") }
-
 func tableLookup(m Map, simple string) (Map, bool) {
        v, ok := m[simple]
        if !ok {
@@ -83,12 +77,17 @@ func tableLookup(m Map, simple string) (Map, bool) {
        if vv, ok := v.(map[string]interface{}); ok {
                return vv, true
        }
+       // This catches a case where a value is of type Values, but doesn't 
(for some
+       // reason) match the map[string]interface{}. This has been observed in 
the
+       // wild, and might be a result of a nil map of type Values.
        if vv, ok := v.(Map); ok {
                return vv, true
        }
        return nil, false
 }
 
+func parsePath(key string) []string { return strings.Split(key, ".") }
+
 func (m Map) GetPathMap(s string) (Map, bool) {
        current := m
        for _, n := range parsePath(s) {
@@ -128,10 +127,12 @@ func splitPath(path string) []string {
        for _, str := range pv {
                if str != "" {
                        str = strings.ReplaceAll(str, "\\.", ".")
+                       // Is str of the form node[expr], convert to "node", 
"[expr]"?
                        nBracket := strings.IndexRune(str, '[')
                        if nBracket > 0 {
                                r = append(r, str[:nBracket], str[nBracket:])
                        } else {
+                               // str is "[expr]" or "node"
                                r = append(r, str)
                        }
                }
@@ -139,15 +140,9 @@ func splitPath(path string) []string {
        return r
 }
 
-func (m Map) GetPathString(s string) string {
-       return GetPathHelper[string](m, s)
-}
-
-func (m Map) GetPathStringOr(s string, def string) string {
-       return pointer.NonEmptyOrDefault(m.GetPathString(s), def)
-}
-
-func GetPathHelper[T any](m Map, name string) T {
+// GetPathAs is a helper function to get a patch value and cast it to a 
specified type.
+// If the path is not found, or the cast fails, the zero value is returned.
+func GetPathAs[T any](m Map, name string) T {
        v, ok := m.GetPath(name)
        if !ok {
                return pointer.Empty[T]()
@@ -156,6 +151,17 @@ func GetPathHelper[T any](m Map, name string) T {
        return t
 }
 
+// GetPathString is a helper around TryGetPathAs[string] to allow usage as a 
method (otherwise impossible with generics)
+func (m Map) GetPathString(s string) string {
+       return GetPathAs[string](m, s)
+}
+
+// GetPathStringOr is a helper around TryGetPathAs[string] to allow usage as a 
method (otherwise impossible with generics),
+// with an allowance for a default value if it is not found/not set.
+func (m Map) GetPathStringOr(s string, def string) string {
+       return pointer.NonEmptyOrDefault(m.GetPathString(s), def)
+}
+
 func (m Map) GetPath(name string) (any, bool) {
        current := any(m)
        paths := splitPath(name)
@@ -204,6 +210,7 @@ func (m Map) GetPath(name string) (any, bool) {
        return current, true
 }
 
+// MustCastAsMap casts a value to a Map; if the value is not a map, it will 
panic..
 func MustCastAsMap(current any) Map {
        m, ok := CastAsMap(current)
        if !ok {
@@ -215,6 +222,7 @@ func MustCastAsMap(current any) Map {
        return m
 }
 
+// CastAsMap casts a value to a Map, if possible.
 func CastAsMap(current any) (Map, bool) {
        if m, ok := current.(Map); ok {
                return m, true
@@ -225,48 +233,31 @@ func CastAsMap(current any) (Map, bool) {
        return nil, false
 }
 
+// ConvertMap translates a Map to a T, via JSON
 func ConvertMap[T any](m Map) (T, error) {
        return fromJSON[T]([]byte(m.JSON()))
 }
 
-func extractIndex(seg string) (int, bool) {
-       if !strings.HasPrefix(seg, "[") || !strings.HasSuffix(seg, "]") {
-               return 0, false
-       }
-       sanitized := seg[1 : len(seg)-1]
-       v, err := strconv.Atoi(sanitized)
+func fromJSON[T any](overlay []byte) (T, error) {
+       v := new(T)
+       err := json.Unmarshal(overlay, &v)
        if err != nil {
-               return 0, false
-       }
-       return v, true
-}
-
-func extractKeyValue(seg string) (string, string, bool) {
-       if !strings.HasPrefix(seg, "[") || !strings.HasSuffix(seg, "]") {
-               return "", "", false
+               return pointer.Empty[T](), err
        }
-       sanitized := seg[1 : len(seg)-1]
-       return strings.Cut(sanitized, ":")
+       return *v, nil
 }
 
-func (m Map) MergeFrom(other Map) {
-       for k, v := range other {
-               if vm, ok := v.(Map); ok {
-                       v = map[string]any(vm)
-               }
-               if v, ok := v.(map[string]any); ok {
-                       if bv, ok := m[k]; ok {
-
-                               if bv, ok := bv.(map[string]any); ok {
-                                       Map(bv).MergeFrom(v)
-                                       continue
-                               }
-                       }
-               }
-               m[k] = v
+// getPV returns the path and value components for the given set flag string, 
which must be in path=value format.
+func getPV(setFlag string) (path string, value string) {
+       pv := strings.Split(setFlag, "=")
+       if len(pv) != 2 {
+               return setFlag, ""
        }
+       path, value = strings.TrimSpace(pv[0]), strings.TrimSpace(pv[1])
+       return
 }
 
+// SetPath applies values from a path like `key.subkey`, `key.[0].var`, or 
`key.[name:foo]`.
 func (m Map) SetPath(paths string, value any) error {
        path := splitPath(paths)
        base := m
@@ -276,6 +267,7 @@ func (m Map) SetPath(paths string, value any) error {
        return nil
 }
 
+// SetPaths applies values from input like `key.subkey=val`
 func (m Map) SetPaths(paths ...string) error {
        for _, sf := range paths {
                p, v := getPV(sf)
@@ -291,40 +283,7 @@ func (m Map) SetPaths(paths ...string) error {
        return nil
 }
 
-func getPV(setFlag string) (path string, value string) {
-       pv := strings.Split(setFlag, "=")
-       if len(pv) != 2 {
-               return setFlag, ""
-       }
-       path, value = strings.TrimSpace(pv[0]), strings.TrimSpace(pv[1])
-       return
-}
-
-func parseValue(valueStr string) any {
-       var value any
-       if v, err := strconv.Atoi(valueStr); err == nil {
-               value = v
-       } else if v, err := strconv.ParseFloat(valueStr, 64); err == nil {
-               value = v
-       } else if v, err := strconv.ParseBool(valueStr); err == nil {
-               value = v
-       } else {
-               value = strings.ReplaceAll(valueStr, "\\,", ",")
-       }
-       return value
-}
-
-func isAlwaysString(s string) bool {
-       for _, a := range alwaysString {
-               if strings.HasPrefix(s, a) {
-                       return true
-               }
-       }
-       return false
-}
-
-var alwaysString = []string{}
-
+// SetSpecPaths applies values from input like `key.subkey=val`, and applies 
them under 'spec'
 func (m Map) SetSpecPaths(paths ...string) error {
        for _, path := range paths {
                if err := m.SetPaths("spec." + path); err != nil {
@@ -402,13 +361,69 @@ func extractKV(seg string) (string, string, bool) {
        return strings.Cut(sanitized, ":")
 }
 
-func GetValueForSetFlag(setFlags []string, path string) string {
-       r := ""
-       for _, sf := range setFlags {
-               p, v := getPV(sf)
-               if p == path {
-                       r = v
+func extractIndex(seg string) (int, bool) {
+       if !strings.HasPrefix(seg, "[") || !strings.HasSuffix(seg, "]") {
+               return 0, false
+       }
+       sanitized := seg[1 : len(seg)-1]
+       v, err := strconv.Atoi(sanitized)
+       if err != nil {
+               return 0, false
+       }
+       return v, true
+}
+
+func extractKeyValue(seg string) (string, string, bool) {
+       if !strings.HasPrefix(seg, "[") || !strings.HasSuffix(seg, "]") {
+               return "", "", false
+       }
+       sanitized := seg[1 : len(seg)-1]
+       return strings.Cut(sanitized, ":")
+}
+
+// alwaysString represents types that should always be decoded as strings
+var alwaysString = []string{}
+
+func isAlwaysString(s string) bool {
+       for _, a := range alwaysString {
+               if strings.HasPrefix(s, a) {
+                       return true
                }
        }
-       return r
+       return false
+}
+
+// parseValue parses string into a value.
+func parseValue(valueStr string) any {
+       var value any
+       if v, err := strconv.Atoi(valueStr); err == nil {
+               value = v
+       } else if v, err := strconv.ParseFloat(valueStr, 64); err == nil {
+               value = v
+       } else if v, err := strconv.ParseBool(valueStr); err == nil {
+               value = v
+       } else {
+               value = strings.ReplaceAll(valueStr, "\\,", ",")
+       }
+       return value
+}
+
+// MergeFrom does a key-wise merge between the current map and the passed in 
map.
+// The other map has precedence, and the result will modify the current map.
+func (m Map) MergeFrom(other Map) {
+       for k, v := range other {
+               if vm, ok := v.(Map); ok {
+                       v = map[string]any(vm)
+               }
+               if v, ok := v.(map[string]any); ok {
+                       if bv, ok := m[k]; ok {
+
+                               if bv, ok := bv.(map[string]any); ok {
+                                       Map(bv).MergeFrom(v)
+                                       continue
+                               }
+                       }
+               }
+               m[k] = v
+       }
 }

Reply via email to