forked from open-policy-agent/conftest
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdocker.go
114 lines (88 loc) · 2.52 KB
/
docker.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
package docker
import (
"bytes"
"encoding/json"
"fmt"
"github.com/moby/buildkit/frontend/dockerfile/instructions"
"github.com/moby/buildkit/frontend/dockerfile/parser"
)
// Parser is a Dockerfile parser.
type Parser struct{}
// Command represents a command in a Dockerfile.
type Command struct {
// Lowercased command name (ex: `from`)
Cmd string
// For ONBUILD only this holds the sub-command
SubCmd string
// Whether the value is written in json form
JSON bool
// Any flags such as `--from=...` for `COPY`.
Flags []string
// The contents of the command (ex: `ubuntu:xenial`)
Value []string
// Stage indicates which stage the command is found in a multistage docker build
Stage int
}
// Unmarshal unmarshals Dockerfiles
func (dp *Parser) Unmarshal(p []byte, v interface{}) error {
r := bytes.NewReader(p)
res, err := parser.Parse(r)
if err != nil {
return fmt.Errorf("parse dockerfile: %w", err)
}
var commands []Command
var stages []*instructions.Stage
for _, child := range res.AST.Children {
instr, err := instructions.ParseInstruction(child)
if err != nil {
return fmt.Errorf("process dockerfile instructions: %w", err)
}
stage, ok := instr.(*instructions.Stage)
if ok {
stages = append(stages, stage)
}
// PrevComment contains all of the comments that came before this node.
// In the event that comments exist, add them to the list of commands before
// adding the node itself.
for _, comment := range child.PrevComment {
cmd := Command{
Cmd: "comment",
Stage: currentStage(stages),
Value: []string{comment},
}
commands = append(commands, cmd)
}
cmd := Command{
Cmd: child.Value,
Flags: child.Flags,
Stage: currentStage(stages),
}
if child.Next != nil && len(child.Next.Children) > 0 {
cmd.SubCmd = child.Next.Children[0].Value
child = child.Next.Children[0]
}
cmd.JSON = child.Attributes["json"]
for n := child.Next; n != nil; n = n.Next {
cmd.Value = append(cmd.Value, n.Value)
}
commands = append(commands, cmd)
}
var dockerFile [][]Command
dockerFile = append(dockerFile, commands)
j, err := json.Marshal(dockerFile)
if err != nil {
return fmt.Errorf("marshal dockerfile to json: %w", err)
}
if err := json.Unmarshal(j, v); err != nil {
return fmt.Errorf("unmarshal dockerfile json: %w", err)
}
return nil
}
// Return the index of the stages. If no stages are present,
// we set the index to zero.
func currentStage(stages []*instructions.Stage) int {
if len(stages) == 0 {
return 0
}
return len(stages) - 1
}