Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
P
packer
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kristopher Ruzic
packer
Commits
f3c1132f
Commit
f3c1132f
authored
Dec 09, 2014
by
Mitchell Hashimoto
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
post-processor/atlas: make it
parent
6f66afce
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
530 additions
and
0 deletions
+530
-0
plugin/post-processor-atlas/main.go
plugin/post-processor-atlas/main.go
+15
-0
plugin/post-processor-atlas/main_test.go
plugin/post-processor-atlas/main_test.go
+1
-0
post-processor/atlas/artifact.go
post-processor/atlas/artifact.go
+37
-0
post-processor/atlas/post-processor.go
post-processor/atlas/post-processor.go
+258
-0
post-processor/atlas/post-processor_test.go
post-processor/atlas/post-processor_test.go
+136
-0
post-processor/atlas/util.go
post-processor/atlas/util.go
+47
-0
post-processor/atlas/util_test.go
post-processor/atlas/util_test.go
+36
-0
No files found.
plugin/post-processor-atlas/main.go
0 → 100644
View file @
f3c1132f
package
main
import
(
"github.com/mitchellh/packer/packer/plugin"
"github.com/mitchellh/packer/post-processor/atlas"
)
func
main
()
{
server
,
err
:=
plugin
.
Server
()
if
err
!=
nil
{
panic
(
err
)
}
server
.
RegisterPostProcessor
(
new
(
atlas
.
PostProcessor
))
server
.
Serve
()
}
plugin/post-processor-atlas/main_test.go
0 → 100644
View file @
f3c1132f
package
main
post-processor/atlas/artifact.go
0 → 100644
View file @
f3c1132f
package
main
import
(
"fmt"
)
const
BuilderId
=
"packer.post-processor.atlas"
type
Artifact
struct
{
Name
string
Type
string
Version
int
}
func
(
*
Artifact
)
BuilderId
()
string
{
return
BuilderId
}
func
(
a
*
Artifact
)
Files
()
[]
string
{
return
nil
}
func
(
a
*
Artifact
)
Id
()
string
{
return
fmt
.
Sprintf
(
"%s/%s/%d"
,
a
.
Name
,
a
.
Type
,
a
.
Version
)
}
func
(
a
*
Artifact
)
String
()
string
{
return
fmt
.
Sprintf
(
"%s/%s (v%d)"
,
a
.
Name
,
a
.
Type
,
a
.
Version
)
}
func
(
*
Artifact
)
State
(
name
string
)
interface
{}
{
return
nil
}
func
(
a
*
Artifact
)
Destroy
()
error
{
return
nil
}
post-processor/atlas/post-processor.go
0 → 100644
View file @
f3c1132f
package
main
import
(
"fmt"
"os"
"strconv"
"strings"
"github.com/hashicorp/atlas-go/archive"
"github.com/hashicorp/atlas-go/v1"
"github.com/mitchellh/mapstructure"
"github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/packer"
)
const
BuildEnvKey
=
"ATLAS_BUILD_ID"
// Artifacts can return a string for this state key and the post-processor
// will use automatically use this as the type. The user's value overrides
// this if `artifact_type_override` is set to true.
const
ArtifactStateType
=
"atlas.artifact.type"
// Artifacts can return a map[string]string for this state key and this
// post-processor will automatically merge it into the metadata for any
// uploaded artifact versions.
const
ArtifactStateMetadata
=
"atlas.artifact.metadata"
type
Config
struct
{
common
.
PackerConfig
`mapstructure:",squash"`
Artifact
string
Type
string
`mapstructure:"artifact_type"`
TypeOverride
bool
`mapstructure:"artifact_type_override"`
Metadata
map
[
string
]
string
ServerAddr
string
`mapstructure:"server_address"`
Token
string
// This shouldn't ever be set outside of unit tests.
Test
bool
`mapstructure:"test"`
tpl
*
packer
.
ConfigTemplate
user
,
name
string
buildId
int
}
type
PostProcessor
struct
{
config
Config
client
*
atlas
.
Client
}
func
(
p
*
PostProcessor
)
Configure
(
raws
...
interface
{})
error
{
_
,
err
:=
common
.
DecodeConfig
(
&
p
.
config
,
raws
...
)
if
err
!=
nil
{
return
err
}
p
.
config
.
tpl
,
err
=
packer
.
NewConfigTemplate
()
if
err
!=
nil
{
return
err
}
p
.
config
.
tpl
.
UserVars
=
p
.
config
.
PackerUserVars
templates
:=
map
[
string
]
*
string
{
"artifact"
:
&
p
.
config
.
Artifact
,
"type"
:
&
p
.
config
.
Type
,
"server_address"
:
&
p
.
config
.
ServerAddr
,
"token"
:
&
p
.
config
.
Token
,
}
errs
:=
new
(
packer
.
MultiError
)
for
key
,
ptr
:=
range
templates
{
*
ptr
,
err
=
p
.
config
.
tpl
.
Process
(
*
ptr
,
nil
)
if
err
!=
nil
{
errs
=
packer
.
MultiErrorAppend
(
errs
,
fmt
.
Errorf
(
"Error processing %s: %s"
,
key
,
err
))
}
}
required
:=
map
[
string
]
*
string
{
"artifact"
:
&
p
.
config
.
Artifact
,
"artifact_type"
:
&
p
.
config
.
Type
,
}
for
key
,
ptr
:=
range
required
{
if
*
ptr
==
""
{
errs
=
packer
.
MultiErrorAppend
(
errs
,
fmt
.
Errorf
(
"%s must be set"
,
key
))
}
}
if
len
(
errs
.
Errors
)
>
0
{
return
errs
}
p
.
config
.
user
,
p
.
config
.
name
,
err
=
atlas
.
ParseSlug
(
p
.
config
.
Artifact
)
if
err
!=
nil
{
return
err
}
// If we have a build ID, save it
if
v
:=
os
.
Getenv
(
BuildEnvKey
);
v
!=
""
{
raw
,
err
:=
strconv
.
ParseInt
(
v
,
0
,
0
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"Error parsing build ID: %s"
,
err
)
}
p
.
config
.
buildId
=
int
(
raw
)
}
// Build the client
p
.
client
=
atlas
.
DefaultClient
()
if
p
.
config
.
ServerAddr
!=
""
{
p
.
client
,
err
=
atlas
.
NewClient
(
p
.
config
.
ServerAddr
)
if
err
!=
nil
{
errs
=
packer
.
MultiErrorAppend
(
errs
,
fmt
.
Errorf
(
"Error initializing client: %s"
,
err
))
return
errs
}
}
if
p
.
config
.
Token
!=
""
{
p
.
client
.
Token
=
p
.
config
.
Token
}
if
!
p
.
config
.
Test
{
// Verify the client
if
err
:=
p
.
client
.
Verify
();
err
!=
nil
{
errs
=
packer
.
MultiErrorAppend
(
errs
,
fmt
.
Errorf
(
"Error initializing client: %s"
,
err
))
return
errs
}
}
return
nil
}
func
(
p
*
PostProcessor
)
PostProcess
(
ui
packer
.
Ui
,
artifact
packer
.
Artifact
)
(
packer
.
Artifact
,
bool
,
error
)
{
if
_
,
err
:=
p
.
client
.
Artifact
(
p
.
config
.
user
,
p
.
config
.
name
);
err
!=
nil
{
if
err
!=
atlas
.
ErrNotFound
{
return
nil
,
false
,
fmt
.
Errorf
(
"Error finding artifact: %s"
,
err
)
}
// Artifact doesn't exist, create it
ui
.
Message
(
fmt
.
Sprintf
(
"Creating artifact: %s"
,
p
.
config
.
Artifact
))
_
,
err
=
p
.
client
.
CreateArtifact
(
p
.
config
.
user
,
p
.
config
.
name
)
if
err
!=
nil
{
return
nil
,
false
,
fmt
.
Errorf
(
"Error creating artifact: %s"
,
err
)
}
}
opts
:=
&
atlas
.
UploadArtifactOpts
{
User
:
p
.
config
.
user
,
Name
:
p
.
config
.
name
,
Type
:
p
.
config
.
Type
,
ID
:
artifact
.
Id
(),
Metadata
:
p
.
metadata
(
artifact
),
BuildId
:
p
.
config
.
buildId
,
}
if
fs
:=
artifact
.
Files
();
len
(
fs
)
>
0
{
var
archiveOpts
archive
.
ArchiveOpts
// We have files. We want to compress/upload them. If we have just
// one file, then we use it as-is. Otherwise, we compress all of
// them into a single file.
var
path
string
if
len
(
fs
)
==
1
{
path
=
fs
[
0
]
}
else
{
path
=
longestCommonPrefix
(
fs
)
if
path
==
""
{
return
nil
,
false
,
fmt
.
Errorf
(
"No common prefix for achiving files: %v"
,
fs
)
}
// Modify the archive options to only include the files
// that are in our file list.
include
:=
make
([]
string
,
0
,
len
(
fs
))
for
i
,
f
:=
range
fs
{
include
[
i
]
=
strings
.
Replace
(
f
,
path
,
""
,
1
)
}
archiveOpts
.
Include
=
include
}
r
,
err
:=
archive
.
CreateArchive
(
path
,
&
archiveOpts
)
if
err
!=
nil
{
return
nil
,
false
,
fmt
.
Errorf
(
"Error archiving artifact: %s"
,
err
)
}
defer
r
.
Close
()
opts
.
File
=
r
opts
.
FileSize
=
r
.
Size
}
ui
.
Message
(
"Uploading artifact version..."
)
var
av
*
atlas
.
ArtifactVersion
doneCh
:=
make
(
chan
struct
{})
errCh
:=
make
(
chan
error
,
1
)
go
func
()
{
var
err
error
av
,
err
=
p
.
client
.
UploadArtifact
(
opts
)
if
err
!=
nil
{
errCh
<-
err
return
}
close
(
doneCh
)
}()
select
{
case
err
:=
<-
errCh
:
return
nil
,
false
,
fmt
.
Errorf
(
"Error uploading: %s"
,
err
)
case
<-
doneCh
:
}
return
&
Artifact
{
Name
:
p
.
config
.
Artifact
,
Type
:
p
.
config
.
Type
,
Version
:
av
.
Version
,
},
true
,
nil
}
func
(
p
*
PostProcessor
)
metadata
(
artifact
packer
.
Artifact
)
map
[
string
]
string
{
var
metadata
map
[
string
]
string
metadataRaw
:=
artifact
.
State
(
ArtifactStateMetadata
)
if
metadataRaw
!=
nil
{
if
err
:=
mapstructure
.
Decode
(
metadataRaw
,
&
metadata
);
err
!=
nil
{
panic
(
err
)
}
}
if
p
.
config
.
Metadata
!=
nil
{
// If we have no extra metadata, just return as-is
if
metadata
==
nil
{
return
p
.
config
.
Metadata
}
// Merge the metadata
for
k
,
v
:=
range
p
.
config
.
Metadata
{
metadata
[
k
]
=
v
}
}
return
metadata
}
func
(
p
*
PostProcessor
)
artifactType
(
artifact
packer
.
Artifact
)
string
{
if
!
p
.
config
.
TypeOverride
{
if
v
:=
artifact
.
State
(
ArtifactStateType
);
v
!=
nil
{
return
v
.
(
string
)
}
}
return
p
.
config
.
Type
}
post-processor/atlas/post-processor_test.go
0 → 100644
View file @
f3c1132f
package
main
import
(
"os"
"reflect"
"testing"
"github.com/mitchellh/packer/packer"
)
func
TestPostProcessorConfigure
(
t
*
testing
.
T
)
{
var
p
PostProcessor
if
err
:=
p
.
Configure
(
validDefaults
());
err
!=
nil
{
t
.
Fatalf
(
"err: %s"
,
err
)
}
if
p
.
client
==
nil
{
t
.
Fatal
(
"should have client"
)
}
if
p
.
client
.
Token
!=
""
{
t
.
Fatal
(
"should not have token"
)
}
}
func
TestPostProcessorConfigure_buildId
(
t
*
testing
.
T
)
{
defer
os
.
Setenv
(
BuildEnvKey
,
os
.
Getenv
(
BuildEnvKey
))
os
.
Setenv
(
BuildEnvKey
,
"5"
)
var
p
PostProcessor
if
err
:=
p
.
Configure
(
validDefaults
());
err
!=
nil
{
t
.
Fatalf
(
"err: %s"
,
err
)
}
if
p
.
config
.
buildId
!=
5
{
t
.
Fatalf
(
"bad: %#v"
,
p
.
config
.
buildId
)
}
}
func
TestPostProcessorMetadata
(
t
*
testing
.
T
)
{
var
p
PostProcessor
if
err
:=
p
.
Configure
(
validDefaults
());
err
!=
nil
{
t
.
Fatalf
(
"err: %s"
,
err
)
}
artifact
:=
new
(
packer
.
MockArtifact
)
metadata
:=
p
.
metadata
(
artifact
)
if
len
(
metadata
)
>
0
{
t
.
Fatalf
(
"bad: %#v"
,
metadata
)
}
}
func
TestPostProcessorMetadata_artifact
(
t
*
testing
.
T
)
{
config
:=
validDefaults
()
config
[
"metadata"
]
=
map
[
string
]
string
{
"foo"
:
"bar"
,
}
var
p
PostProcessor
if
err
:=
p
.
Configure
(
config
);
err
!=
nil
{
t
.
Fatalf
(
"err: %s"
,
err
)
}
artifact
:=
new
(
packer
.
MockArtifact
)
artifact
.
StateValues
=
map
[
string
]
interface
{}{
ArtifactStateMetadata
:
map
[
interface
{}]
interface
{}{
"bar"
:
"baz"
,
},
}
metadata
:=
p
.
metadata
(
artifact
)
expected
:=
map
[
string
]
string
{
"foo"
:
"bar"
,
"bar"
:
"baz"
,
}
if
!
reflect
.
DeepEqual
(
metadata
,
expected
)
{
t
.
Fatalf
(
"bad: %#v"
,
metadata
)
}
}
func
TestPostProcessorMetadata_config
(
t
*
testing
.
T
)
{
config
:=
validDefaults
()
config
[
"metadata"
]
=
map
[
string
]
string
{
"foo"
:
"bar"
,
}
var
p
PostProcessor
if
err
:=
p
.
Configure
(
config
);
err
!=
nil
{
t
.
Fatalf
(
"err: %s"
,
err
)
}
artifact
:=
new
(
packer
.
MockArtifact
)
metadata
:=
p
.
metadata
(
artifact
)
expected
:=
map
[
string
]
string
{
"foo"
:
"bar"
,
}
if
!
reflect
.
DeepEqual
(
metadata
,
expected
)
{
t
.
Fatalf
(
"bad: %#v"
,
metadata
)
}
}
func
TestPostProcessorType
(
t
*
testing
.
T
)
{
var
p
PostProcessor
if
err
:=
p
.
Configure
(
validDefaults
());
err
!=
nil
{
t
.
Fatalf
(
"err: %s"
,
err
)
}
artifact
:=
new
(
packer
.
MockArtifact
)
actual
:=
p
.
artifactType
(
artifact
)
if
actual
!=
"foo"
{
t
.
Fatalf
(
"bad: %#v"
,
actual
)
}
}
func
TestPostProcessorType_artifact
(
t
*
testing
.
T
)
{
var
p
PostProcessor
if
err
:=
p
.
Configure
(
validDefaults
());
err
!=
nil
{
t
.
Fatalf
(
"err: %s"
,
err
)
}
artifact
:=
new
(
packer
.
MockArtifact
)
artifact
.
StateValues
=
map
[
string
]
interface
{}{
ArtifactStateType
:
"bar"
,
}
actual
:=
p
.
artifactType
(
artifact
)
if
actual
!=
"bar"
{
t
.
Fatalf
(
"bad: %#v"
,
actual
)
}
}
func
validDefaults
()
map
[
string
]
interface
{}
{
return
map
[
string
]
interface
{}{
"artifact"
:
"mitchellh/test"
,
"artifact_type"
:
"foo"
,
"test"
:
true
,
}
}
post-processor/atlas/util.go
0 → 100644
View file @
f3c1132f
package
main
import
(
"math"
"strings"
)
// longestCommonPrefix finds the longest common prefix for all the strings
// given as an argument, or returns the empty string if a prefix can't be
// found.
//
// This function just uses brute force instead of a more optimized algorithm.
func
longestCommonPrefix
(
vs
[]
string
)
string
{
// Find the shortest string
var
shortest
string
length
:=
math
.
MaxUint32
for
_
,
v
:=
range
vs
{
if
len
(
v
)
<
length
{
shortest
=
v
length
=
len
(
v
)
}
}
// Now go through and find a prefix to all the strings using this
// short string, which itself must contain the prefix.
for
i
:=
len
(
shortest
);
i
>
0
;
i
--
{
// We only care about prefixes with path seps
if
shortest
[
i
-
1
]
!=
'/'
{
continue
}
bad
:=
false
prefix
:=
shortest
[
0
:
i
]
for
_
,
v
:=
range
vs
{
if
!
strings
.
HasPrefix
(
v
,
prefix
)
{
bad
=
true
break
}
}
if
!
bad
{
return
prefix
}
}
return
""
}
post-processor/atlas/util_test.go
0 → 100644
View file @
f3c1132f
package
main
import
(
"testing"
)
func
TestLongestCommonPrefix
(
t
*
testing
.
T
)
{
cases
:=
[]
struct
{
Input
[]
string
Output
string
}{
{
[]
string
{
"foo"
,
"bar"
},
""
,
},
{
[]
string
{
"foo"
,
"foobar"
},
""
,
},
{
[]
string
{
"foo/"
,
"foo/bar"
},
"foo/"
,
},
{
[]
string
{
"/foo/"
,
"/bar"
},
"/"
,
},
}
for
_
,
tc
:=
range
cases
{
actual
:=
longestCommonPrefix
(
tc
.
Input
)
if
actual
!=
tc
.
Output
{
t
.
Fatalf
(
"bad: %#v
\n\n
%#v"
,
actual
,
tc
.
Input
)
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment