Skip to content

Commit c2472ee

Browse files
committed
Add SchemaVersion and PackageVersion to packages
Signed-off-by: Jose Fuentes <[email protected]>
1 parent 8d61ed6 commit c2472ee

File tree

7 files changed

+150
-28
lines changed

7 files changed

+150
-28
lines changed

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ require (
77
github.com/aws/aws-sdk-go v1.25.30
88
github.com/gomarkdown/markdown v0.0.0-20191104174740-4d42851d4d5a
99
github.com/gookit/color v1.2.0
10+
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6 // indirect
1011
github.com/open-policy-agent/opa v0.15.0
1112
github.com/pkg/errors v0.8.1
1213
github.com/sergi/go-diff v1.0.0 // indirect
1314
github.com/spf13/cobra v0.0.5
1415
github.com/spf13/viper v1.5.0
1516
github.com/yudai/gojsondiff v1.0.0
1617
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
18+
golang.org/x/arch v0.0.0-20191126211547-368ea8f32fff // indirect
1719
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
1820
google.golang.org/api v0.13.0
1921
gopkg.in/yaml.v2 v2.2.5

go.sum

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSN
9292
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
9393
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
9494
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
95+
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f h1:Jnx61latede7zDD3DiiP4gmNz33uK0U5HDUaF0a/HVQ=
9596
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
9697
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
9798
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -117,6 +118,8 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
117118
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
118119
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
119120
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
121+
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6 h1:UDMh68UUwekSh5iP2OMhRRZJiiBccgV7axzUG8vi56c=
122+
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
120123
github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q=
121124
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
122125
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
@@ -238,6 +241,8 @@ go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
238241
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
239242
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
240243
golang.org/dl v0.0.0-20190829154251-82a15e2f2ead/go.mod h1:IUMfjQLJQd4UTqG1Z90tenwKoCX93Gn3MAQJMOSBsDQ=
244+
golang.org/x/arch v0.0.0-20191126211547-368ea8f32fff h1:k/MrR0lKiCokRu1JUDDAWhWZinfBAOZRzz3LkPOkFMs=
245+
golang.org/x/arch v0.0.0-20191126211547-368ea8f32fff/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
241246
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
242247
golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
243248
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -398,5 +403,6 @@ k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH
398403
k8s.io/utils v0.0.0-20190221042446-c2654d5206da h1:ElyM7RPonbKnQqOcw7dG2IK5uvQQn3b/WPHqD5mBvP4=
399404
k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0=
400405
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
406+
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
401407
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
402408
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

pkg/lint/manifest.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,18 @@ func LintPolicyManifest(manifest packaging.PolicyManifest) []LintError {
2424
lint("Manifest Namespace absent")
2525
}
2626

27+
if manifest.SchemaVersion == "" {
28+
lint("Manifest SchemaVersion absent")
29+
} else if !isSemver(manifest.SchemaVersion) {
30+
lint("Manifest SchemaVersion must be semver")
31+
}
32+
33+
if manifest.PackageVersion == "" {
34+
lint("Manifest PackageVersion absent")
35+
} else if !isSemver(manifest.PackageVersion) {
36+
lint("Manifest PackageVersion must be semver")
37+
}
38+
2739
sections := manifest.Sections
2840
if len(sections) == 0 {
2941
lint("No sections in manifest")
@@ -172,3 +184,8 @@ func LintRule(sectionID string, rule packaging.Rule) []LintError {
172184

173185
return lints
174186
}
187+
188+
func isSemver(version string) bool {
189+
match, _ := regexp.MatchString("^v?\\d+.\\d+.\\d+(-.+)?$", version)
190+
return match
191+
}

pkg/lint/manifest_test.go

Lines changed: 84 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -211,9 +211,11 @@ func TestSectionLint(t *testing.T) {
211211

212212
func TestLintPolicyManifestSuccess(t *testing.T) {
213213
validPackage := packaging.PolicyManifest{
214-
ID: "mypackage",
215-
Namespace: "mynamespace",
216-
Name: "My Package",
214+
SchemaVersion: "1.0.0",
215+
ID: "mypackage",
216+
Namespace: "mynamespace",
217+
Name: "My Package",
218+
PackageVersion: "1.0.0",
217219
Sections: []packaging.Section{
218220
{
219221
ID: "1.2",
@@ -246,23 +248,65 @@ var invalidManifests = []struct {
246248
name: "No sections",
247249
expectedLint: "No sections in manifest",
248250
manifest: packaging.PolicyManifest{
249-
ID: "mypackage",
250-
Name: "My Package",
251+
SchemaVersion: "1.0.0",
252+
ID: "mypackage",
253+
Name: "My Package",
254+
PackageVersion: "1.0.0",
251255
},
252256
},
253257
{
254258
name: "No ID",
255259
expectedLint: "Manifest ID absent",
256260
manifest: packaging.PolicyManifest{
257-
Name: "My Package",
261+
SchemaVersion: "1.0.0",
262+
Name: "My Package",
263+
PackageVersion: "1.0.0",
264+
},
265+
},
266+
{
267+
name: "No SchemaVersion",
268+
expectedLint: "Manifest SchemaVersion absent",
269+
manifest: packaging.PolicyManifest{
270+
ID: "mypackage",
271+
Name: "My Package",
272+
PackageVersion: "1.0.0",
273+
},
274+
},
275+
{
276+
name: "SchemaVersion not semver",
277+
expectedLint: "Manifest SchemaVersion must be semver",
278+
manifest: packaging.PolicyManifest{
279+
ID: "mypackage",
280+
Name: "My Package",
281+
SchemaVersion: "1",
282+
},
283+
},
284+
{
285+
name: "No PackageVersion",
286+
expectedLint: "Manifest PackageVersion absent",
287+
manifest: packaging.PolicyManifest{
288+
ID: "mypackage",
289+
Name: "My Package",
290+
SchemaVersion: "1.0.0",
291+
},
292+
},
293+
{
294+
name: "PackageVersion not semver",
295+
expectedLint: "Manifest PackageVersion must be semver",
296+
manifest: packaging.PolicyManifest{
297+
ID: "mypackage",
298+
Name: "My Package",
299+
PackageVersion: "1",
258300
},
259301
},
260302
{
261303
name: "Duplicated section ID",
262304
expectedLint: "Section ID 1.2 duplicated 2 times",
263305
manifest: packaging.PolicyManifest{
264-
ID: "1",
265-
Name: "My Package",
306+
SchemaVersion: "1.0.0",
307+
ID: "1",
308+
Name: "My Package",
309+
PackageVersion: "1.0.0",
266310
Sections: []packaging.Section{
267311
{
268312
ID: "1.2",
@@ -291,8 +335,10 @@ var invalidManifests = []struct {
291335
name: "Duplicated section Name",
292336
expectedLint: "Section Name 'foobar' duplicated 2 times",
293337
manifest: packaging.PolicyManifest{
294-
ID: "1",
295-
Name: "My Package",
338+
SchemaVersion: "1.0.0",
339+
ID: "1",
340+
Name: "My Package",
341+
PackageVersion: "1.0.0",
296342
Sections: []packaging.Section{
297343
{
298344
ID: "1.1",
@@ -321,8 +367,10 @@ var invalidManifests = []struct {
321367
name: "Duplicated section Name",
322368
expectedLint: "Rule Name 'My Rule' duplicated 3 times",
323369
manifest: packaging.PolicyManifest{
324-
ID: "1",
325-
Name: "My Package",
370+
SchemaVersion: "1.0.0",
371+
ID: "1",
372+
Name: "My Package",
373+
PackageVersion: "1.0.0",
326374
Sections: []packaging.Section{
327375
{
328376
ID: "1.1",
@@ -382,3 +430,27 @@ func TestManfiestLint(t *testing.T) {
382430
)
383431
}
384432
}
433+
434+
func TestIsSemver(t *testing.T) {
435+
semverTcs := []struct {
436+
version string
437+
result bool
438+
}{
439+
{"v1.0.0", true},
440+
{"1.0.0", true},
441+
{"v1.0.0-alpha", true},
442+
{"1.0.0-alpha", true},
443+
{"1", false},
444+
{"a", false},
445+
{"a.b.c", false},
446+
{"", false},
447+
}
448+
449+
for _, tc := range semverTcs {
450+
t.Run(tc.version, func(t *testing.T) {
451+
if got, want := isSemver(tc.version), tc.result; got != want {
452+
t.Errorf("Failed to check if %s is semver: got = %v, want = %v", tc.version, got, want)
453+
}
454+
})
455+
}
456+
}

pkg/packaging/packaging.go

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,24 @@ type Package interface {
2222

2323
// PolicyManifest contains all the information about the policy manifest of the package.
2424
type PolicyManifest struct {
25-
Namespace string `yaml:"namespace"`
26-
ID string `yaml:"id"`
27-
DataGatherers []string `yaml:"data-gatherers,omitempty"`
28-
RootQuery string `yaml:"root-query"`
29-
Name string `yaml:"name"`
30-
Description string `yaml:"description,omitempty"`
31-
Sections []Section `yaml:"sections,omitempty"`
25+
// SchemaVersion is the version of the PolicyManifest schema, and thus the version of the Preflight Package format. It follows semver.
26+
SchemaVersion string `yaml:"schema-version"`
27+
// PackageVersion is the version of the package. No format is enforced, but it is recommended to follow semver.
28+
PackageVersion string `yaml:"package-version"`
29+
// Namespace is the namespace of the package. We recommend to use FQDNs.
30+
Namespace string `yaml:"namespace"`
31+
// ID is the ID of the package itself.
32+
ID string `yaml:"id"`
33+
// DataGatherers is the list of data-gatherers the package depends on.
34+
DataGatherers []string `yaml:"data-gatherers,omitempty"`
35+
// RootQuery is the query needed in the REGO context to access the result of the checks.
36+
RootQuery string `yaml:"root-query"`
37+
// Name is the name of the package.
38+
Name string `yaml:"name"`
39+
// Description is a text describing the package.
40+
Description string `yaml:"description,omitempty"`
41+
// Sections contains the different sections inside the package.
42+
Sections []Section `yaml:"sections,omitempty"`
3243
}
3344

3445
// GlobalID returns a global unique ID that contains the namespace and the ID.
@@ -38,20 +49,30 @@ func (m *PolicyManifest) GlobalID() string {
3849

3950
// Section holds the information for a section of the policy manifest.
4051
type Section struct {
41-
ID string `yaml:"id"`
42-
Name string `yaml:"name"`
52+
// ID is the ID of the section.
53+
ID string `yaml:"id"`
54+
// Name is the name of the section.
55+
Name string `yaml:"name"`
56+
// Description is the description of the section.
4357
Description string `yaml:"description,omitempty"`
44-
Rules []Rule `yaml:"rules,omitempty"`
58+
// Rules contain all the rules in the section.
59+
Rules []Rule `yaml:"rules,omitempty"`
4560
}
4661

4762
// Rule holds the information for a rule.
4863
type Rule struct {
49-
ID string `yaml:"id"`
50-
Name string `yaml:"name"`
51-
Description string `yaml:"description,omitempty"`
52-
Manual bool `yaml:"manual,omitempty"`
53-
Remediation string `yaml:"remediation,omitempty"`
54-
Links []string `yaml:"links,omitempty"`
64+
// ID is the id of the rule.
65+
ID string `yaml:"id"`
66+
// Name is a shortname for the rule.
67+
Name string `yaml:"name"`
68+
// Description is a text describing what the rule is about.
69+
Description string `yaml:"description,omitempty"`
70+
// Manual indicated whether the rule can be evaluated automatically by Preflight or requires manual intervention.
71+
Manual bool `yaml:"manual,omitempty"`
72+
// Remediation is a text describing how to fix a failure of the rule.
73+
Remediation string `yaml:"remediation,omitempty"`
74+
// Links contains useful links related to the rule.
75+
Links []string `yaml:"links,omitempty"`
5576
}
5677

5778
// EvalPackage evaluated the rules in a package given an input

preflight-packages/examples.jetstack.io/gke_basic/policy-manifest.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
schema-version: "1.0.0"
12
id: "gke_basic"
23
namespace: "examples.jetstack.io"
4+
package-version: "1.0.0"
35
data-gatherers:
46
- gke
57
# `root-query` selects selects the rego package: `data.<repo_package>`.

preflight-packages/examples.jetstack.io/pods_basic/policy-manifest.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
schema-version: "1.0.0"
12
id: "pods_basic"
23
namespace: "examples.jetstack.io"
4+
package-version: "1.0.0"
35
data-gatherers:
46
- k8s/pods
57
# `root-query` selects selects the rego package: `data.<repo_package>`.

0 commit comments

Comments
 (0)