Skip to content

Commit

Permalink
Refactor Beat packaging and cross-building
Browse files Browse the repository at this point in the history
This refactors Beat packaging to use a declarative YAML based packaging
specification. This makes it easier to customize the contents of packages (e.g.
adding additional X-Pack content or simply tailoring what's included for single
Beat).

The specification itself is pretty simple. It consists of package metadata and a
list of files to include. The values can be templated which allows for a single
package spec to be reused across Beats. Here's an example spec for an OSS
Windows zip package.

```
spec:
  name:         '{{.BeatName}}-oss'
  service_name: '{{.BeatServiceName}}'
  os:           '{{.GOOS}}'
  arch:         '{{.PackageArch}}'
  vendor:       '{{.BeatVendor}}'
  version:      '{{ beat_version }}'
  license:      ASL 2.0
  url:          '{{.BeatURL}}'
  description: '{{.BeatDescription}}'
  files:
    '{{.BeatName}}{{.BinaryExt}}':
      source: build/golang-crossbuild/{{.BeatName}}-{{.GOOS}}-{{.Platform.Arch}}{{.BinaryExt}}
      mode:   0755
    fields.yml:
      source: fields.yml
      mode:   0644
    LICENSE.txt:
      source: '{{ repo.RootDir }}/licenses/APACHE-LICENSE-2.0.txt'
      mode: 0644
    NOTICE.txt:
      source: '{{ repo.RootDir }}/NOTICE.txt'
      mode:   0644
    README.md:
      template: '{{ elastic_beats_dir }}/dev-tools/mage/templates/common/README.md.tmpl'
      mode:   0644
    .build_hash.txt:
      content: >
        {{ commit }}
      mode:   0644
    '{{.BeatName}}.reference.yml':
      source: '{{.BeatName}}.reference.yml'
      mode:   0644
    '{{.BeatName}}.yml':
      source: '{{.BeatName}}.yml'
      mode: 0600
      config: true
    kibana:
      source: _meta/kibana.generated
      mode:   0644
    install-service-{{.BeatName}}.ps1:
      template: '{{ elastic_beats_dir }}/dev-tools/mage/templates/windows/install-service.ps1.tmpl'
      mode: 0755
    uninstall-service-{{.BeatName}}.ps1:
      template: '{{ elastic_beats_dir }}/dev-tools/mage/templates/windows/uninstall-service.ps1.tmpl'
      mode: 0755
```

Each Beat has two build targets for packaging.

- `make snapshot`
- `make release`

The set of target platforms can be influenced by the `PLATFORMS` environment
variable which accepts an platform selection expression. For example to add
ARMv7 (for your Raspberry Pi) to the default set of platforms you would use
`PLATFORMS='+linux/armv7' make snapshot`. Or to only build for Windows set
`PLATFORMS='windows'`. Full details can be found in the godocs for
`NewPlatformList`.

For the release manager there are two new top-level targets that take care of
ensuring that the proper Go version is used. The naming here aligns with what
several of the other projects are using for their release manager targets.

- `make release-manager-snapshot`
- `make release-manager-release`

Build Process Details
----

Below is the command line output of building and packaging Packetbeat for
linux/armv7 only. I'll describe each step in the build process.

```
$ PLATFORMS='+all linux/armv7' make snapshot
Installing mage from vendor
>> golangCrossBuild: Building for linux/armv7
>> buildGoDaemon: Building for linux/armv7
>> Building using: cmd='build/mage-linux-amd64 golangCrossBuild', env=[CC=arm-linux-gnueabihf-gcc, CXX=arm-linux-gnueabihf-g++, GOARCH=arm, GOARM=7, GOOS=linux, PLATFORM_ID=linux-armv7]
>> Building using: cmd='build/mage-linux-amd64 buildGoDaemon', env=[CC=arm-linux-gnueabihf-gcc, CXX=arm-linux-gnueabihf-g++, GOARCH=arm, GOARM=7, GOOS=linux, PLATFORM_ID=linux-armv7]
grammar.y: warning: 38 shift/reduce conflicts [-Wconflicts-sr]
>> package: Building packetbeat type=tar.gz for platform=linux/armv7
>> package: Building packetbeat-oss type=deb for platform=linux/armv7
>> package: Building packetbeat type=rpm for platform=linux/armv7
>> package: Building packetbeat type=deb for platform=linux/armv7
>> package: Building packetbeat-oss type=tar.gz for platform=linux/armv7
>> package: Building packetbeat-oss type=rpm for platform=linux/armv7
>> Testing package contents
package ran for 1m6.597416206s

$ tree build/distributions/
build/distributions/
├── packetbeat-oss-7.0.0-alpha1-SNAPSHOT-armhf.deb
├── packetbeat-oss-7.0.0-alpha1-SNAPSHOT-armhf.deb.sha512
├── packetbeat-oss-7.0.0-alpha1-SNAPSHOT-armhfp.rpm
├── packetbeat-oss-7.0.0-alpha1-SNAPSHOT-armhfp.rpm.sha512
├── packetbeat-oss-7.0.0-alpha1-SNAPSHOT-linux-armv7.tar.gz
└── packetbeat-oss-7.0.0-alpha1-SNAPSHOT-linux-armv7.tar.gz.sha512
```

1. Install [mage](https://magefile.org/) from the vendor directory to `$GOPATH/bin`.

   Mage is used to provide a simple gmake like wrapper around build logic that's
   written in Go. Additionally it makes is possible to build and package without
   gmake by invoking mage directly [think Windows users]. For example:

   ```
   $ mage -l
    Targets:
      build                 builds the Beat binary.
      buildGoDaemon         builds the go-daemon binary (use crossBuildGoDaemon).
      clean                 cleans all generated files and build artifacts.
      crossBuild            cross-builds the beat for all target platforms.
      crossBuildGoDaemon    cross-builds the go-daemon binary using Docker.
      golangCrossBuild      build the Beat binary inside of the golang-builder.
      package               packages the Beat for distribution.
      testPackages          tests the generated packages (i.e.
      update                updates the generated files (aka make update).
  ```

2. Cross-build Packetbeat for linux/armv7. Cross-building requires Docker and
   uses images hosted at `docker.elastic.co/beats-dev/golang-crossbuild`. The
   repo for these images is https://github.com/elastic/golang-crossbuild. These
   images give us wider platform support for cross-building than we had.

   `mage golangCrossBuild` is invoked inside of the container. This handles
   cross-compiling libpcap and then invoking `go build` with the proper args.

3. Cross-build go-daemon for linux/armv7. This is actually done in parallel with
   the other cross-builds. GOMAXPROCS determines the number of concurrent jobs.

   `mage buildGoDaemon` is invoked inside of the container.

4. After all cross-builds complete, packaging begins. The package types are
   decided based on the package specs that are registered for each OS.

   Zip and tar.gz files are built natively with Go. RPM and deb packages are
   first generated as tar.gz where we have full control over the target file
   names, ownership, and modes regardless of the underlying filesystem [think
   Windows]. Then FPM is invoked inside of Docker to translate the tar.gz file
   into a proper RPM or deb.

5. SHA512 side-car files are generated for each package. Go is used for this so
   no special command line tools are needed.

6. The generated packages are inspected with `dev-tools/package_test.go`. This
   looks at the contents of the packages to ensure the files have the expected
   ownership and modes (e.g. the config file should have 0600).

Changes

- Add Boot2Docker workaround for shared volume permissions

- Mirror the libpcap source to S3

- Use MAX_PARALLEL to control the number of parallel jobs. The default value is
  the lesser of the NumCPU and NCPU from the Docker daemon.

- Add jenkins_package.sh for Jenkins.
  • Loading branch information
andrewkroh committed Jun 29, 2018
1 parent fe17304 commit 23c4bc0
Show file tree
Hide file tree
Showing 59 changed files with 5,602 additions and 358 deletions.
1 change: 1 addition & 0 deletions CHANGELOG-developer.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ The list below covers the major changes between 6.3.0 and master only.
- Port fields.yml collector to Golang {pull}6911[6911]
- Dashboards under _meta/kibana are expected to be decoded. See https://github.com/elastic/beats/pull/7224 for a conversion script. {pull}7265[7265]
- Constructor `(github.com/elastic/beats/libbeat/output/codec/json).New` expects a new `escapeHTML` parameter. {pull}7445[7445]
- Packaging has been refactored and updates are required. See the PR for migration details. {pull}7388[7388]

==== Bugfixes

Expand Down
78 changes: 37 additions & 41 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
BUILD_DIR=$(CURDIR)/build
COVERAGE_DIR=$(BUILD_DIR)/coverage
BEATS=packetbeat filebeat winlogbeat metricbeat heartbeat auditbeat
BEATS?=auditbeat filebeat heartbeat metricbeat packetbeat winlogbeat
PROJECTS=libbeat $(BEATS)
PROJECTS_ENV=libbeat filebeat metricbeat
SNAPSHOT?=yes
PYTHON_ENV?=$(BUILD_DIR)/python-env
VIRTUALENV_PARAMS?=
FIND=find . -type f -not -path "*/vendor/*" -not -path "*/build/*" -not -path "*/.git/*"
Expand Down Expand Up @@ -62,6 +61,7 @@ clean:
@rm -rf build
@$(foreach var,$(PROJECTS),$(MAKE) -C $(var) clean || exit 1;)
@$(MAKE) -C generator clean
@-mage -clean 2> /dev/null

# Cleans up the vendor directory from unnecessary files
# This should always be run after updating the dependencies
Expand Down Expand Up @@ -109,51 +109,12 @@ lint:
@go get $(GOLINT_REPO) $(REVIEWDOG_REPO)
$(REVIEWDOG) $(REVIEWDOG_OPTIONS)

# Collects all dashboards and generates dashboard folder for https://github.com/elastic/beats-dashboards/tree/master/dashboards
.PHONY: beats-dashboards
beats-dashboards:
@mkdir -p build/dashboards
@$(foreach var,$(BEATS),cp -r $(var)/_meta/kibana.generated/ build/dashboards/$(var) || exit 1;)

# Builds the documents for each beat
.PHONY: docs
docs:
@$(foreach var,$(PROJECTS),BUILD_DIR=${BUILD_DIR} $(MAKE) -C $(var) docs || exit 1;)
sh ./script/build_docs.sh dev-guide github.com/elastic/beats/docs/devguide ${BUILD_DIR}

.PHONY: package-all
package-all: update beats-dashboards
@$(foreach var,$(BEATS),SNAPSHOT=$(SNAPSHOT) $(MAKE) -C $(var) package-all || exit 1;)

@echo "Start building the dashboards package"
@mkdir -p build/upload/
@BUILD_DIR=${BUILD_DIR} UPLOAD_DIR=${BUILD_DIR}/upload SNAPSHOT=$(SNAPSHOT) $(MAKE) -C dev-tools/packer package-dashboards ${BUILD_DIR}/upload/build_id.txt
@mv build/upload build/dashboards-upload

@# Copy build files over to top build directory
@mkdir -p build/upload/
@$(foreach var,$(BEATS),cp -r $(var)/build/upload/ build/upload/$(var) || exit 1;)
@cp -r build/dashboards-upload build/upload/dashboards
@# Run tests on the generated packages.
@go test ./dev-tools/package_test.go -files "${BUILD_DIR}/upload/*/*"

# Upload nightly builds to S3
.PHONY: upload-nightlies-s3
upload-nightlies-s3: all
aws s3 cp --recursive --acl public-read build/upload s3://beats-nightlies

# Run after building to sign packages and publish to APT and YUM repos.
.PHONY: package-upload
upload-package:
$(MAKE) -C dev-tools/packer deb-rpm-s3
# You must export AWS_ACCESS_KEY=<AWS access> and export AWS_SECRET_KEY=<secret>
# before running this make target.
dev-tools/packer/docker/deb-rpm-s3/deb-rpm-s3.sh

.PHONY: release-upload
upload-release:
aws s3 cp --recursive --acl public-read build/upload s3://download.elasticsearch.org/beats/

.PHONY: notice
notice: python-env
@echo "Generating NOTICE"
Expand All @@ -171,3 +132,38 @@ python-env:
.PHONY: test-apm
test-apm:
sh ./script/test_apm.sh

### Packaging targets ####

# Builds a snapshot release.
.PHONY: snapshot
snapshot:
@$(MAKE) SNAPSHOT=true release

# Builds a release.
.PHONY: release
release: beats-dashboards
@$(foreach var,$(BEATS),$(MAKE) -C $(var) release || exit 1;)
@$(foreach var,$(BEATS),mkdir -p build/distributions/$(var) && mv -f $(var)/build/distributions/* build/distributions/$(var)/ || exit 1;)

# Builds a snapshot release. The Go version defined in .go-version will be
# installed and used for the build.
.PHONY: release-manager-snapshot
release-manager-snapshot:
./dev-tools/run_with_go_ver $(MAKE) snapshot

# Builds a snapshot release. The Go version defined in .go-version will be
# installed and used for the build.
.PHONY: release-manager-release
release-manager-release:
./dev-tools/run_with_go_ver $(MAKE) release

# Installs the mage build tool from the vendor directory.
.PHONY: mage
mage:
@go install github.com/elastic/beats/vendor/github.com/magefile/mage

# Collects dashboards from all Beats and generates a zip file distribution.
.PHONY: beats-dashboards
beats-dashboards: mage update
@mage packageBeatDashboards
50 changes: 3 additions & 47 deletions auditbeat/Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
BEAT_NAME=auditbeat
BEAT_TITLE=Auditbeat
BEAT_DESCRIPTION=Audit the activities of users and processes on your system.
SYSTEM_TESTS=true
TEST_ENVIRONMENT?=true
GOX_OS?=linux windows ## @Building List of all OS to be supported by "make crosscompile".
Expand All @@ -12,49 +11,6 @@ FIELDS_FILE_PATH=module
# Path to the libbeat Makefile
include ${ES_BEATS}/libbeat/scripts/Makefile

# This is called by the beats packer before building starts
.PHONY: before-build
before-build:
@cat ${ES_BEATS}/auditbeat/_meta/common.p1.yml \
<(go run scripts/generate_config.go -os windows -concat) \
${ES_BEATS}/auditbeat/_meta/common.p2.yml \
${ES_BEATS}/libbeat/_meta/config.yml > \
${PREFIX}/${BEAT_NAME}-win.yml
@cat ${ES_BEATS}/auditbeat/_meta/common.reference.yml \
<(go run scripts/generate_config.go -os windows -concat -ref) \
${ES_BEATS}/libbeat/_meta/config.reference.yml > \
${PREFIX}/${BEAT_NAME}-win.reference.yml

@cat ${ES_BEATS}/auditbeat/_meta/common.p1.yml \
<(go run scripts/generate_config.go -os darwin -concat) \
${ES_BEATS}/auditbeat/_meta/common.p2.yml \
${ES_BEATS}/libbeat/_meta/config.yml > \
${PREFIX}/${BEAT_NAME}-darwin.yml
@cat ${ES_BEATS}/auditbeat/_meta/common.reference.yml \
<(go run scripts/generate_config.go -os darwin -concat -ref) \
${ES_BEATS}/libbeat/_meta/config.reference.yml > \
${PREFIX}/${BEAT_NAME}-darwin.reference.yml

@cat ${ES_BEATS}/auditbeat/_meta/common.p1.yml \
<(go run scripts/generate_config.go -os linux -arch amd64 -concat) \
${ES_BEATS}/auditbeat/_meta/common.p2.yml \
${ES_BEATS}/libbeat/_meta/config.yml > \
${PREFIX}/${BEAT_NAME}-linux.yml
@cat ${ES_BEATS}/auditbeat/_meta/common.reference.yml \
<(go run scripts/generate_config.go -os linux -concat -ref) \
${ES_BEATS}/libbeat/_meta/config.reference.yml > \
${PREFIX}/${BEAT_NAME}-linux.reference.yml

@cat ${ES_BEATS}/auditbeat/_meta/common.p1.yml \
<(go run scripts/generate_config.go -os linux -arch i386 -concat) \
${ES_BEATS}/auditbeat/_meta/common.p2.yml \
${ES_BEATS}/libbeat/_meta/config.yml > \
${PREFIX}/${BEAT_NAME}-linux-386.yml
@cat ${ES_BEATS}/auditbeat/_meta/common.reference.yml \
<(go run scripts/generate_config.go -os linux -concat -ref) \
${ES_BEATS}/libbeat/_meta/config.reference.yml > \
${PREFIX}/${BEAT_NAME}-linux-386.reference.yml

# Collects all dependencies and then calls update
.PHONY: collect
collect: fields collect-docs configs kibana
Expand All @@ -63,10 +19,10 @@ collect: fields collect-docs configs kibana
.PHONY: configs
configs: python-env
@cat ${ES_BEATS}/auditbeat/_meta/common.p1.yml \
<(go run scripts/generate_config.go -os ${DEV_OS} -concat) \
${ES_BEATS}/auditbeat/_meta/common.p2.yml > _meta/beat.yml
<(go run scripts/generate_config.go -os ${DEV_OS} -concat) \
${ES_BEATS}/auditbeat/_meta/common.p2.yml > _meta/beat.yml
@cat ${ES_BEATS}/auditbeat/_meta/common.reference.yml \
<(go run scripts/generate_config.go -os ${DEV_OS} -ref -concat) > _meta/beat.reference.yml
<(go run scripts/generate_config.go -os ${DEV_OS} -ref -concat) > _meta/beat.reference.yml

# Collects all module docs
.PHONY: collect-docs
Expand Down
Loading

0 comments on commit 23c4bc0

Please sign in to comment.