From 1449154d058c1a23910246174e0ef0ad27ca15c4 Mon Sep 17 00:00:00 2001 From: Daisuke Taniwaki Date: Mon, 5 Jul 2021 19:08:31 +0900 Subject: [PATCH 1/7] feat: Get css URL from configmap Signed-off-by: Daisuke Taniwaki --- api/jsonschema/schema.json | 8 + api/openapi-spec/swagger.json | 30 ++ config/config.go | 3 + pkg/apiclient/http1/info-service-client.go | 5 + pkg/apiclient/info/info.pb.go | 416 +++++++++++++++++++-- pkg/apiclient/info/info.pb.gw.go | 65 ++++ pkg/apiclient/info/info.proto | 10 + server/apiserver/argoserver.go | 7 +- server/info/info_server.go | 11 +- ui/src/app/app.tsx | 12 + ui/src/app/shared/services/info-service.ts | 7 +- ui/src/models/info.ts | 4 + 12 files changed, 538 insertions(+), 40 deletions(-) diff --git a/api/jsonschema/schema.json b/api/jsonschema/schema.json index b312c5ecfae1..ddb8b928a2fd 100644 --- a/api/jsonschema/schema.json +++ b/api/jsonschema/schema.json @@ -3591,6 +3591,14 @@ ], "type": "object" }, + "io.argoproj.workflow.v1alpha1.GetSettingsResponse": { + "properties": { + "uiCssURL": { + "type": "string" + } + }, + "type": "object" + }, "io.argoproj.workflow.v1alpha1.GetUserInfoResponse": { "properties": { "email": { diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index be124e793025..55dcfbf4fbea 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -1471,6 +1471,28 @@ } } }, + "/api/v1/settings": { + "get": { + "tags": [ + "InfoService" + ], + "operationId": "InfoService_GetSettings", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/io.argoproj.workflow.v1alpha1.GetSettingsResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/grpc.gateway.runtime.Error" + } + } + } + } + }, "/api/v1/stream/event-sources/{namespace}": { "get": { "tags": [ @@ -7020,6 +7042,14 @@ } } }, + "io.argoproj.workflow.v1alpha1.GetSettingsResponse": { + "type": "object", + "properties": { + "uiCssURL": { + "type": "string" + } + } + }, "io.argoproj.workflow.v1alpha1.GetUserInfoResponse": { "type": "object", "properties": { diff --git a/config/config.go b/config/config.go index 9062b048c541..78e4bedab447 100644 --- a/config/config.go +++ b/config/config.go @@ -126,6 +126,9 @@ type Config struct { // The command/args for each image, needed when the command is not specified and the emissary executor is used. // https://argoproj.github.io/argo-workflows/workflow-executors/#emissary-emissary Images map[string]Image `json:"images,omitempty"` + + // UiCssURL local or remote path to user-defined CSS to customize the UI. + UiCssURL string `json:"ui.cssurl,omitempty"` } func (c Config) GetContainerRuntimeExecutor(labels labels.Labels) (string, error) { diff --git a/pkg/apiclient/http1/info-service-client.go b/pkg/apiclient/http1/info-service-client.go index b1e934804e56..e40346e3090d 100644 --- a/pkg/apiclient/http1/info-service-client.go +++ b/pkg/apiclient/http1/info-service-client.go @@ -25,3 +25,8 @@ func (h InfoServiceClient) GetUserInfo(_ context.Context, in *infopkg.GetUserInf out := &infopkg.GetUserInfoResponse{} return out, h.Get(in, out, "/api/v1/userinfo") } + +func (h InfoServiceClient) GetSettings(_ context.Context, in *infopkg.GetSettingsRequest, _ ...grpc.CallOption) (*infopkg.GetSettingsResponse, error) { + out := &infopkg.GetSettingsResponse{} + return out, h.Get(in, out, "/api/v1/settings") +} diff --git a/pkg/apiclient/info/info.pb.go b/pkg/apiclient/info/info.pb.go index f5cc78cdf970..8a617f785e86 100644 --- a/pkg/apiclient/info/info.pb.go +++ b/pkg/apiclient/info/info.pb.go @@ -287,50 +287,141 @@ func (m *GetUserInfoResponse) GetServiceAccountName() string { return "" } +type GetSettingsRequest struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetSettingsRequest) Reset() { *m = GetSettingsRequest{} } +func (m *GetSettingsRequest) String() string { return proto.CompactTextString(m) } +func (*GetSettingsRequest) ProtoMessage() {} +func (*GetSettingsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_96940c93018255fa, []int{5} +} +func (m *GetSettingsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetSettingsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetSettingsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetSettingsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetSettingsRequest.Merge(m, src) +} +func (m *GetSettingsRequest) XXX_Size() int { + return m.Size() +} +func (m *GetSettingsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetSettingsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetSettingsRequest proto.InternalMessageInfo + +type GetSettingsResponse struct { + UiCssURL string `protobuf:"bytes,1,opt,name=uiCssURL,proto3" json:"uiCssURL,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetSettingsResponse) Reset() { *m = GetSettingsResponse{} } +func (m *GetSettingsResponse) String() string { return proto.CompactTextString(m) } +func (*GetSettingsResponse) ProtoMessage() {} +func (*GetSettingsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_96940c93018255fa, []int{6} +} +func (m *GetSettingsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetSettingsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetSettingsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetSettingsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetSettingsResponse.Merge(m, src) +} +func (m *GetSettingsResponse) XXX_Size() int { + return m.Size() +} +func (m *GetSettingsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetSettingsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetSettingsResponse proto.InternalMessageInfo + +func (m *GetSettingsResponse) GetUiCssURL() string { + if m != nil { + return m.UiCssURL + } + return "" +} + func init() { proto.RegisterType((*GetInfoRequest)(nil), "info.GetInfoRequest") proto.RegisterType((*InfoResponse)(nil), "info.InfoResponse") proto.RegisterType((*GetVersionRequest)(nil), "info.GetVersionRequest") proto.RegisterType((*GetUserInfoRequest)(nil), "info.GetUserInfoRequest") proto.RegisterType((*GetUserInfoResponse)(nil), "info.GetUserInfoResponse") + proto.RegisterType((*GetSettingsRequest)(nil), "info.GetSettingsRequest") + proto.RegisterType((*GetSettingsResponse)(nil), "info.GetSettingsResponse") } func init() { proto.RegisterFile("pkg/apiclient/info/info.proto", fileDescriptor_96940c93018255fa) } var fileDescriptor_96940c93018255fa = []byte{ - // 511 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x53, 0x41, 0x6b, 0x13, 0x41, - 0x14, 0x66, 0x93, 0x26, 0xd5, 0x49, 0xad, 0xe9, 0x6b, 0xb0, 0x63, 0xd0, 0x10, 0x82, 0x87, 0x20, - 0x38, 0x4b, 0x5a, 0xfc, 0x01, 0x2a, 0x18, 0x02, 0xea, 0x21, 0x62, 0x0f, 0x52, 0x90, 0xc9, 0xe6, - 0x65, 0x3b, 0xdd, 0xcd, 0xcc, 0x3a, 0x33, 0xbb, 0xbd, 0x7b, 0xf3, 0xec, 0xc5, 0xdf, 0xe3, 0x49, - 0xf0, 0x22, 0xf8, 0x07, 0x24, 0xf8, 0x43, 0x64, 0x67, 0x77, 0xd3, 0x44, 0x7b, 0x10, 0xbc, 0x2c, - 0xef, 0x7d, 0xef, 0xed, 0x37, 0xdf, 0x37, 0x6f, 0x1e, 0xb9, 0x9f, 0x44, 0xa1, 0xcf, 0x13, 0x11, - 0xc4, 0x02, 0xa5, 0xf5, 0x85, 0x5c, 0x28, 0xf7, 0x61, 0x89, 0x56, 0x56, 0xc1, 0x4e, 0x1e, 0x77, - 0xef, 0x85, 0x4a, 0x85, 0x31, 0xe6, 0x7d, 0x3e, 0x97, 0x52, 0x59, 0x6e, 0x85, 0x92, 0xa6, 0xe8, - 0xe9, 0xbe, 0x0c, 0x85, 0x3d, 0x4f, 0x67, 0x2c, 0x50, 0x4b, 0x9f, 0xeb, 0x50, 0x25, 0x5a, 0x5d, - 0xb8, 0xe0, 0xd1, 0xa5, 0xd2, 0xd1, 0x22, 0x56, 0x97, 0xc6, 0x2f, 0x4f, 0x31, 0x7e, 0x05, 0xf9, - 0xd9, 0x88, 0xc7, 0xc9, 0x39, 0x1f, 0xf9, 0x21, 0x4a, 0xd4, 0xdc, 0xe2, 0xbc, 0xa0, 0x1b, 0xb4, - 0xc9, 0xfe, 0x18, 0xed, 0x44, 0x2e, 0xd4, 0x14, 0xdf, 0xa7, 0x68, 0xec, 0xe0, 0xb3, 0x47, 0xf6, - 0x8a, 0xdc, 0x24, 0x4a, 0x1a, 0x84, 0x87, 0xa4, 0xbd, 0xe4, 0x92, 0x87, 0x38, 0x7f, 0xc5, 0x97, - 0x68, 0x12, 0x1e, 0x20, 0xf5, 0xfa, 0xde, 0xf0, 0xe6, 0xf4, 0x2f, 0x1c, 0xce, 0x48, 0x23, 0x16, - 0x32, 0x32, 0xb4, 0xd6, 0xaf, 0x0f, 0x5b, 0xc7, 0xcf, 0xd9, 0x95, 0x5a, 0x56, 0xa9, 0x75, 0xc1, - 0xbb, 0xb5, 0x5a, 0x96, 0x9d, 0xb0, 0x24, 0x0a, 0x59, 0x2e, 0x98, 0x55, 0x28, 0xab, 0x04, 0xb3, - 0x17, 0x42, 0x46, 0xd3, 0x82, 0x74, 0x70, 0x48, 0x0e, 0xc6, 0x68, 0x4f, 0x51, 0x1b, 0xa1, 0x64, - 0xa5, 0xb7, 0x43, 0x60, 0x8c, 0xf6, 0x8d, 0x41, 0xbd, 0xe9, 0xe2, 0x9b, 0x47, 0x0e, 0xb7, 0xe0, - 0xd2, 0xcc, 0x1d, 0xd2, 0x14, 0xc6, 0xa4, 0xa8, 0x4b, 0x0b, 0x65, 0x06, 0x94, 0xec, 0x9a, 0x74, - 0x76, 0x81, 0x81, 0xa5, 0x35, 0x57, 0xa8, 0xd2, 0xfc, 0x8f, 0x50, 0xab, 0x34, 0x31, 0xb4, 0xde, - 0xaf, 0xe7, 0x7f, 0x14, 0x19, 0x74, 0x48, 0x03, 0x97, 0x5c, 0xc4, 0x74, 0xc7, 0xf5, 0x17, 0x09, - 0x3c, 0x20, 0xb7, 0x5c, 0x70, 0x8a, 0x5a, 0x2c, 0x04, 0xce, 0x69, 0xa3, 0xef, 0x0d, 0x6f, 0x4c, - 0xb7, 0x41, 0x60, 0x04, 0x0c, 0xea, 0x4c, 0x04, 0xf8, 0x24, 0x08, 0x54, 0x2a, 0x6d, 0x7e, 0x83, - 0xb4, 0xe9, 0x88, 0xae, 0xa9, 0x1c, 0x7f, 0xa9, 0x91, 0x56, 0x6e, 0xe3, 0x75, 0x51, 0x82, 0x09, - 0xd9, 0x2d, 0xa7, 0x06, 0x1d, 0xe6, 0x1e, 0xd0, 0xf6, 0x10, 0xbb, 0x50, 0xa0, 0x9b, 0xd6, 0x07, - 0x9d, 0x0f, 0x3f, 0x7e, 0x7d, 0xaa, 0xed, 0xc3, 0x9e, 0x7b, 0x59, 0xd9, 0xc8, 0xbd, 0x3c, 0xf8, - 0xe8, 0x11, 0x72, 0x75, 0xa9, 0x70, 0xb4, 0xa6, 0xdb, 0xbe, 0xe6, 0xee, 0xe4, 0xff, 0x47, 0x59, - 0x32, 0x0e, 0x8e, 0x9c, 0x90, 0x03, 0xb8, 0x5d, 0x09, 0xc9, 0xca, 0xc3, 0xcf, 0x48, 0x6b, 0x63, - 0x66, 0x40, 0xd7, 0x5a, 0xfe, 0x98, 0x6e, 0xf7, 0xee, 0x35, 0x95, 0xd2, 0x25, 0x75, 0xe4, 0x00, - 0xed, 0x8a, 0x3c, 0x35, 0xa8, 0xf3, 0xee, 0xa7, 0xcf, 0xbe, 0xae, 0x7a, 0xde, 0xf7, 0x55, 0xcf, - 0xfb, 0xb9, 0xea, 0x79, 0x6f, 0x1f, 0xff, 0xfb, 0x1e, 0x6d, 0x6c, 0xeb, 0xac, 0xe9, 0xd6, 0xe6, - 0xe4, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe5, 0x42, 0xb8, 0x53, 0xca, 0x03, 0x00, 0x00, + // 558 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x54, 0x41, 0x6b, 0x13, 0x41, + 0x14, 0x66, 0x93, 0x26, 0xad, 0x93, 0x5a, 0xd3, 0x69, 0xb0, 0xeb, 0xa2, 0x21, 0x04, 0x0f, 0x41, + 0x70, 0x96, 0xb4, 0xf8, 0x03, 0xb4, 0x60, 0x08, 0x54, 0x0f, 0x29, 0xed, 0x41, 0x0a, 0x32, 0xd9, + 0xbc, 0x6c, 0xa7, 0xd9, 0xcc, 0xac, 0x33, 0xb3, 0xdb, 0xbb, 0x37, 0xcf, 0x5e, 0x3c, 0xfa, 0x73, + 0x04, 0x2f, 0x82, 0x7f, 0x40, 0x82, 0x3f, 0x44, 0x76, 0x76, 0x36, 0xd9, 0xa4, 0x3d, 0x08, 0xbd, + 0x84, 0xf7, 0xbe, 0x79, 0xfb, 0xcd, 0xf7, 0xbd, 0xf7, 0x26, 0xe8, 0x59, 0x3c, 0x0b, 0x7d, 0x1a, + 0xb3, 0x20, 0x62, 0xc0, 0xb5, 0xcf, 0xf8, 0x54, 0x98, 0x1f, 0x12, 0x4b, 0xa1, 0x05, 0xde, 0xca, + 0x62, 0xef, 0x69, 0x28, 0x44, 0x18, 0x41, 0x56, 0xe7, 0x53, 0xce, 0x85, 0xa6, 0x9a, 0x09, 0xae, + 0xf2, 0x1a, 0xef, 0x5d, 0xc8, 0xf4, 0x55, 0x32, 0x26, 0x81, 0x98, 0xfb, 0x54, 0x86, 0x22, 0x96, + 0xe2, 0xda, 0x04, 0x2f, 0x6f, 0x84, 0x9c, 0x4d, 0x23, 0x71, 0xa3, 0x7c, 0x7b, 0x8b, 0xf2, 0x0b, + 0xc8, 0x4f, 0xfb, 0x34, 0x8a, 0xaf, 0x68, 0xdf, 0x0f, 0x81, 0x83, 0xa4, 0x1a, 0x26, 0x39, 0x5d, + 0xb7, 0x89, 0xf6, 0x06, 0xa0, 0x87, 0x7c, 0x2a, 0x46, 0xf0, 0x29, 0x01, 0xa5, 0xbb, 0xdf, 0x1c, + 0xb4, 0x9b, 0xe7, 0x2a, 0x16, 0x5c, 0x01, 0x7e, 0x81, 0x9a, 0x73, 0xca, 0x69, 0x08, 0x93, 0xf7, + 0x74, 0x0e, 0x2a, 0xa6, 0x01, 0xb8, 0x4e, 0xc7, 0xe9, 0x3d, 0x18, 0xdd, 0xc2, 0xf1, 0x25, 0xaa, + 0x45, 0x8c, 0xcf, 0x94, 0x5b, 0xe9, 0x54, 0x7b, 0x8d, 0xa3, 0xb7, 0x64, 0xa5, 0x96, 0x14, 0x6a, + 0x4d, 0xf0, 0x71, 0xa9, 0x96, 0xa4, 0xc7, 0x24, 0x9e, 0x85, 0x24, 0x13, 0x4c, 0x0a, 0x94, 0x14, + 0x82, 0xc9, 0x29, 0xe3, 0xb3, 0x51, 0x4e, 0xda, 0x3d, 0x40, 0xfb, 0x03, 0xd0, 0x17, 0x20, 0x15, + 0x13, 0xbc, 0xd0, 0xdb, 0x42, 0x78, 0x00, 0xfa, 0x5c, 0x81, 0x2c, 0xbb, 0xf8, 0xe9, 0xa0, 0x83, + 0x35, 0xd8, 0x9a, 0x79, 0x8c, 0xea, 0x4c, 0xa9, 0x04, 0xa4, 0xb5, 0x60, 0x33, 0xec, 0xa2, 0x6d, + 0x95, 0x8c, 0xaf, 0x21, 0xd0, 0x6e, 0xc5, 0x1c, 0x14, 0x69, 0xf6, 0x45, 0x28, 0x45, 0x12, 0x2b, + 0xb7, 0xda, 0xa9, 0x66, 0x5f, 0xe4, 0x19, 0x6e, 0xa1, 0x1a, 0xcc, 0x29, 0x8b, 0xdc, 0x2d, 0x53, + 0x9f, 0x27, 0xf8, 0x39, 0x7a, 0x68, 0x82, 0x0b, 0x90, 0x6c, 0xca, 0x60, 0xe2, 0xd6, 0x3a, 0x4e, + 0x6f, 0x67, 0xb4, 0x0e, 0x62, 0x82, 0xb0, 0x02, 0x99, 0xb2, 0x00, 0x5e, 0x07, 0x81, 0x48, 0xb8, + 0xce, 0x3a, 0xe8, 0xd6, 0x0d, 0xd1, 0x1d, 0x27, 0xd6, 0xe3, 0x19, 0x68, 0xcd, 0x78, 0xa8, 0x0a, + 0x8f, 0x7d, 0x63, 0x71, 0x85, 0x5a, 0x8b, 0x1e, 0xda, 0x49, 0xd8, 0x89, 0x52, 0xe7, 0xa3, 0x53, + 0x6b, 0x72, 0x99, 0x1f, 0x7d, 0xaf, 0xa2, 0x46, 0xd6, 0x8f, 0xb3, 0xfc, 0x0e, 0x3c, 0x44, 0xdb, + 0x76, 0xfc, 0xb8, 0x45, 0xcc, 0x26, 0xae, 0x6f, 0x83, 0x87, 0x73, 0xb4, 0xdc, 0xc3, 0x6e, 0xeb, + 0xf3, 0xef, 0xbf, 0x5f, 0x2b, 0x7b, 0x78, 0xd7, 0xac, 0x68, 0xda, 0x37, 0x2b, 0x8c, 0xbf, 0x38, + 0x08, 0xad, 0xa6, 0x83, 0x0f, 0x97, 0x74, 0xeb, 0xf3, 0xf2, 0x86, 0xf7, 0xdf, 0x09, 0xcb, 0xd8, + 0x3d, 0x34, 0x42, 0xf6, 0xf1, 0xa3, 0x42, 0x48, 0x6a, 0x2f, 0xbf, 0x44, 0x8d, 0xd2, 0xf0, 0xb1, + 0xbb, 0xd4, 0xb2, 0xb1, 0x26, 0xde, 0x93, 0x3b, 0x4e, 0xac, 0x4b, 0xd7, 0x90, 0x63, 0xdc, 0x2c, + 0xc8, 0x13, 0x05, 0xd2, 0x38, 0xcd, 0xd9, 0x8b, 0xbe, 0x97, 0xd8, 0x37, 0x06, 0x54, 0x62, 0xdf, + 0x1c, 0xd2, 0x6d, 0x76, 0x65, 0x2b, 0xde, 0x9c, 0xfc, 0x58, 0xb4, 0x9d, 0x5f, 0x8b, 0xb6, 0xf3, + 0x67, 0xd1, 0x76, 0x3e, 0xbc, 0xfa, 0xff, 0xe7, 0x5e, 0xfa, 0x53, 0x19, 0xd7, 0xcd, 0xeb, 0x3e, + 0xfe, 0x17, 0x00, 0x00, 0xff, 0xff, 0xbe, 0x8f, 0xdc, 0x4b, 0x71, 0x04, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -348,6 +439,7 @@ type InfoServiceClient interface { GetInfo(ctx context.Context, in *GetInfoRequest, opts ...grpc.CallOption) (*InfoResponse, error) GetVersion(ctx context.Context, in *GetVersionRequest, opts ...grpc.CallOption) (*v1alpha1.Version, error) GetUserInfo(ctx context.Context, in *GetUserInfoRequest, opts ...grpc.CallOption) (*GetUserInfoResponse, error) + GetSettings(ctx context.Context, in *GetSettingsRequest, opts ...grpc.CallOption) (*GetSettingsResponse, error) } type infoServiceClient struct { @@ -385,11 +477,21 @@ func (c *infoServiceClient) GetUserInfo(ctx context.Context, in *GetUserInfoRequ return out, nil } +func (c *infoServiceClient) GetSettings(ctx context.Context, in *GetSettingsRequest, opts ...grpc.CallOption) (*GetSettingsResponse, error) { + out := new(GetSettingsResponse) + err := c.cc.Invoke(ctx, "/info.InfoService/GetSettings", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // InfoServiceServer is the server API for InfoService service. type InfoServiceServer interface { GetInfo(context.Context, *GetInfoRequest) (*InfoResponse, error) GetVersion(context.Context, *GetVersionRequest) (*v1alpha1.Version, error) GetUserInfo(context.Context, *GetUserInfoRequest) (*GetUserInfoResponse, error) + GetSettings(context.Context, *GetSettingsRequest) (*GetSettingsResponse, error) } // UnimplementedInfoServiceServer can be embedded to have forward compatible implementations. @@ -405,6 +507,9 @@ func (*UnimplementedInfoServiceServer) GetVersion(ctx context.Context, req *GetV func (*UnimplementedInfoServiceServer) GetUserInfo(ctx context.Context, req *GetUserInfoRequest) (*GetUserInfoResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetUserInfo not implemented") } +func (*UnimplementedInfoServiceServer) GetSettings(ctx context.Context, req *GetSettingsRequest) (*GetSettingsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetSettings not implemented") +} func RegisterInfoServiceServer(s *grpc.Server, srv InfoServiceServer) { s.RegisterService(&_InfoService_serviceDesc, srv) @@ -464,6 +569,24 @@ func _InfoService_GetUserInfo_Handler(srv interface{}, ctx context.Context, dec return interceptor(ctx, in, info, handler) } +func _InfoService_GetSettings_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetSettingsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(InfoServiceServer).GetSettings(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/info.InfoService/GetSettings", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(InfoServiceServer).GetSettings(ctx, req.(*GetSettingsRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _InfoService_serviceDesc = grpc.ServiceDesc{ ServiceName: "info.InfoService", HandlerType: (*InfoServiceServer)(nil), @@ -480,6 +603,10 @@ var _InfoService_serviceDesc = grpc.ServiceDesc{ MethodName: "GetUserInfo", Handler: _InfoService_GetUserInfo_Handler, }, + { + MethodName: "GetSettings", + Handler: _InfoService_GetSettings_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "pkg/apiclient/info/info.proto", @@ -688,6 +815,67 @@ func (m *GetUserInfoResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *GetSettingsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GetSettingsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetSettingsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + return len(dAtA) - i, nil +} + +func (m *GetSettingsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GetSettingsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetSettingsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.UiCssURL) > 0 { + i -= len(m.UiCssURL) + copy(dAtA[i:], m.UiCssURL) + i = encodeVarintInfo(dAtA, i, uint64(len(m.UiCssURL))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintInfo(dAtA []byte, offset int, v uint64) int { offset -= sovInfo(v) base := offset @@ -794,6 +982,34 @@ func (m *GetUserInfoResponse) Size() (n int) { return n } +func (m *GetSettingsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *GetSettingsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.UiCssURL) + if l > 0 { + n += 1 + l + sovInfo(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + func sovInfo(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -1301,6 +1517,140 @@ func (m *GetUserInfoResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *GetSettingsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowInfo + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GetSettingsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetSettingsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipInfo(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthInfo + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetSettingsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowInfo + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GetSettingsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetSettingsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UiCssURL", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowInfo + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthInfo + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthInfo + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UiCssURL = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipInfo(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthInfo + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipInfo(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/pkg/apiclient/info/info.pb.gw.go b/pkg/apiclient/info/info.pb.gw.go index 5b023ca0d673..3634124a506b 100644 --- a/pkg/apiclient/info/info.pb.gw.go +++ b/pkg/apiclient/info/info.pb.gw.go @@ -87,6 +87,24 @@ func local_request_InfoService_GetUserInfo_0(ctx context.Context, marshaler runt } +func request_InfoService_GetSettings_0(ctx context.Context, marshaler runtime.Marshaler, client InfoServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetSettingsRequest + var metadata runtime.ServerMetadata + + msg, err := client.GetSettings(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_InfoService_GetSettings_0(ctx context.Context, marshaler runtime.Marshaler, server InfoServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetSettingsRequest + var metadata runtime.ServerMetadata + + msg, err := server.GetSettings(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterInfoServiceHandlerServer registers the http handlers for service InfoService to "mux". // UnaryRPC :call InfoServiceServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -162,6 +180,29 @@ func RegisterInfoServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux }) + mux.Handle("GET", pattern_InfoService_GetSettings_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_InfoService_GetSettings_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_InfoService_GetSettings_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -263,6 +304,26 @@ func RegisterInfoServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux }) + mux.Handle("GET", pattern_InfoService_GetSettings_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_InfoService_GetSettings_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_InfoService_GetSettings_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -272,6 +333,8 @@ var ( pattern_InfoService_GetVersion_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "version"}, "", runtime.AssumeColonVerbOpt(true))) pattern_InfoService_GetUserInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "userinfo"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_InfoService_GetSettings_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "settings"}, "", runtime.AssumeColonVerbOpt(true))) ) var ( @@ -280,4 +343,6 @@ var ( forward_InfoService_GetVersion_0 = runtime.ForwardResponseMessage forward_InfoService_GetUserInfo_0 = runtime.ForwardResponseMessage + + forward_InfoService_GetSettings_0 = runtime.ForwardResponseMessage ) diff --git a/pkg/apiclient/info/info.proto b/pkg/apiclient/info/info.proto index 817ae2dd9bac..97dc15478555 100644 --- a/pkg/apiclient/info/info.proto +++ b/pkg/apiclient/info/info.proto @@ -29,6 +29,13 @@ message GetUserInfoResponse { string serviceAccountName = 6; } +message GetSettingsRequest { +} + +message GetSettingsResponse { + string uiCssURL = 1; +} + service InfoService { rpc GetInfo (GetInfoRequest) returns (InfoResponse) { option (google.api.http).get = "/api/v1/info"; @@ -39,4 +46,7 @@ service InfoService { rpc GetUserInfo (GetUserInfoRequest) returns (GetUserInfoResponse) { option (google.api.http).get = "/api/v1/userinfo"; } + rpc GetSettings (GetSettingsRequest) returns (GetSettingsResponse) { + option (google.api.http).get = "/api/v1/settings"; + } } diff --git a/server/apiserver/argoserver.go b/server/apiserver/argoserver.go index 62a36900be8f..1ee6b704dbab 100644 --- a/server/apiserver/argoserver.go +++ b/server/apiserver/argoserver.go @@ -33,7 +33,6 @@ import ( workflowpkg "github.com/argoproj/argo-workflows/v3/pkg/apiclient/workflow" workflowarchivepkg "github.com/argoproj/argo-workflows/v3/pkg/apiclient/workflowarchive" workflowtemplatepkg "github.com/argoproj/argo-workflows/v3/pkg/apiclient/workflowtemplate" - "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" "github.com/argoproj/argo-workflows/v3/server/artifacts" "github.com/argoproj/argo-workflows/v3/server/auth" "github.com/argoproj/argo-workflows/v3/server/auth/sso" @@ -172,7 +171,7 @@ func (as *argoServer) Run(ctx context.Context, port int, browserOpenFunc func(st artifactRepositories := artifactrepositories.New(as.clients.Kubernetes, as.managedNamespace, &config.ArtifactRepository) artifactServer := artifacts.NewArtifactServer(as.gatekeeper, hydrator.New(offloadRepo), wfArchive, instanceIDService, artifactRepositories) eventServer := event.NewController(instanceIDService, eventRecorderManager, as.eventQueueSize, as.eventWorkerCount) - grpcServer := as.newGRPCServer(instanceIDService, offloadRepo, wfArchive, eventServer, config.Links) + grpcServer := as.newGRPCServer(instanceIDService, offloadRepo, wfArchive, eventServer, config) httpServer := as.newHTTPServer(ctx, port, artifactServer) // Start listener @@ -216,7 +215,7 @@ func (as *argoServer) Run(ctx context.Context, port int, browserOpenFunc func(st <-as.stopCh } -func (as *argoServer) newGRPCServer(instanceIDService instanceid.Service, offloadNodeStatusRepo sqldb.OffloadNodeStatusRepo, wfArchive sqldb.WorkflowArchive, eventServer *event.Controller, links []*v1alpha1.Link) *grpc.Server { +func (as *argoServer) newGRPCServer(instanceIDService instanceid.Service, offloadNodeStatusRepo sqldb.OffloadNodeStatusRepo, wfArchive sqldb.WorkflowArchive, eventServer *event.Controller, config *Config) *grpc.Server { serverLog := log.NewEntry(log.StandardLogger()) // "Prometheus histograms are a great way to measure latency distributions of your RPCs. However, since it is bad practice to have metrics of high cardinality the latency monitoring metrics are disabled by default. To enable them please call the following in your server initialization code:" @@ -247,7 +246,7 @@ func (as *argoServer) newGRPCServer(instanceIDService instanceid.Service, offloa grpcServer := grpc.NewServer(sOpts...) - infopkg.RegisterInfoServiceServer(grpcServer, info.NewInfoServer(as.managedNamespace, links)) + infopkg.RegisterInfoServiceServer(grpcServer, info.NewInfoServer(as.managedNamespace, config.Links, config.UiCssURL)) eventpkg.RegisterEventServiceServer(grpcServer, eventServer) eventsourcepkg.RegisterEventSourceServiceServer(grpcServer, eventsource.NewEventSourceServer()) sensorpkg.RegisterSensorServiceServer(grpcServer, sensor.NewSensorServer()) diff --git a/server/info/info_server.go b/server/info/info_server.go index e2be97c81dbb..0d7f01491869 100644 --- a/server/info/info_server.go +++ b/server/info/info_server.go @@ -12,6 +12,7 @@ import ( type infoServer struct { managedNamespace string links []*wfv1.Link + uiCssURL string } func (i *infoServer) GetUserInfo(ctx context.Context, _ *infopkg.GetUserInfoRequest) (*infopkg.GetUserInfoResponse, error) { @@ -38,6 +39,12 @@ func (i *infoServer) GetVersion(context.Context, *infopkg.GetVersionRequest) (*w return &version, nil } -func NewInfoServer(managedNamespace string, links []*wfv1.Link) infopkg.InfoServiceServer { - return &infoServer{managedNamespace, links} +func (i *infoServer) GetSettings(context.Context, *infopkg.GetSettingsRequest) (*infopkg.GetSettingsResponse, error) { + return &infopkg.GetSettingsResponse{ + UiCssURL: i.uiCssURL, + }, nil +} + +func NewInfoServer(managedNamespace string, links []*wfv1.Link, uiCssURL string) infopkg.InfoServiceServer { + return &infoServer{managedNamespace, links, uiCssURL} } diff --git a/ui/src/app/app.tsx b/ui/src/app/app.tsx index 8b61cca0c353..ad6958bc89b3 100644 --- a/ui/src/app/app.tsx +++ b/ui/src/app/app.tsx @@ -5,6 +5,7 @@ import * as PropTypes from 'prop-types'; import * as React from 'react'; import {AppRouter} from './app-router'; import {ContextApis, Provider} from './shared/context'; +import { services } from './shared/services'; const history = createBrowserHistory(); @@ -25,6 +26,17 @@ export class App extends React.Component<{}> { this.navigationManager = new NavigationManager(history); } + public async componentDidMount() { + const settings = await services.info.getSettings(); + if (settings.uiCssURL) { + const link = document.createElement('link'); + link.href = settings.uiCssURL; + link.rel = 'stylesheet'; + link.type = 'text/css'; + document.head.appendChild(link); + } + } + public render() { const providerContext: ContextApis = { notifications: this.notificationsManager, diff --git a/ui/src/app/shared/services/info-service.ts b/ui/src/app/shared/services/info-service.ts index 118bcdcf28ee..6e4f7369e1e5 100644 --- a/ui/src/app/shared/services/info-service.ts +++ b/ui/src/app/shared/services/info-service.ts @@ -1,4 +1,5 @@ -import {GetUserInfoResponse, Info, Version} from '../../../models'; +import request = require('superagent'); +import {GetUserInfoResponse, Info, Version, Settings} from '../../../models'; import requests from './requests'; @@ -20,4 +21,8 @@ export class InfoService { public getUserInfo() { return requests.get(`api/v1/userinfo`).then(res => res.body as GetUserInfoResponse); } + + public getSettings() { + return request.get(`api/v1/settings`).then(res => res.body as Settings) + } } diff --git a/ui/src/models/info.ts b/ui/src/models/info.ts index 189d61e378b5..393b29bb2a6c 100644 --- a/ui/src/models/info.ts +++ b/ui/src/models/info.ts @@ -21,3 +21,7 @@ export interface GetUserInfoResponse { emailVerified?: boolean; serviceAccountName?: string; } + +export interface Settings { + uiCssURL?: string; +} From a12008344b9cb929eb98c082824eb681168889b8 Mon Sep 17 00:00:00 2001 From: Daisuke Taniwaki Date: Mon, 5 Jul 2021 19:09:47 +0900 Subject: [PATCH 2/7] feat(server): Add extra file server Signed-off-by: Daisuke Taniwaki --- cmd/argo/commands/server.go | 4 ++++ server/apiserver/argoserver.go | 19 +++++++++++-------- server/static/static.go | 28 ++++++++++++++++++++++------ 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/cmd/argo/commands/server.go b/cmd/argo/commands/server.go index 89fbe96ab268..5603ca90afc8 100644 --- a/cmd/argo/commands/server.go +++ b/cmd/argo/commands/server.go @@ -38,6 +38,7 @@ func NewServerCommand() *cobra.Command { configMap string port int baseHRef string + extraFilesDir string secure bool htst bool namespaced bool // --namespaced @@ -92,6 +93,7 @@ See %s`, help.ArgoSever), "namespace": namespace, "managedNamespace": managedNamespace, "baseHRef": baseHRef, + "extraFilesDir": extraFilesDir, "secure": secure, }).Info() @@ -127,6 +129,7 @@ See %s`, help.ArgoSever), opts := apiserver.ArgoServerOpts{ BaseHRef: baseHRef, + ExtraFilesDir: extraFilesDir, TLSConfig: tlsConfig, HSTS: htst, Namespace: namespace, @@ -179,6 +182,7 @@ See %s`, help.ArgoSever), defaultBaseHRef = "/" } command.Flags().StringVar(&baseHRef, "basehref", defaultBaseHRef, "Value for base href in index.html. Used if the server is running behind reverse proxy under subpath different from /. Defaults to the environment variable BASE_HREF.") + command.Flags().StringVar(&extraFilesDir, "extra-files-dir", "", "Value to serve files from a specified directory. Defaults no serve.") // "-e" for encrypt, like zip // We default to secure mode if we find certs available, otherwise we default to insecure mode. _, err := os.Stat("argo-server.crt") diff --git a/server/apiserver/argoserver.go b/server/apiserver/argoserver.go index 1ee6b704dbab..ce5c0a4f96db 100644 --- a/server/apiserver/argoserver.go +++ b/server/apiserver/argoserver.go @@ -62,7 +62,8 @@ const ( ) type argoServer struct { - baseHRef string + baseHRef string + extraFilesDir string // https://itnext.io/practical-guide-to-securing-grpc-connections-with-go-and-tls-part-1-f63058e9d6d1 tlsConfig *tls.Config hsts bool @@ -80,12 +81,13 @@ type argoServer struct { } type ArgoServerOpts struct { - BaseHRef string - TLSConfig *tls.Config - Namespace string - Clients *types.Clients - RestConfig *rest.Config - AuthModes auth.Modes + BaseHRef string + ExtraFilesDir string + TLSConfig *tls.Config + Namespace string + Clients *types.Clients + RestConfig *rest.Config + AuthModes auth.Modes // config map name ConfigName string ManagedNamespace string @@ -118,6 +120,7 @@ func NewArgoServer(ctx context.Context, opts ArgoServerOpts) (*argoServer, error } return &argoServer{ baseHRef: opts.BaseHRef, + extraFilesDir: opts.ExtraFilesDir, tlsConfig: opts.TLSConfig, hsts: opts.HSTS, namespace: opts.Namespace, @@ -311,7 +314,7 @@ func (as *argoServer) newHTTPServer(ctx context.Context, port int, artifactServe mux.Handle("/oauth2/callback", handlers.ProxyHeaders(http.HandlerFunc(as.oAuth2Service.HandleCallback))) mux.Handle("/metrics", promhttp.Handler()) // we only enable HTST if we are secure mode, otherwise you would never be able access the UI - mux.HandleFunc("/", static.NewFilesServer(as.baseHRef, as.tlsConfig != nil && as.hsts, as.xframeOptions, as.accessControlAllowOrigin).ServerFiles) + mux.HandleFunc("/", static.NewFilesServer(as.baseHRef, as.tlsConfig != nil && as.hsts, as.xframeOptions, as.accessControlAllowOrigin, as.extraFilesDir).ServerFiles) return &httpServer } diff --git a/server/static/static.go b/server/static/static.go index f2df6d60b6f2..d4070b46abc6 100644 --- a/server/static/static.go +++ b/server/static/static.go @@ -6,15 +6,24 @@ import ( "strings" ) +var ( + extraPath = "/extra/" +) + type FilesServer struct { - baseHRef string - hsts bool - xframeOpts string - corsAllowOrigin string + baseHRef string + hsts bool + xframeOpts string + corsAllowOrigin string + extraFilesHandler http.Handler } -func NewFilesServer(baseHRef string, hsts bool, xframeOpts string, corsAllowOrigin string) *FilesServer { - return &FilesServer{baseHRef, hsts, xframeOpts, corsAllowOrigin} +func NewFilesServer(baseHRef string, hsts bool, xframeOpts string, corsAllowOrigin string, extraFilesDir string) *FilesServer { + var extraFilesHandler http.Handler + if extraFilesDir != "" { + extraFilesHandler = http.StripPrefix(extraPath, http.FileServer(http.Dir(extraFilesDir))) + } + return &FilesServer{baseHRef, hsts, xframeOpts, corsAllowOrigin, extraFilesHandler} } func (s *FilesServer) ServerFiles(w http.ResponseWriter, r *http.Request) { @@ -50,6 +59,13 @@ func (s *FilesServer) ServerFiles(w http.ResponseWriter, r *http.Request) { w.Header().Set("Strict-Transport-Security", "max-age=31536000") } + if s.extraFilesHandler != nil { + if strings.HasPrefix(r.URL.Path, extraPath) { + s.extraFilesHandler.ServeHTTP(w, r) + return + } + } + // in my IDE (IntelliJ) the next line is red for some reason - but this is fine ServeHTTP(w, r) } From 30b30354946b913ef7bd966f4250e557302263aa Mon Sep 17 00:00:00 2001 From: Daisuke Taniwaki Date: Mon, 5 Jul 2021 22:13:14 +0900 Subject: [PATCH 3/7] fix(ui): Fix lint issues Signed-off-by: Daisuke Taniwaki --- ui/src/app/app.tsx | 2 +- ui/src/app/shared/services/info-service.ts | 4 ++-- ui/src/models/info.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/src/app/app.tsx b/ui/src/app/app.tsx index ad6958bc89b3..7ab570ec9fc0 100644 --- a/ui/src/app/app.tsx +++ b/ui/src/app/app.tsx @@ -5,7 +5,7 @@ import * as PropTypes from 'prop-types'; import * as React from 'react'; import {AppRouter} from './app-router'; import {ContextApis, Provider} from './shared/context'; -import { services } from './shared/services'; +import {services} from './shared/services'; const history = createBrowserHistory(); diff --git a/ui/src/app/shared/services/info-service.ts b/ui/src/app/shared/services/info-service.ts index 6e4f7369e1e5..b446b7ba0264 100644 --- a/ui/src/app/shared/services/info-service.ts +++ b/ui/src/app/shared/services/info-service.ts @@ -1,5 +1,5 @@ import request = require('superagent'); -import {GetUserInfoResponse, Info, Version, Settings} from '../../../models'; +import {GetUserInfoResponse, Info, Settings, Version} from '../../../models'; import requests from './requests'; @@ -23,6 +23,6 @@ export class InfoService { } public getSettings() { - return request.get(`api/v1/settings`).then(res => res.body as Settings) + return request.get(`api/v1/settings`).then(res => res.body as Settings); } } diff --git a/ui/src/models/info.ts b/ui/src/models/info.ts index 393b29bb2a6c..f4564f7b19b5 100644 --- a/ui/src/models/info.ts +++ b/ui/src/models/info.ts @@ -23,5 +23,5 @@ export interface GetUserInfoResponse { } export interface Settings { - uiCssURL?: string; + uiCssURL?: string; } From 916d076e827abedffb432a5c1e358a851a31e0fc Mon Sep 17 00:00:00 2001 From: Daisuke Taniwaki Date: Mon, 5 Jul 2021 22:25:40 +0900 Subject: [PATCH 4/7] fix(server): Fix extra files handler Signed-off-by: Daisuke Taniwaki --- server/static/static.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/server/static/static.go b/server/static/static.go index d4070b46abc6..b72a809f1a4d 100644 --- a/server/static/static.go +++ b/server/static/static.go @@ -27,6 +27,13 @@ func NewFilesServer(baseHRef string, hsts bool, xframeOpts string, corsAllowOrig } func (s *FilesServer) ServerFiles(w http.ResponseWriter, r *http.Request) { + if s.extraFilesHandler != nil { + if strings.HasPrefix(r.URL.Path, extraPath) { + s.extraFilesHandler.ServeHTTP(w, r) + return + } + } + // If there is no stored static file, we'll redirect to the js app if Hash(strings.TrimLeft(r.URL.Path, "/")) == "" { r.URL.Path = "index.html" @@ -59,13 +66,6 @@ func (s *FilesServer) ServerFiles(w http.ResponseWriter, r *http.Request) { w.Header().Set("Strict-Transport-Security", "max-age=31536000") } - if s.extraFilesHandler != nil { - if strings.HasPrefix(r.URL.Path, extraPath) { - s.extraFilesHandler.ServeHTTP(w, r) - return - } - } - // in my IDE (IntelliJ) the next line is red for some reason - but this is fine ServeHTTP(w, r) } From d18649872806dccce3a6b376d4d57f533c30de46 Mon Sep 17 00:00:00 2001 From: Daisuke Taniwaki Date: Mon, 5 Jul 2021 22:28:47 +0900 Subject: [PATCH 5/7] fix: Update docs Signed-off-by: Daisuke Taniwaki --- docs/cli/argo_server.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/cli/argo_server.md b/docs/cli/argo_server.md index bd21e5692399..9c6637dff602 100644 --- a/docs/cli/argo_server.md +++ b/docs/cli/argo_server.md @@ -27,6 +27,7 @@ See https://argoproj.github.io/argo-workflows/argo-server.md --configmap string Name of K8s configmap to retrieve workflow controller configuration (default "workflow-controller-configmap") --event-operation-queue-size int how many events operations that can be queued at once (default 16) --event-worker-count int how many event workers to run (default 4) + --extra-files-dir string Value to serve files from a specified directory. Defaults no serve. -h, --help help for server --hsts Whether or not we should add a HTTP Secure Transport Security header. This only has effect if secure is enabled. (default true) --log-format string The formatter to use for logs. One of: text|json (default "text") From 8a66f71af07c7782e805858b40e59942a5d04103 Mon Sep 17 00:00:00 2001 From: Daisuke Taniwaki Date: Mon, 5 Jul 2021 23:40:17 +0900 Subject: [PATCH 6/7] fix(server): Fix extra files serving Signed-off-by: Daisuke Taniwaki --- server/static/static.go | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/server/static/static.go b/server/static/static.go index b72a809f1a4d..231b9ff5386b 100644 --- a/server/static/static.go +++ b/server/static/static.go @@ -27,24 +27,6 @@ func NewFilesServer(baseHRef string, hsts bool, xframeOpts string, corsAllowOrig } func (s *FilesServer) ServerFiles(w http.ResponseWriter, r *http.Request) { - if s.extraFilesHandler != nil { - if strings.HasPrefix(r.URL.Path, extraPath) { - s.extraFilesHandler.ServeHTTP(w, r) - return - } - } - - // If there is no stored static file, we'll redirect to the js app - if Hash(strings.TrimLeft(r.URL.Path, "/")) == "" { - r.URL.Path = "index.html" - } - - if r.URL.Path == "index.html" { - // hack to prevent ServerHTTP from giving us gzipped content which we can do our search-and-replace on - r.Header.Del("Accept-Encoding") - w = &responseRewriter{ResponseWriter: w, old: []byte(``), new: []byte(fmt.Sprintf(``, s.baseHRef))} - } - if s.xframeOpts != "" { w.Header().Set("X-Frame-Options", s.xframeOpts) } @@ -66,6 +48,25 @@ func (s *FilesServer) ServerFiles(w http.ResponseWriter, r *http.Request) { w.Header().Set("Strict-Transport-Security", "max-age=31536000") } + // Serve extra files if any. + if s.extraFilesHandler != nil { + if strings.HasPrefix(r.URL.Path, extraPath) { + s.extraFilesHandler.ServeHTTP(w, r) + return + } + } + + // If there is no stored static file, we'll redirect to the js app + if Hash(strings.TrimLeft(r.URL.Path, "/")) == "" { + r.URL.Path = "index.html" + } + + if r.URL.Path == "index.html" { + // hack to prevent ServerHTTP from giving us gzipped content which we can do our search-and-replace on + r.Header.Del("Accept-Encoding") + w = &responseRewriter{ResponseWriter: w, old: []byte(``), new: []byte(fmt.Sprintf(``, s.baseHRef))} + } + // in my IDE (IntelliJ) the next line is red for some reason - but this is fine ServeHTTP(w, r) } From 8aeeee708aed0b0381409fc0e13522faf9336369 Mon Sep 17 00:00:00 2001 From: Daisuke Taniwaki Date: Tue, 6 Jul 2021 00:05:18 +0900 Subject: [PATCH 7/7] fix(server): Handle no extra files case Signed-off-by: Daisuke Taniwaki --- server/static/static.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/server/static/static.go b/server/static/static.go index 231b9ff5386b..1dcaf91a98cd 100644 --- a/server/static/static.go +++ b/server/static/static.go @@ -49,11 +49,13 @@ func (s *FilesServer) ServerFiles(w http.ResponseWriter, r *http.Request) { } // Serve extra files if any. - if s.extraFilesHandler != nil { - if strings.HasPrefix(r.URL.Path, extraPath) { + if strings.HasPrefix(r.URL.Path, extraPath) { + if s.extraFilesHandler != nil { s.extraFilesHandler.ServeHTTP(w, r) - return + } else { + http.NotFound(w, r) } + return } // If there is no stored static file, we'll redirect to the js app