istio

Форк
0
/
collection.go 
237 строк · 6.1 Кб
1
//  Copyright Istio Authors
2
//
3
//  Licensed under the Apache License, Version 2.0 (the "License");
4
//  you may not use this file except in compliance with the License.
5
//  You may obtain a copy of the License at
6
//
7
//      http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//  Unless required by applicable law or agreed to in writing, software
10
//  distributed under the License is distributed on an "AS IS" BASIS,
11
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//  See the License for the specific language governing permissions and
13
//  limitations under the License.
14

15
package topics
16

17
import (
18
	"fmt"
19
	"html/template"
20
	"net/http"
21
	"sort"
22
	"strings"
23

24
	"sigs.k8s.io/yaml"
25

26
	"istio.io/istio/pkg/ctrlz/fw"
27
	"istio.io/istio/pkg/ctrlz/topics/assets"
28
)
29

30
// ReadableCollection is a staticCollection collection of entries to be exposed via CtrlZ.
31
type ReadableCollection interface {
32
	Name() string
33
	Keys() (keys []string, err error)
34
	Get(id string) (any, error)
35
}
36

37
// collection topic is a Topic fw.implementation that exposes a set of collections through CtrlZ.
38
type collectionTopic struct {
39
	title       string
40
	prefix      string
41
	collections []ReadableCollection
42

43
	mainTmpl *template.Template
44
	listTmpl *template.Template
45
	itemTmpl *template.Template
46
}
47

48
var _ fw.Topic = &collectionTopic{}
49

50
// Title is implementation of Topic.Title.
51
func (c *collectionTopic) Title() string {
52
	return c.title
53
}
54

55
// Prefix is implementation of Topic.Prefix.
56
func (c *collectionTopic) Prefix() string {
57
	return c.prefix
58
}
59

60
// Activate is implementation of Topic.Activate.
61
func (c *collectionTopic) Activate(context fw.TopicContext) {
62
	l := template.Must(context.Layout().Clone())
63
	c.mainTmpl = assets.ParseTemplate(l, "templates/collection/main.html")
64

65
	l = template.Must(context.Layout().Clone())
66
	c.listTmpl = assets.ParseTemplate(l, "templates/collection/list.html")
67

68
	l = template.Must(context.Layout().Clone())
69
	c.itemTmpl = assets.ParseTemplate(l, "templates/collection/item.html")
70

71
	_ = context.HTMLRouter().
72
		StrictSlash(true).
73
		NewRoute().
74
		PathPrefix("/").
75
		Methods("GET").
76
		HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
77
			parts := strings.SplitN(req.URL.Path, "/", 4)
78
			parts = parts[2:] // Skip the empty and title parts.
79

80
			switch len(parts) {
81
			case 1:
82
				if parts[0] == "" {
83
					c.handleMain(w, req)
84
				} else {
85
					c.handleCollection(w, req, parts[0])
86
				}
87

88
			case 2:
89
				c.handleItem(w, req, parts[0], parts[1])
90

91
			default:
92
				c.handleError(w, req, fmt.Sprintf("InvalidUrl %s", req.URL.Path))
93
			}
94
		})
95
}
96

97
// mainContext is passed to the template processor and carries information that is used by the main template.
98
type mainContext struct {
99
	Title       string
100
	Collections []string
101
	Error       string
102
}
103

104
func (c *collectionTopic) handleMain(w http.ResponseWriter, _ *http.Request) {
105
	context := mainContext{}
106
	names := make([]string, 0, len(c.collections))
107
	for _, n := range c.collections {
108
		names = append(names, n.Name())
109
	}
110
	sort.Strings(names)
111
	context.Collections = names
112
	context.Title = c.title
113
	fw.RenderHTML(w, c.mainTmpl, context)
114
}
115

116
// listContext is passed to the template processor and carries information that is used by the list template.
117
type listContext struct {
118
	Collection string
119
	Keys       []string
120
	Error      string
121
}
122

123
func (c *collectionTopic) handleCollection(w http.ResponseWriter, _ *http.Request, collection string) {
124
	k, err := c.listCollection(collection)
125
	context := listContext{}
126
	if err == nil {
127
		context.Collection = collection
128
		context.Keys = k
129
	} else {
130
		context.Error = err.Error()
131
	}
132
	fw.RenderHTML(w, c.listTmpl, context)
133
}
134

135
// itemContext is passed to the template processor and carries information that is used by the list template.
136
type itemContext struct {
137
	Collection string
138
	Key        string
139
	Value      any
140
	Error      string
141
}
142

143
func (c *collectionTopic) handleItem(w http.ResponseWriter, _ *http.Request, collection, key string) {
144
	v, err := c.getItem(collection, key)
145
	context := itemContext{}
146
	if err == nil {
147
		switch v.(type) {
148
		case string:
149

150
		default:
151
			var b []byte
152
			if b, err = yaml.Marshal(v); err != nil {
153
				context.Error = err.Error()
154
				break
155
			}
156
			v = string(b)
157
		}
158

159
		context.Collection = collection
160
		context.Key = key
161
		context.Value = v
162
	} else {
163
		context.Error = err.Error()
164
	}
165
	fw.RenderHTML(w, c.itemTmpl, context)
166
}
167

168
func (c *collectionTopic) handleError(w http.ResponseWriter, _ *http.Request, errorText string) {
169
	fw.RenderHTML(w, c.mainTmpl, mainContext{Error: errorText})
170
}
171

172
func (c *collectionTopic) listCollection(name string) ([]string, error) {
173
	for _, col := range c.collections {
174
		if col.Name() == name {
175
			return col.Keys()
176
		}
177
	}
178

179
	return nil, fmt.Errorf("collection not found: %q", name)
180
}
181

182
func (c *collectionTopic) getItem(collection string, id string) (any, error) {
183
	for _, col := range c.collections {
184
		if col.Name() == collection {
185
			return col.Get(id)
186
		}
187
	}
188

189
	return nil, fmt.Errorf("collection not found: %q", collection)
190
}
191

192
// NewCollectionTopic creates a new custom topic that exposes the provided collections.
193
func NewCollectionTopic(title string, prefix string, collections ...ReadableCollection) fw.Topic {
194
	return &collectionTopic{
195
		title:       title,
196
		prefix:      prefix,
197
		collections: collections,
198
	}
199
}
200

201
// NewStaticCollection creates a static collection from the given set of items.
202
func NewStaticCollection(name string, items map[string]any) ReadableCollection {
203
	return &staticCollection{
204
		name:  name,
205
		items: items,
206
	}
207
}
208

209
// staticCollection is a ReadableCollection implementation that operates on static data that is supplied
210
// during construction.
211
type staticCollection struct {
212
	name  string
213
	items map[string]any
214
}
215

216
var _ ReadableCollection = &staticCollection{}
217

218
// Name is implementation of ReadableCollection.Name.
219
func (r *staticCollection) Name() string {
220
	return r.name
221
}
222

223
// Keys is implementation of ReadableCollection.Keys.
224
func (r *staticCollection) Keys() ([]string, error) {
225
	keys := make([]string, 0, len(r.items))
226
	for k := range r.items {
227
		keys = append(keys, k)
228
	}
229
	sort.Strings(keys)
230

231
	return keys, nil
232
}
233

234
// Get is implementation of ReadableCollection.Get.
235
func (r *staticCollection) Get(id string) (any, error) {
236
	return r.items[id], nil
237
}
238

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.