github.com/argoproj/argo-cd/v3@v3.2.1/pkg/apiclient/application/forwarder_overwrite.go (about)

     1  package application
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	gohttp "net/http"
     8  	"strings"
     9  
    10  	"github.com/argoproj/argo-cd/v3/util/kube"
    11  
    12  	"github.com/argoproj/pkg/v2/grpc/http"
    13  	"github.com/grpc-ecosystem/grpc-gateway/runtime"
    14  
    15  	//nolint:staticcheck
    16  	"github.com/golang/protobuf/proto"
    17  
    18  	"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
    19  )
    20  
    21  // appFields is a map of fields that can be selected from an application.
    22  // The manually maintained list is required because application list response might include thousands of applications
    23  // and JSON based field handling is too slow.
    24  var appFields = map[string]func(app *v1alpha1.Application) any{
    25  	"metadata.name":              func(app *v1alpha1.Application) any { return app.Name },
    26  	"metadata.namespace":         func(app *v1alpha1.Application) any { return app.Namespace },
    27  	"metadata.annotations":       func(app *v1alpha1.Application) any { return app.Annotations },
    28  	"metadata.labels":            func(app *v1alpha1.Application) any { return app.Labels },
    29  	"metadata.creationTimestamp": func(app *v1alpha1.Application) any { return app.CreationTimestamp },
    30  	"metadata.deletionTimestamp": func(app *v1alpha1.Application) any { return app.DeletionTimestamp },
    31  	"spec":                       func(app *v1alpha1.Application) any { return app.Spec },
    32  	"status.sourceHydrator":      func(app *v1alpha1.Application) any { return app.Status.SourceHydrator },
    33  	"status.sync.status":         func(app *v1alpha1.Application) any { return app.Status.Sync.Status },
    34  	"status.health":              func(app *v1alpha1.Application) any { return app.Status.Health },
    35  	"status.summary":             func(app *v1alpha1.Application) any { return app.Status.Summary },
    36  	"status.operationState.startedAt": func(app *v1alpha1.Application) any {
    37  		if app.Status.OperationState != nil {
    38  			return app.Status.OperationState.StartedAt
    39  		}
    40  		return nil
    41  	},
    42  	"status.operationState.finishedAt": func(app *v1alpha1.Application) any {
    43  		if app.Status.OperationState != nil {
    44  			return app.Status.OperationState.FinishedAt
    45  		}
    46  		return nil
    47  	},
    48  	"status.resources": func(app *v1alpha1.Application) any {
    49  		if len(app.Status.Resources) > 0 {
    50  			return app.Status.Resources
    51  		}
    52  		return nil
    53  	},
    54  	"operation.sync": func(app *v1alpha1.Application) any {
    55  		if app.Operation != nil {
    56  			return app.Operation.Sync
    57  		}
    58  		return nil
    59  	},
    60  	"status.operationState.phase": func(app *v1alpha1.Application) any {
    61  		if app.Status.OperationState != nil {
    62  			return app.Status.OperationState.Phase
    63  		}
    64  		return nil
    65  	},
    66  	"status.operationState.operation.sync": func(app *v1alpha1.Application) any {
    67  		if app.Status.OperationState != nil {
    68  			return app.Status.OperationState.SyncResult
    69  		}
    70  		return nil
    71  	},
    72  }
    73  
    74  func processApplicationListField(v any, fields map[string]any, exclude bool) (any, error) {
    75  	if appList, ok := v.(*v1alpha1.ApplicationList); ok {
    76  		var items []map[string]any
    77  		for _, app := range appList.Items {
    78  			converted := make(map[string]any)
    79  			items = append(items, converted)
    80  			for field, fn := range appFields {
    81  				if _, ok := fields["items."+field]; ok == exclude {
    82  					continue
    83  				}
    84  				value := fn(&app)
    85  				if value == nil {
    86  					continue
    87  				}
    88  				parts := strings.Split(field, ".")
    89  				item := converted
    90  				for i := 0; i < len(parts); i++ {
    91  					subField := parts[i]
    92  					if i == len(parts)-1 {
    93  						item[subField] = value
    94  					} else {
    95  						if _, ok := item[subField]; !ok {
    96  							item[subField] = make(map[string]any)
    97  						}
    98  						nestedMap, ok := item[subField].(map[string]any)
    99  						if !ok {
   100  							return nil, fmt.Errorf("field %s is not a map", field)
   101  						}
   102  						item = nestedMap
   103  					}
   104  				}
   105  			}
   106  		}
   107  		return map[string]any{
   108  			"items":    items,
   109  			"metadata": appList.ListMeta,
   110  		}, nil
   111  	}
   112  	return nil, errors.New("not an application list")
   113  }
   114  
   115  func init() {
   116  	logsForwarder := func(ctx context.Context, mux *runtime.ServeMux, marshaler runtime.Marshaler, w gohttp.ResponseWriter, req *gohttp.Request, recv func() (proto.Message, error), opts ...func(context.Context, gohttp.ResponseWriter, proto.Message) error) {
   117  		if req.URL.Query().Get("download") == "true" {
   118  			w.Header().Set("Content-Type", "application/octet-stream")
   119  			fileName := "log"
   120  			namespace := req.URL.Query().Get("namespace")
   121  			podName := req.URL.Query().Get("podName")
   122  			container := req.URL.Query().Get("container")
   123  			if kube.IsValidResourceName(namespace) && kube.IsValidResourceName(podName) && kube.IsValidResourceName(container) {
   124  				fileName = fmt.Sprintf("%s-%s-%s", namespace, podName, container)
   125  			}
   126  			w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment;filename="%s.log"`, fileName))
   127  			for {
   128  				msg, err := recv()
   129  				if err != nil {
   130  					_, _ = w.Write([]byte(err.Error()))
   131  					return
   132  				}
   133  				if logEntry, ok := msg.(*LogEntry); ok {
   134  					if logEntry.GetLast() {
   135  						return
   136  					}
   137  					if _, err = w.Write([]byte(logEntry.GetContent() + "\n")); err != nil {
   138  						return
   139  					}
   140  				}
   141  			}
   142  		} else {
   143  			http.StreamForwarder(ctx, mux, marshaler, w, req, recv, opts...)
   144  		}
   145  	}
   146  	forward_ApplicationService_PodLogs_0 = logsForwarder
   147  	forward_ApplicationService_PodLogs_1 = logsForwarder
   148  	forward_ApplicationService_WatchResourceTree_0 = http.StreamForwarder
   149  	forward_ApplicationService_Watch_0 = http.NewStreamForwarder(func(message proto.Message) (string, error) {
   150  		event, ok := message.(*v1alpha1.ApplicationWatchEvent)
   151  		if !ok {
   152  			return "", errors.New("unexpected message type")
   153  		}
   154  		return event.Application.Name, nil
   155  	})
   156  	forward_ApplicationService_List_0 = http.UnaryForwarderWithFieldProcessor(processApplicationListField)
   157  	forward_ApplicationService_ManagedResources_0 = http.UnaryForwarder
   158  }