Skip to content

Commit 7c21709

Browse files
authored
Merge pull request #12138 from jorgemarey/f-ns-meta
Add metadata to namespaces
2 parents bc40222 + 1004050 commit 7c21709

16 files changed

+122
-46
lines changed

.changelog/12138.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:improvement
2+
namespaces: Allow adding custom metadata to namespaces.
3+
```

api/namespace.go

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ type Namespace struct {
7171
Description string
7272
Quota string
7373
Capabilities *NamespaceCapabilities `hcl:"capabilities,block"`
74+
Meta map[string]string
7475
CreateIndex uint64
7576
ModifyIndex uint64
7677
}

command/agent/namespace_endpoint_test.go

-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
//go:build ent
2-
// +build ent
3-
41
package agent
52

63
import (

command/namespace_apply.go

+18-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,11 @@ func (c *NamespaceApplyCommand) AutocompleteFlags() complete.Flags {
6161
}
6262

6363
func (c *NamespaceApplyCommand) AutocompleteArgs() complete.Predictor {
64-
return NamespacePredictor(c.Meta.Client, nil)
64+
return complete.PredictOr(
65+
NamespacePredictor(c.Meta.Client, nil),
66+
complete.PredictFiles("*.hcl"),
67+
complete.PredictFiles("*.json"),
68+
)
6569
}
6670

6771
func (c *NamespaceApplyCommand) Synopsis() string {
@@ -216,6 +220,7 @@ func parseNamespaceSpecImpl(result *api.Namespace, list *ast.ObjectList) error {
216220
}
217221

218222
delete(m, "capabilities")
223+
delete(m, "meta")
219224

220225
// Decode the rest
221226
if err := mapstructure.WeakDecode(m, result); err != nil {
@@ -238,5 +243,17 @@ func parseNamespaceSpecImpl(result *api.Namespace, list *ast.ObjectList) error {
238243
}
239244
}
240245

246+
if metaO := list.Filter("meta"); len(metaO.Items) > 0 {
247+
for _, o := range metaO.Elem().Items {
248+
var m map[string]interface{}
249+
if err := hcl.DecodeObject(&m, o.Val); err != nil {
250+
return err
251+
}
252+
if err := mapstructure.WeakDecode(m, &result.Meta); err != nil {
253+
return err
254+
}
255+
}
256+
}
257+
241258
return nil
242259
}

command/namespace_apply_test.go

-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
//go:build ent
2-
// +build ent
3-
41
package command
52

63
import (

command/namespace_delete_test.go

-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
//go:build ent
2-
// +build ent
3-
41
package command
52

63
import (

command/namespace_inspect_test.go

-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
//go:build ent
2-
// +build ent
3-
41
package command
52

63
import (

command/namespace_list_test.go

+1-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
//go:build ent
2-
// +build ent
3-
41
package command
52

63
import (
@@ -10,10 +7,7 @@ import (
107
"github.com/mitchellh/cli"
118
)
129

13-
func TestNamespaceListCommand_Implements(t *testing.T) {
14-
t.Parallel()
15-
var _ cli.Command = &NamespaceListCommand{}
16-
}
10+
var _ cli.Command = (*NamespaceListCommand)(nil)
1711

1812
func TestNamespaceListCommand_Fails(t *testing.T) {
1913
t.Parallel()

command/namespace_status.go

+11
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package command
22

33
import (
44
"fmt"
5+
"sort"
56
"strings"
67

78
"github.com/hashicorp/nomad/api"
@@ -81,6 +82,16 @@ func (c *NamespaceStatusCommand) Run(args []string) int {
8182

8283
c.Ui.Output(formatNamespaceBasics(ns))
8384

85+
if len(ns.Meta) > 0 {
86+
c.Ui.Output(c.Colorize().Color("\n[bold]Metadata[reset]"))
87+
var meta []string
88+
for k := range ns.Meta {
89+
meta = append(meta, fmt.Sprintf("%s|%s", k, ns.Meta[k]))
90+
}
91+
sort.Strings(meta)
92+
c.Ui.Output(formatKV(meta))
93+
}
94+
8495
if ns.Quota != "" {
8596
quotas := client.Quotas()
8697
spec, _, err := quotas.Info(ns.Quota, nil)

command/namespace_status_oss_test.go

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//go:build !ent
2+
// +build !ent
3+
4+
package command
5+
6+
import "github.com/hashicorp/nomad/api"
7+
8+
func testQuotaSpec() *api.QuotaSpec {
9+
panic("not implemented - enterprise only")
10+
}

command/namespace_status_test.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
//go:build ent
2-
// +build ent
3-
41
package command
52

63
import (
@@ -77,6 +74,10 @@ func TestNamespaceStatusCommand_Good_Quota(t *testing.T) {
7774
srv, client, url := testServer(t, true, nil)
7875
defer srv.Shutdown()
7976

77+
if !srv.Enterprise {
78+
t.Skip("Skipping enterprise-only quota test")
79+
}
80+
8081
ui := cli.NewMockUi()
8182
cmd := &NamespaceStatusCommand{Meta: Meta{Ui: ui}}
8283

nomad/mock/mock.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -2246,8 +2246,10 @@ func AllocNetworkStatus() *structs.AllocNetworkStatus {
22462246
}
22472247

22482248
func Namespace() *structs.Namespace {
2249+
uuid := uuid.Generate()
22492250
ns := &structs.Namespace{
2250-
Name: fmt.Sprintf("team-%s", uuid.Generate()),
2251+
Name: fmt.Sprintf("team-%s", uuid),
2252+
Meta: map[string]string{"team": uuid},
22512253
Description: "test namespace",
22522254
CreateIndex: 100,
22532255
ModifyIndex: 200,

nomad/structs/structs.go

+21
Original file line numberDiff line numberDiff line change
@@ -4963,6 +4963,9 @@ type Namespace struct {
49634963
// Capabilities is the set of capabilities allowed for this namespace
49644964
Capabilities *NamespaceCapabilities
49654965

4966+
// Meta is the set of metadata key/value pairs that attached to the namespace
4967+
Meta map[string]string
4968+
49664969
// Hash is the hash of the namespace which is used to efficiently replicate
49674970
// cross-regions.
49684971
Hash []byte
@@ -5016,6 +5019,18 @@ func (n *Namespace) SetHash() []byte {
50165019
}
50175020
}
50185021

5022+
// sort keys to ensure hash stability when meta is stored later
5023+
var keys []string
5024+
for k := range n.Meta {
5025+
keys = append(keys, k)
5026+
}
5027+
sort.Strings(keys)
5028+
5029+
for _, k := range keys {
5030+
_, _ = hash.Write([]byte(k))
5031+
_, _ = hash.Write([]byte(n.Meta[k]))
5032+
}
5033+
50195034
// Finalize the hash
50205035
hashVal := hash.Sum(nil)
50215036

@@ -5035,6 +5050,12 @@ func (n *Namespace) Copy() *Namespace {
50355050
c.DisabledTaskDrivers = helper.CopySliceString(n.Capabilities.DisabledTaskDrivers)
50365051
nc.Capabilities = c
50375052
}
5053+
if n.Meta != nil {
5054+
nc.Meta = make(map[string]string, len(n.Meta))
5055+
for k, v := range n.Meta {
5056+
nc.Meta[k] = v
5057+
}
5058+
}
50385059
copy(nc.Hash, n.Hash)
50395060
return nc
50405061
}

website/content/api-docs/namespaces.mdx

+35-15
Original file line numberDiff line numberDiff line change
@@ -48,17 +48,24 @@ $ curl \
4848
```json
4949
[
5050
{
51-
"CreateIndex": 31,
52-
"Description": "Production API Servers",
53-
"ModifyIndex": 31,
54-
"Name": "api-prod",
51+
"Capabilities": null,
52+
"CreateIndex": 1,
53+
"Description": "Default shared namespace",
54+
"Meta": null,
55+
"ModifyIndex": 1,
56+
"Name": "default",
5557
"Quota": ""
5658
},
5759
{
58-
"CreateIndex": 5,
59-
"Description": "Default shared namespace",
60-
"ModifyIndex": 5,
61-
"Name": "default",
60+
"Capabilities": null,
61+
"CreateIndex": 17,
62+
"Description": "Development Staging Namespace",
63+
"Meta": {
64+
"type": "dev",
65+
"contact": "[email protected]"
66+
},
67+
"ModifyIndex": 17,
68+
"Name": "staging",
6269
"Quota": ""
6370
}
6471
]
@@ -88,19 +95,23 @@ The table below shows this endpoint's support for
8895

8996
```shell-session
9097
$ curl \
91-
https://localhost:4646/v1/namespace/api-prod
98+
https://localhost:4646/v1/namespace/staging
9299
```
93100

94101
### Sample Response
95102

96103
```json
97104
{
98-
"CreateIndex": 31,
99-
"Description": "Production API Servers",
100-
"Quota": "",
101-
"Hash": "N8WvePwqkp6J354eLJMKyhvsFdPELAos0VuBfMoVKoU=",
102-
"ModifyIndex": 31,
103-
"Name": "api-prod"
105+
"Capabilities": null,
106+
"CreateIndex": 17,
107+
"Description": "Development Staging Namespace",
108+
"Meta": {
109+
"type": "dev",
110+
"contact": "[email protected]"
111+
},
112+
"ModifyIndex": 17,
113+
"Name": "staging",
114+
"Quota": ""
104115
}
105116
```
106117

@@ -128,6 +139,10 @@ The table below shows this endpoint's support for
128139
- `Description` `(string: "")` - Specifies an optional human-readable
129140
description of the namespace.
130141

142+
- `Meta` `(object: null)` - Optional object with string keys and values of
143+
metadata to attach to the namespace. Namespace metadata is not used by Nomad
144+
and is intended for use by operators and third party tools.
145+
131146
- `Quota` `(string: "")` - Specifies an quota to attach to the namespace.
132147

133148
### Sample Payload
@@ -136,10 +151,15 @@ The table below shows this endpoint's support for
136151
{
137152
"Name": "api-prod",
138153
"Description": "Production API Servers",
154+
"Meta": {
155+
"contact": "[email protected]"
156+
},
139157
"Quota": "prod-quota"
140158
}
141159
```
142160

161+
Note that the `Quota` key is Enterprise-only.
162+
143163
### Sample Request
144164

145165
```shell-session

website/content/docs/commands/namespace/apply.mdx

+9-4
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ when introduced in Nomad 0.7.
1818
nomad namespace apply [options] <input>
1919
```
2020

21-
Apply is used to create or update a namespace. The specification file
21+
Apply is used to create or update a namespace. The HCL specification file
2222
will be read from stdin by specifying "-", otherwise a path to the file is
2323
expected.
2424

@@ -37,7 +37,7 @@ If ACLs are enabled, this command requires a management ACL token.
3737

3838
- `-description` : An optional human readable description for the namespace.
3939

40-
- `json` : Parse the input as a JSON namespace specification.
40+
- `-json` : Parse the input as a JSON namespace specification.
4141

4242
## Examples
4343

@@ -56,13 +56,18 @@ $ nomad namespace apply -quota= api-prod
5656

5757
Create a namespace from a file:
5858
```shell-session
59-
$ cat namespace.json
59+
$ cat namespace.hcl
6060
name = "dev"
6161
description = "Namespace for developers"
6262
6363
capabilities {
6464
enabled_task_drivers = ["docker", "exec"]
6565
disabled_task_drivers = ["raw_exec"]
6666
}
67-
$ nomad namespace apply namespace.json
67+
68+
meta {
69+
owner = "John Doe"
70+
contact_mail = "[email protected]
71+
}
72+
$ nomad namespace apply namespace.hcl
6873
```

website/content/docs/commands/namespace/status.mdx

+6-3
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,14 @@ View the status of a namespace:
3333

3434
```shell-session
3535
$ nomad namespace status default
36-
Name = default
37-
Description = Default shared namespace
38-
Quota = shared-default-quota
36+
Name = api-prod
37+
Description = Prod API servers
38+
Quota = prod
3939
EnabledDrivers = docker,exec
4040
DisabledDrivers = raw_exec
41+
42+
Metadata
43+
4144
4245
Quota Limits
4346
Region CPU Usage Memory Usage

0 commit comments

Comments
 (0)