Add support for RPM packages

This was thankfully made easy by NFPM!

Refactored the DEB packaging so that the code is almost entirely shared
between the two.
This commit is contained in:
Zach Wasserman
2021-03-02 17:19:24 -08:00
parent b1b3f97ada
commit fc6e6afd34
5 changed files with 250 additions and 231 deletions
+3 -1
View File
@@ -107,8 +107,10 @@ func main() {
return packaging.BuildPkg(opt)
case "deb":
return packaging.BuildDeb(opt)
case "rpm":
return packaging.BuildRPM(opt)
default:
return errors.New("type must be one of ('pkg', 'deb')")
return errors.New("type must be one of ('pkg', 'deb', 'rpm')")
}
}
+5
View File
@@ -67,6 +67,7 @@ github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
github.com/bombsimon/wsl/v3 v3.1.0 h1:E5SRssoBgtVFPcYWUOFJEcgaySgdtTNYzsSKDOY7ss8=
github.com/bombsimon/wsl/v3 v3.1.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc=
github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e h1:hHg27A0RSSp2Om9lubZpiMgVbvn39bsUmW9U5h0twqc=
github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oDpT4efm8tSYHXV5tHSdRvBet/b/QzxZ+XyyPehvm3A=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
@@ -229,6 +230,7 @@ github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OI
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/rpmpack v0.0.0-20201206194719-59e495f2b7e1/go.mod h1:+y9lKiqDhR4zkLl+V9h4q0rdyrYVsWWm6LLCQP33DIk=
github.com/google/rpmpack v0.0.0-20201225075926-0a97c2c4b688 h1:jyGRCFMyDK/gKe6QRZQWci5+wEUBFElvxLHs3iwO3hY=
github.com/google/rpmpack v0.0.0-20201225075926-0a97c2c4b688/go.mod h1:+y9lKiqDhR4zkLl+V9h4q0rdyrYVsWWm6LLCQP33DIk=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
@@ -412,6 +414,7 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
@@ -465,6 +468,7 @@ github.com/ryancurrah/gomodguard v1.2.0/go.mod h1:rNqbC4TOIdUDcVMSIpNNAzTbzXAZa6
github.com/ryanrolds/sqlclosecheck v0.3.0 h1:AZx+Bixh8zdUBxUA1NxbxVAS78vTPq4rCb8OUZI9xFw=
github.com/ryanrolds/sqlclosecheck v0.3.0/go.mod h1:1gREqxyTGR3lVtpngyFo3hZAgk0KCtEdgEkHwDbigdA=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sassoftware/go-rpmutils v0.0.0-20190420191620-a8f1baeba37b h1:+gCnWOZV8Z/8jehJ2CdqB47Z3S+SREmQcuXkRFLNsiI=
github.com/sassoftware/go-rpmutils v0.0.0-20190420191620-a8f1baeba37b/go.mod h1:am+Fp8Bt506lA3Rk3QCmSqmYmLMnPDhdDUcosQCAx+I=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4=
@@ -558,6 +562,7 @@ github.com/tommy-muehle/go-mnd v1.3.1-0.20201008215730-16041ac3fe65 h1:Y0bLA422k
github.com/tommy-muehle/go-mnd v1.3.1-0.20201008215730-16041ac3fe65/go.mod h1:T22e7iRN4LsFPZGyRLRXeF+DWVXFuV9thsyO7NjbbTI=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.9 h1:RsKRIA2MO8x56wkkcd3LbtcE/uMszhb6DpRf+3uwa3I=
github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ultraware/funlen v0.0.3 h1:5ylVWm8wsNwH5aWo9438pwvsK0QiqVuUrt9bn7S/iLA=
github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
+2 -230
View File
@@ -1,235 +1,7 @@
package packaging
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"text/template"
"github.com/fleetdm/orbit/pkg/constant"
"github.com/fleetdm/orbit/pkg/update"
"github.com/goreleaser/nfpm/v2"
"github.com/goreleaser/nfpm/v2/deb"
"github.com/goreleaser/nfpm/v2/files"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
)
import "github.com/goreleaser/nfpm/v2/deb"
func BuildDeb(opt Options) error {
// Initialize directories
tmpDir, err := ioutil.TempDir("", "orbit-package")
if err != nil {
return errors.Wrap(err, "failed to create temp dir")
}
defer os.RemoveAll(tmpDir)
log.Debug().Str("path", tmpDir).Msg("created temp dir")
filesystemRoot := filepath.Join(tmpDir, "root")
if err := os.MkdirAll(filesystemRoot, constant.DefaultDirMode); err != nil {
return errors.Wrap(err, "create root dir")
}
orbitRoot := filepath.Join(filesystemRoot, "var", "lib", "orbit")
if err := os.MkdirAll(orbitRoot, constant.DefaultDirMode); err != nil {
return errors.Wrap(err, "create orbit dir")
}
// Initialize autoupdate metadata
updateOpt := update.DefaultOptions
updateOpt.Platform = "linux"
updateOpt.RootDirectory = orbitRoot
updateOpt.OrbitChannel = opt.OrbitChannel
updateOpt.OsqueryChannel = opt.OsqueryChannel
// TODO these should be configurable
updateOpt.ServerURL = "https://tuf.fleetctl.com"
if err := initializeUpdates(updateOpt); err != nil {
return errors.Wrap(err, "initialize updates")
}
// Write files
if err := writeSystemdUnit(opt, filesystemRoot); err != nil {
return errors.Wrap(err, "write systemd unit")
}
if err := writeEnvFile(opt, filesystemRoot); err != nil {
return errors.Wrap(err, "write env file")
}
postInstallPath := filepath.Join(tmpDir, "postinstall.sh")
if err := writePostInstall(opt, postInstallPath); err != nil {
return errors.Wrap(err, "write postinstall script")
}
// Pick up all file contents
contents := files.Contents{
&files.Content{
Source: filepath.Join(filesystemRoot, "**"),
Destination: "/",
},
&files.Content{
Source: "orbit",
Destination: "/var/lib/orbit/orbit",
FileInfo: &files.ContentFileInfo{
Mode: constant.DefaultExecutableMode,
},
},
// Symlink current into /var/lib/orbit/bin/orbit/orbit
&files.Content{
Source: "/var/lib/orbit/bin/orbit/linux/" + opt.OrbitChannel + "/orbit",
Destination: "/var/lib/orbit/bin/orbit/orbit",
Type: "symlink",
FileInfo: &files.ContentFileInfo{
Mode: constant.DefaultExecutableMode | os.ModeSymlink,
},
},
// Symlink current into /usr/local/bin
&files.Content{
Source: "/var/lib/orbit/bin/orbit/orbit",
Destination: "/usr/local/bin/orbit",
Type: "symlink",
FileInfo: &files.ContentFileInfo{
Mode: constant.DefaultExecutableMode | os.ModeSymlink,
},
},
}
contents, err = files.ExpandContentGlobs(contents, false)
if err != nil {
return errors.Wrap(err, "glob contents")
}
for _, c := range contents {
log.Debug().Interface("file", c).Msg("added file")
}
// Build package
info := &nfpm.Info{
Name: "orbit-osquery",
Version: opt.Version,
Description: "Orbit osquery -- runtime and autoupdater by Fleet",
Arch: "amd64",
Maintainer: "Fleet Engineers <engineering@fleetdm.com>",
Homepage: "https://github.com/fleetdm/orbit",
Overridables: nfpm.Overridables{
Contents: contents,
EmptyFolders: []string{
"/var/log/osquery",
"/var/log/orbit",
},
Scripts: nfpm.Scripts{
PostInstall: postInstallPath,
},
},
}
pkger := deb.Default
filename := pkger.ConventionalFileName(info)
out, err := os.OpenFile(filename, os.O_CREATE|os.O_RDWR, constant.DefaultFileMode)
if err != nil {
return errors.Wrap(err, "open output file")
}
defer out.Close()
if err := deb.Default.Package(info, out); err != nil {
return errors.Wrap(err, "write deb package")
}
log.Info().Str("path", filename).Msg("wrote deb package")
return nil
}
func writeSystemdUnit(opt Options, rootPath string) error {
systemdRoot := filepath.Join(rootPath, "usr", "lib", "systemd", "system")
if err := os.MkdirAll(systemdRoot, constant.DefaultDirMode); err != nil {
return errors.Wrap(err, "create systemd dir")
}
if err := ioutil.WriteFile(
filepath.Join(systemdRoot, "orbit.service"),
[]byte(`
[Unit]
Description=Fleet Orbit osquery
After=network.service syslog.service
StartLimitIntervalSec=0
[Service]
TimeoutStartSec=0
EnvironmentFile=/etc/default/orbit
ExecStart=/var/lib/orbit/bin/orbit/orbit
Restart=always
RestartSec=1
KillMode=control-group
KillSignal=SIGTERM
CPUQuota=20%
[Install]
WantedBy=multi-user.target
`),
constant.DefaultFileMode,
); err != nil {
return errors.Wrap(err, "write file")
}
return nil
}
var envTemplate = template.Must(template.New("env").Parse(`
{{- if .Insecure }}ORBIT_INSECURE=true{{ end }}
{{ if .FleetURL }}ORBIT_FLEET_URL={{.FleetURL}}{{ end }}
{{ if .FleetCertificate }}ORBIT_FLEET_CERTIFICATE=/var/lib/orbit/fleet.pem{{ end }}
{{ if .EnrollSecret }}ORBIT_ENROLL_SECRET={{.EnrollSecret}}{{ end }}
`))
func writeEnvFile(opt Options, rootPath string) error {
envRoot := filepath.Join(rootPath, "etc", "default")
if err := os.MkdirAll(envRoot, constant.DefaultDirMode); err != nil {
return errors.Wrap(err, "create env dir")
}
var contents bytes.Buffer
if err := envTemplate.Execute(&contents, opt); err != nil {
return errors.Wrap(err, "execute template")
}
if err := ioutil.WriteFile(
filepath.Join(envRoot, "orbit"),
contents.Bytes(),
constant.DefaultFileMode,
); err != nil {
return errors.Wrap(err, "write file")
}
return nil
}
var postInstallTemplate = template.Must(template.New("postinstall").Parse(`
#!/bin/sh
# Exit on error
set -e
# If we have a systemd, daemon-reload away now
if [ -x /bin/systemctl ] && pidof systemd ; then
/bin/systemctl daemon-reload 2>/dev/null 2>&1
{{ if .StartService -}}
/bin/systemctl restart orbit.service 2>&1
{{- end}}
fi
`))
func writePostInstall(opt Options, path string) error {
var contents bytes.Buffer
if err := postInstallTemplate.Execute(&contents, opt); err != nil {
return errors.Wrap(err, "execute template")
}
if err := ioutil.WriteFile(path, contents.Bytes(), constant.DefaultFileMode); err != nil {
return errors.Wrap(err, "write file")
}
return nil
return buildNFPM(opt, deb.Default)
}
+233
View File
@@ -0,0 +1,233 @@
package packaging
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"text/template"
"github.com/fleetdm/orbit/pkg/constant"
"github.com/fleetdm/orbit/pkg/update"
"github.com/goreleaser/nfpm/v2"
"github.com/goreleaser/nfpm/v2/files"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
)
func buildNFPM(opt Options, pkger nfpm.Packager) error {
// Initialize directories
tmpDir, err := ioutil.TempDir("", "orbit-package")
if err != nil {
return errors.Wrap(err, "failed to create temp dir")
}
defer os.RemoveAll(tmpDir)
log.Debug().Str("path", tmpDir).Msg("created temp dir")
filesystemRoot := filepath.Join(tmpDir, "root")
if err := os.MkdirAll(filesystemRoot, constant.DefaultDirMode); err != nil {
return errors.Wrap(err, "create root dir")
}
orbitRoot := filepath.Join(filesystemRoot, "var", "lib", "orbit")
if err := os.MkdirAll(orbitRoot, constant.DefaultDirMode); err != nil {
return errors.Wrap(err, "create orbit dir")
}
// Initialize autoupdate metadata
updateOpt := update.DefaultOptions
updateOpt.Platform = "linux"
updateOpt.RootDirectory = orbitRoot
updateOpt.OrbitChannel = opt.OrbitChannel
updateOpt.OsqueryChannel = opt.OsqueryChannel
// TODO these should be configurable
updateOpt.ServerURL = "https://tuf.fleetctl.com"
if err := initializeUpdates(updateOpt); err != nil {
return errors.Wrap(err, "initialize updates")
}
// Write files
if err := writeSystemdUnit(opt, filesystemRoot); err != nil {
return errors.Wrap(err, "write systemd unit")
}
if err := writeEnvFile(opt, filesystemRoot); err != nil {
return errors.Wrap(err, "write env file")
}
postInstallPath := filepath.Join(tmpDir, "postinstall.sh")
if err := writePostInstall(opt, postInstallPath); err != nil {
return errors.Wrap(err, "write postinstall script")
}
// Pick up all file contents
contents := files.Contents{
&files.Content{
Source: filepath.Join(filesystemRoot, "**"),
Destination: "/",
},
&files.Content{
Source: "orbit",
Destination: "/var/lib/orbit/orbit",
FileInfo: &files.ContentFileInfo{
Mode: constant.DefaultExecutableMode,
},
},
// Symlink current into /var/lib/orbit/bin/orbit/orbit
&files.Content{
Source: "/var/lib/orbit/bin/orbit/linux/" + opt.OrbitChannel + "/orbit",
Destination: "/var/lib/orbit/bin/orbit/orbit",
Type: "symlink",
FileInfo: &files.ContentFileInfo{
Mode: constant.DefaultExecutableMode | os.ModeSymlink,
},
},
// Symlink current into /usr/local/bin
&files.Content{
Source: "/var/lib/orbit/bin/orbit/orbit",
Destination: "/usr/local/bin/orbit",
Type: "symlink",
FileInfo: &files.ContentFileInfo{
Mode: constant.DefaultExecutableMode | os.ModeSymlink,
},
},
}
contents, err = files.ExpandContentGlobs(contents, false)
if err != nil {
return errors.Wrap(err, "glob contents")
}
for _, c := range contents {
log.Debug().Interface("file", c).Msg("added file")
}
// Build package
info := &nfpm.Info{
Name: "orbit-osquery",
Version: opt.Version,
Description: "Orbit osquery -- runtime and autoupdater by Fleet",
Arch: "amd64",
Maintainer: "Fleet Engineers <engineering@fleetdm.com>",
Homepage: "https://github.com/fleetdm/orbit",
Overridables: nfpm.Overridables{
Contents: contents,
EmptyFolders: []string{
"/var/log/osquery",
"/var/log/orbit",
},
Scripts: nfpm.Scripts{
PostInstall: postInstallPath,
},
},
}
filename := pkger.ConventionalFileName(info)
out, err := os.OpenFile(filename, os.O_CREATE|os.O_RDWR, constant.DefaultFileMode)
if err != nil {
return errors.Wrap(err, "open output file")
}
defer out.Close()
if err := pkger.Package(info, out); err != nil {
return errors.Wrap(err, "write package")
}
log.Info().Str("path", filename).Msg("wrote package")
return nil
}
func writeSystemdUnit(opt Options, rootPath string) error {
systemdRoot := filepath.Join(rootPath, "usr", "lib", "systemd", "system")
if err := os.MkdirAll(systemdRoot, constant.DefaultDirMode); err != nil {
return errors.Wrap(err, "create systemd dir")
}
if err := ioutil.WriteFile(
filepath.Join(systemdRoot, "orbit.service"),
[]byte(`
[Unit]
Description=Orbit osquery
After=network.service syslog.service
StartLimitIntervalSec=0
[Service]
TimeoutStartSec=0
EnvironmentFile=/etc/default/orbit
ExecStart=/var/lib/orbit/bin/orbit/orbit
Restart=always
RestartSec=1
KillMode=control-group
KillSignal=SIGTERM
CPUQuota=20%
[Install]
WantedBy=multi-user.target
`),
constant.DefaultFileMode,
); err != nil {
return errors.Wrap(err, "write file")
}
return nil
}
var envTemplate = template.Must(template.New("env").Parse(`
{{- if .Insecure }}ORBIT_INSECURE=true{{ end }}
{{ if .FleetURL }}ORBIT_FLEET_URL={{.FleetURL}}{{ end }}
{{ if .FleetCertificate }}ORBIT_FLEET_CERTIFICATE=/var/lib/orbit/fleet.pem{{ end }}
{{ if .EnrollSecret }}ORBIT_ENROLL_SECRET={{.EnrollSecret}}{{ end }}
`))
func writeEnvFile(opt Options, rootPath string) error {
envRoot := filepath.Join(rootPath, "etc", "default")
if err := os.MkdirAll(envRoot, constant.DefaultDirMode); err != nil {
return errors.Wrap(err, "create env dir")
}
var contents bytes.Buffer
if err := envTemplate.Execute(&contents, opt); err != nil {
return errors.Wrap(err, "execute template")
}
if err := ioutil.WriteFile(
filepath.Join(envRoot, "orbit"),
contents.Bytes(),
constant.DefaultFileMode,
); err != nil {
return errors.Wrap(err, "write file")
}
return nil
}
var postInstallTemplate = template.Must(template.New("postinstall").Parse(`
#!/bin/sh
# Exit on error
set -e
# If we have a systemd, daemon-reload away now
if [ -x /bin/systemctl ] && pidof systemd ; then
/bin/systemctl daemon-reload 2>/dev/null 2>&1
{{ if .StartService -}}
/bin/systemctl restart orbit.service 2>&1
{{- end}}
fi
`))
func writePostInstall(opt Options, path string) error {
var contents bytes.Buffer
if err := postInstallTemplate.Execute(&contents, opt); err != nil {
return errors.Wrap(err, "execute template")
}
if err := ioutil.WriteFile(path, contents.Bytes(), constant.DefaultFileMode); err != nil {
return errors.Wrap(err, "write file")
}
return nil
}
+7
View File
@@ -0,0 +1,7 @@
package packaging
import "github.com/goreleaser/nfpm/v2/rpm"
func BuildRPM(opt Options) error {
return buildNFPM(opt, rpm.Default)
}