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