-
Notifications
You must be signed in to change notification settings - Fork 48
/
version.go
166 lines (139 loc) · 5.33 KB
/
version.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package pool
// Copyright (c) 2013-2017 The btcsuite developers
// Copyright (c) 2015-2016 The Decred developers
// Heavily inspired by https://github.com/btcsuite/btcd/blob/master/version.go
// Copyright (C) 2015-2019 The Lightning Network Developers
import (
"bytes"
"context"
"fmt"
"math"
"strings"
"google.golang.org/grpc/metadata"
)
// Commit stores the current commit hash of this build, this should be set
// using the -ldflags during compilation.
var Commit string
// semanticAlphabet is the allowed characters from the semantic versioning
// guidelines for pre-release version and build metadata strings. In particular
// they MUST only contain characters in semanticAlphabet.
const semanticAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-"
// These constants define the application version and follow the semantic
// versioning 2.0.0 spec (http://semver.org/).
const (
appMajor uint = 0
appMinor uint = 6
appPatch uint = 5
// appPreRelease MUST only contain characters from semanticAlphabet per
// the semantic versioning spec.
appPreRelease = "beta"
// defaultAgentName is the default name of the software that is added as
// the first part of the user agent string.
defaultAgentName = "poold"
// additionalInfoInitiator is the field name of the initiator in the
// additional information part of the user agent.
additionalInfoInitiator = "initiator"
)
// agentName stores the name of the software that is added as the first part of
// the user agent string. This defaults to the value "poold" when being run as
// a standalone component but can be overwritten by LiT for example when poold
// is integrated into the UI.
var agentName = defaultAgentName
// SetAgentName overwrites the default agent name which can be used to identify
// the software Pool is bundled in (for example LiT). This function panics if
// the agent name contains characters outside of the allowed semantic alphabet.
func SetAgentName(newAgentName string) {
for _, r := range newAgentName {
if !strings.ContainsRune(semanticAlphabet, r) {
panic(fmt.Errorf("rune: %v is not in the semantic "+
"alphabet", r))
}
}
agentName = newAgentName
}
// Version returns the application version as a properly formed string per the
// semantic versioning 2.0.0 spec (http://semver.org/) and the commit it was
// built on.
func Version() string {
// Append commit hash of current build to version.
return fmt.Sprintf("%s commit=%s", semanticVersion(), Commit)
}
// semanticVersion returns the SemVer part of the version.
func semanticVersion() string {
// Start with the major, minor, and patch versions.
version := fmt.Sprintf("%d.%d.%d", appMajor, appMinor, appPatch)
// Append pre-release version if there is one. The hyphen called for
// by the semantic versioning spec is automatically appended and should
// not be contained in the pre-release string. The pre-release version
// is not appended if it contains invalid characters.
preRelease := normalizeVerString(appPreRelease, semanticAlphabet)
if preRelease != "" {
version = fmt.Sprintf("%s-%s", version, preRelease)
}
return version
}
// UserAgent returns the full user agent string that identifies the software
// that is submitting swaps to the loop server.
func UserAgent(initiator string) string {
// We'll only allow "safe" characters in the initiator portion of the
// user agent string and spaces only if surrounded by other characters.
initiatorAlphabet := semanticAlphabet + ". "
cleanInitiator := normalizeVerString(
strings.TrimSpace(initiator), initiatorAlphabet,
)
if len(cleanInitiator) > 0 {
cleanInitiator = fmt.Sprintf(",%s=%s", additionalInfoInitiator,
cleanInitiator)
}
// The whole user agent string is limited to 255 characters server side
// and also consists of the agent name, version and commit. So we only
// want to take up at most 150 characters for the initiator. Anything
// more will just be dropped.
strLen := len(cleanInitiator)
cleanInitiator = cleanInitiator[:int(math.Min(float64(strLen), 150))]
// Assemble full string, including the commit hash of current build.
return fmt.Sprintf(
"%s/v%s/commit=%s%s", agentName, semanticVersion(), Commit,
cleanInitiator,
)
}
// ContextWithInitiator creates a new context with the given initiator string
// added (provided it is not empty).
func ContextWithInitiator(ctx context.Context,
initiator string) context.Context {
trimmed := strings.TrimSpace(initiator)
if trimmed == "" {
return ctx
}
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
md = metadata.MD{}
}
md[additionalInfoInitiator] = []string{trimmed}
return metadata.NewIncomingContext(ctx, md)
}
// InitiatorFromContext attempts to read the initiator string from a context and
// returns it if successful. If no initiator string can be found then the empty
// string is returned.
func InitiatorFromContext(ctx context.Context) string {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return ""
}
initiators := md[additionalInfoInitiator]
if len(initiators) == 0 {
return ""
}
return initiators[0]
}
// normalizeVerString returns the passed string stripped of all characters
// which are not valid according to the given alphabet.
func normalizeVerString(str, alphabet string) string {
var result bytes.Buffer
for _, r := range str {
if strings.ContainsRune(alphabet, r) {
result.WriteRune(r)
}
}
return result.String()
}