diff --git a/.gitignore b/.gitignore
index 6d17819..b925cf6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
-SOURCES/grafana-pcp-2.0.2.tar.gz
-SOURCES/grafana-pcp-deps-2.0.2.tar.xz
+SOURCES/grafana-pcp-3.0.2.tar.gz
+SOURCES/grafana-pcp-vendor-3.0.2.tar.xz
+SOURCES/grafana-pcp-webpack-3.0.2.tar.gz
diff --git a/.grafana-pcp.metadata b/.grafana-pcp.metadata
index aca3298..e4fd00f 100644
--- a/.grafana-pcp.metadata
+++ b/.grafana-pcp.metadata
@@ -1,2 +1,3 @@
-72d5789fd9277bc9816f6a0590bb5f044ad1175c SOURCES/grafana-pcp-2.0.2.tar.gz
-226f68fa48b86eb4dfdcca73a79ab292adaa6503 SOURCES/grafana-pcp-deps-2.0.2.tar.xz
+8196f1c480fd56f90eb5cf6aaf42c0f0818dff73 SOURCES/grafana-pcp-3.0.2.tar.gz
+d2b773374b80b1ceac9111c3ecb2364b968f42b0 SOURCES/grafana-pcp-vendor-3.0.2.tar.xz
+2fc03a2e11363ede44e4dca8a5814af01829ea8b SOURCES/grafana-pcp-webpack-3.0.2.tar.gz
diff --git a/SOURCES/Makefile b/SOURCES/Makefile
new file mode 100644
index 0000000..6fffbaa
--- /dev/null
+++ b/SOURCES/Makefile
@@ -0,0 +1,45 @@
+all: grafana-pcp-$(VER).tar.gz \
+	 grafana-pcp-vendor-$(VER).tar.xz \
+	 grafana-pcp-webpack-$(VER).tar.gz
+
+grafana-pcp-$(VER).tar.gz grafana-pcp-$(VER)/:
+	wget https://github.com/performancecopilot/grafana-pcp/archive/v$(VER)/grafana-pcp-$(VER).tar.gz
+	rm -rf grafana-pcp-$(VER)
+	tar xfz grafana-pcp-$(VER).tar.gz
+	cd grafana-pcp-$(VER) && shopt -s nullglob && \
+		for patch in ../*.patch; do patch -p1 < $$patch; done
+
+grafana-pcp-vendor-$(VER).tar.xz: grafana-pcp-$(VER)/
+	# Go
+	cd grafana-pcp-$(VER) && go mod vendor -v
+	awk '$$2~/^v/ && $$4 != "indirect" {print "Provides: bundled(golang(" $$1 ")) = " substr($$2, 2)}' grafana-pcp-$(VER)/go.mod | \
+		sed -E 's/=(.*)-(.*)-(.*)/=\1-\2.\3/g' > $@.manifest
+
+	# Node.js
+	cd grafana-pcp-$(VER) && yarn install --pure-lockfile
+	# Remove files with licensing issues
+	find grafana-pcp-$(VER) -type d -name 'node-notifier' -prune -exec rm -r {} \;
+	find grafana-pcp-$(VER) -name '*.exe' -delete
+	# Remove not required packages
+	rm -r grafana-pcp-$(VER)/node_modules/puppeteer
+	./list_bundled_nodejs_packages.py grafana-pcp-$(VER)/ >> $@.manifest
+
+	# Jsonnet
+	cd grafana-pcp-$(VER) && jb --jsonnetpkg-home=vendor_jsonnet install
+
+	# Create tarball
+	XZ_OPT=-9 tar cfJ $@ \
+		grafana-pcp-$(VER)/vendor \
+		$$(find grafana-pcp-$(VER) -type d -name "node_modules" -prune) \
+		grafana-pcp-$(VER)/vendor_jsonnet
+
+grafana-pcp-webpack-$(VER).tar.gz: grafana-pcp-$(VER)/
+	cd grafana-pcp-$(VER) && \
+		yarn install --pure-lockfile && \
+		make dist-dashboards dist-frontend && \
+		chmod -R g-w,o-w dist
+
+	tar cfz $@ grafana-pcp-$(VER)/dist
+
+clean:
+	rm -rf *.tar.gz *.tar.xz *.manifest *.rpm grafana-pcp-*/
diff --git a/SOURCES/create_dependency_bundle.sh b/SOURCES/create_dependency_bundle.sh
deleted file mode 100755
index fbcd787..0000000
--- a/SOURCES/create_dependency_bundle.sh
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/bin/bash -eu
-
-SRC=$(readlink -f "${1:?Usage: $0 source destination}")
-DEST=$(readlink -f "${2:?Usage: $0 source destination}")
-
-if [ -f "$DEST" ]; then
-    echo "File $DEST exists already."
-    exit 0
-fi
-if [ "$#" -gt 2 ]; then
-    PATCHES=$(readlink -f "${@:3}")
-else
-    PATCHES=""
-fi
-
-pushd "$(mktemp -d)"
-
-echo Extracting sources...
-tar xfz "$SRC"
-cd grafana-pcp-*
-
-echo Applying patches...
-for patch in $PATCHES
-do
-    patch -p1 < $patch
-done
-
-echo Installing dependencies...
-yarn install
-
-echo Removing files with licensing issues...
-rm -rf node_modules/node-notifier
-
-echo Compressing...
-XZ_OPT=-9 tar cJf "$DEST" node_modules
-
-popd
diff --git a/SOURCES/list_bundled_nodejs_packages.py b/SOURCES/list_bundled_nodejs_packages.py
index a42e223..a7c5e22 100755
--- a/SOURCES/list_bundled_nodejs_packages.py
+++ b/SOURCES/list_bundled_nodejs_packages.py
@@ -1,24 +1,23 @@
 #!/usr/bin/env python3
 import sys
-import os.path
-import tarfile
-from io import TextIOWrapper
 import json
 import re
 from packaging import version
 
 
-def read_declared_pkgs(f):
-    package_json = json.load(f)
-    return list(package_json['devDependencies'].keys()) + list(package_json['dependencies'].keys())
+def read_declared_pkgs(package_json_path):
+    with open(package_json_path) as f:
+        package_json = json.load(f)
+        return list(package_json['dependencies'].keys()) + list(package_json['devDependencies'].keys())
 
 
-def read_installed_pkgs(f):
-    lockfile = f.read()
-    return re.findall(r'^"?'  # can start with a "
-                      r'(.+?)@.+(?:,.*)?:\n'  # characters up to @
-                      r'  version "(.+)"',  # and the version
-                      lockfile, re.MULTILINE)
+def read_installed_pkgs(yarn_lock_path):
+    with open(yarn_lock_path) as f:
+        lockfile = f.read()
+        return re.findall(r'^"?'  # can start with a "
+                          r'(.+?)@.+(?:,.*)?:\n'  # characters up to @
+                          r'  version "(.+)"',  # and the version
+                          lockfile, re.MULTILINE)
 
 
 def list_provides(declared_pkgs, installed_pkgs):
@@ -29,23 +28,17 @@ def list_provides(declared_pkgs, installed_pkgs):
         versions = [version.parse(pkg_version)
                     for pkg_name, pkg_version in installed_pkgs if pkg_name == declared_pkg]
         oldest_version = sorted(versions)[0]
-        yield f"Provides: bundled(nodejs-{declared_pkg}) = {oldest_version}"
+        yield f"Provides: bundled(npm({declared_pkg})) = {oldest_version}"
 
 
 if __name__ == "__main__":
     if len(sys.argv) != 2:
-        print(f"usage: {sys.argv[0]} grafana-pcp-X.Y.Z.tar.gz", file=sys.stdout)
+        print(f"usage: {sys.argv[0]} package-X.Y.Z/", file=sys.stdout)
         sys.exit(1)
 
-    source_archive_path = sys.argv[1]
-    root_dir = os.path.basename(source_archive_path)[:-len('.tar.gz')]
-    with tarfile.open(source_archive_path) as tar:
-        package_json = TextIOWrapper(tar.extractfile(f'{root_dir}/package.json'))
-        declared_pkgs = read_declared_pkgs(package_json)
-
-        yarn_lock = TextIOWrapper(tar.extractfile(f'{root_dir}/yarn.lock'))
-        installed_pkgs = read_installed_pkgs(yarn_lock)
-
+    package_dir = sys.argv[1]
+    declared_pkgs = read_declared_pkgs(f"{package_dir}/package.json")
+    installed_pkgs = read_installed_pkgs(f"{package_dir}/yarn.lock")
     provides = list_provides(declared_pkgs, installed_pkgs)
     for provide in sorted(provides):
         print(provide)
diff --git a/SPECS/grafana-pcp.spec b/SPECS/grafana-pcp.spec
index e7f5801..a5313ee 100644
--- a/SPECS/grafana-pcp.spec
+++ b/SPECS/grafana-pcp.spec
@@ -1,25 +1,65 @@
+# https://bugzilla.redhat.com/show_bug.cgi?id=1752991
+# unfortunately the go_arches macro doesn't reflect that change yet
+ExcludeArch: i686
+
+%global grafanapcp_arches %{lua: go_arches = {}
+  for arch in rpm.expand("%{go_arches}"):gmatch("%S+") do
+    go_arches[arch] = 1
+  end
+  for arch in rpm.expand("%{nodejs_arches}"):gmatch("%S+") do
+    if go_arches[arch] then
+      print(arch .. " ")
+  end
+end}
+
+# gobuild and gotest macros are defined in go-rpm-macros, which is not available on RHEL
+# definitions lifted from Fedora 34 podman.spec
+%if ! 0%{?gobuild:1}
+%define gobuild(o:) GO111MODULE=off go build -buildmode pie -compiler gc -tags="rpm_crashtraceback ${BUILDTAGS:-}" -ldflags "${LDFLAGS:-} -B 0x$(head -c20 /dev/urandom|od -An -tx1|tr -d ' \\n') -extldflags '-Wl,-z,relro -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld '" -a -v -x %{?**};
+%endif
+%if ! 0%{?gotest:1}
+%define gotest() GO111MODULE=off go test -buildmode pie -compiler gc -ldflags "${LDFLAGS:-} -extldflags '-Wl,-z,relro -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld '" %{?**};
+%endif
+
+# Specify if the frontend and dashboards will be compiled as part of the build or are attached
+# as a webpack tarball (in case of an unsuitable nodejs or jsonnet version on the build system)
+%define compile_frontend 0
+
 Name:           grafana-pcp
-Version:        2.0.2
+Version:        3.0.2
 Release:        1%{?dist}
 Summary:        Performance Co-Pilot Grafana Plugin
+License:        ASL 2.0
+URL:            https://github.com/performancecopilot/grafana-pcp
 
-%global         github https://github.com/performancecopilot/grafana-pcp
-%global         install_dir %{_sharedstatedir}/grafana/plugins/grafana-pcp
+Source0:        https://github.com/performancecopilot/grafana-pcp/archive/v%{version}/%{name}-%{version}.tar.gz
+Source1:        grafana-pcp-vendor-%{version}.tar.xz
+%if %{compile_frontend} == 0
+# Source2 contains the precompiled frontend and dashboards
+Source2:        grafana-pcp-webpack-%{version}.tar.gz
+%endif
+Source3:        Makefile
+Source4:        list_bundled_nodejs_packages.py
 
-BuildArch:      noarch
-ExclusiveArch:  %{nodejs_arches}
+# Intersection of go_arches and nodejs_arches
+ExclusiveArch:  %{grafanapcp_arches}
 
-License:        ASL 2.0
-URL:            %{github}
+BuildRequires:  systemd-rpm-macros, golang, go-srpm-macros
+%if 0%{?fedora} >= 31
+BuildRequires:  go-rpm-macros
+%endif
+%if %{compile_frontend}
+BuildRequires:  make, nodejs >= 1:12, nodejs < 1:13, yarnpkg, golang-github-google-jsonnet
+%endif
+
+# omit golang debugsource, see BZ 995136 and related
+%global         dwz_low_mem_die_limit 0
+%global         _debugsource_template %{nil}
 
-Source0:        %{github}/archive/v%{version}/%{name}-%{version}.tar.gz
-Source1:        grafana-pcp-deps-%{version}.tar.xz
-Source2:        create_dependency_bundle.sh
-Source3:        list_bundled_nodejs_packages.py
+%global         install_dir %{_sharedstatedir}/grafana/plugins/performancecopilot-pcp-app
 
-BuildRequires:  nodejs
-Requires:       grafana >= 6.6.0
-Suggests:       pcp >= 5.0.0
+Requires:       grafana >= 7.3.6
+Suggests:       pcp >= 5.2.2
 Suggests:       redis >= 5.0.0
 Suggests:       bpftrace >= 0.9.2
 
@@ -30,48 +70,61 @@ Obsoletes: pcp-webapp-grafana <= 4.3.4
 Obsoletes: pcp-webapp-graphite <= 4.3.4
 Obsoletes: pcp-webapp-vector <= 4.3.4
 
-# Bundled npm packages
-Provides: bundled(nodejs-@babel/cli) = 7.8.4
-Provides: bundled(nodejs-@babel/core) = 7.8.4
-Provides: bundled(nodejs-@babel/preset-env) = 7.8.4
-Provides: bundled(nodejs-@babel/preset-react) = 7.8.3
-Provides: bundled(nodejs-@babel/preset-typescript) = 7.8.3
-Provides: bundled(nodejs-@grafana/data) = 6.6.0
-Provides: bundled(nodejs-@grafana/ui) = 6.6.0
-Provides: bundled(nodejs-@types/benchmark) = 1.0.31
-Provides: bundled(nodejs-@types/d3) = 5.7.2
-Provides: bundled(nodejs-@types/grafana) = 4.6.3
-Provides: bundled(nodejs-@types/jest) = 24.9.1
-Provides: bundled(nodejs-@types/lodash) = 4.14.149
-Provides: bundled(nodejs-babel-jest) = 24.9.0
-Provides: bundled(nodejs-babel-loader) = 8.0.6
-Provides: bundled(nodejs-babel-plugin-angularjs-annotate) = 0.10.0
-Provides: bundled(nodejs-benchmark) = 2.1.4
-Provides: bundled(nodejs-clean-webpack-plugin) = 0.1.19
-Provides: bundled(nodejs-copy-webpack-plugin) = 5.1.1
-Provides: bundled(nodejs-core-js) = 1.2.7
-Provides: bundled(nodejs-css-loader) = 1.0.1
-Provides: bundled(nodejs-d3-flame-graph) = 2.1.9
-Provides: bundled(nodejs-d3-selection) = 1.4.0
-Provides: bundled(nodejs-expr-eval) = 1.2.3
-Provides: bundled(nodejs-jest) = 24.9.0
-Provides: bundled(nodejs-jest-date-mock) = 1.0.8
-Provides: bundled(nodejs-jsdom) = 9.12.0
-Provides: bundled(nodejs-lodash) = 4.17.15
-Provides: bundled(nodejs-memoize-one) = 4.1.0
-Provides: bundled(nodejs-mocha) = 6.2.2
-Provides: bundled(nodejs-prunk) = 1.3.1
-Provides: bundled(nodejs-q) = 1.5.1
-Provides: bundled(nodejs-regenerator-runtime) = 0.11.1
-Provides: bundled(nodejs-request) = 2.88.0
-Provides: bundled(nodejs-style-loader) = 0.22.1
-Provides: bundled(nodejs-ts-jest) = 24.3.0
-Provides: bundled(nodejs-ts-loader) = 4.5.0
-Provides: bundled(nodejs-tslint) = 5.20.1
-Provides: bundled(nodejs-tslint-config-airbnb) = 5.11.2
-Provides: bundled(nodejs-typescript) = 3.7.5
-Provides: bundled(nodejs-webpack) = 4.41.5
-Provides: bundled(nodejs-webpack-cli) = 3.3.10
+# vendored golang and node.js build dependencies
+# this is for security purposes, if nodejs-foo ever needs an update,
+# affected packages can be easily identified.
+# Note: generated by the Makefile (see README.md)
+Provides: bundled(golang(github.com/grafana/grafana-plugin-sdk-go)) = 0.79.0
+Provides: bundled(golang(github.com/smartystreets/goconvey)) = 1.6.4
+Provides: bundled(npm(@babel/plugin-transform-modules-commonjs)) = 7.12.1
+Provides: bundled(npm(@grafana/data)) = 7.3.6
+Provides: bundled(npm(@grafana/runtime)) = 7.3.6
+Provides: bundled(npm(@grafana/toolkit)) = 7.3.6
+Provides: bundled(npm(@grafana/ui)) = 7.3.6
+Provides: bundled(npm(@types/blueimp-md5)) = 2.7.0
+Provides: bundled(npm(@types/d3-selection)) = 1.4.3
+Provides: bundled(npm(@types/enzyme)) = 3.10.5
+Provides: bundled(npm(@types/enzyme-adapter-react-16)) = 1.0.6
+Provides: bundled(npm(@types/expect-puppeteer)) = 3.3.1
+Provides: bundled(npm(@types/jest)) = 24.0.13
+Provides: bundled(npm(@types/jest-environment-puppeteer)) = 4.4.1
+Provides: bundled(npm(@types/lodash)) = 4.14.165
+Provides: bundled(npm(@types/memoize-one)) = 5.1.2
+Provides: bundled(npm(@types/react-autosuggest)) = 9.3.14
+Provides: bundled(npm(@types/react-redux)) = 7.1.12
+Provides: bundled(npm(@types/redux)) = 3.6.0
+Provides: bundled(npm(@types/redux-persist)) = 4.3.1
+Provides: bundled(npm(@types/redux-persist-transform-filter)) = 0.0.4
+Provides: bundled(npm(babel-plugin-remove-object-properties)) = 1.0.2
+Provides: bundled(npm(blueimp-md5)) = 2.18.0
+Provides: bundled(npm(core-js)) = 1.2.7
+Provides: bundled(npm(d3-flame-graph)) = 3.1.1
+Provides: bundled(npm(d3-selection)) = 1.4.1
+Provides: bundled(npm(emotion)) = 10.0.27
+Provides: bundled(npm(enzyme)) = 3.11.0
+Provides: bundled(npm(enzyme-adapter-react-16)) = 1.15.5
+Provides: bundled(npm(eslint-plugin-prettier)) = 3.1.4
+Provides: bundled(npm(jest)) = 25.5.4
+Provides: bundled(npm(jest-date-mock)) = 1.0.8
+Provides: bundled(npm(jest-puppeteer)) = 4.4.0
+Provides: bundled(npm(lodash)) = 4.17.19
+Provides: bundled(npm(loglevel)) = 1.7.1
+Provides: bundled(npm(loglevel-plugin-prefix)) = 0.8.4
+Provides: bundled(npm(memoize-one)) = 4.1.0
+Provides: bundled(npm(monaco-editor)) = 0.20.0
+Provides: bundled(npm(monaco-editor-webpack-plugin)) = 1.9.0
+Provides: bundled(npm(prettier)) = 1.19.1
+Provides: bundled(npm(prettier-plugin-organize-imports)) = 1.1.1
+Provides: bundled(npm(puppeteer)) = 5.5.0
+Provides: bundled(npm(react-autosuggest)) = 10.0.4
+Provides: bundled(npm(react-monaco-editor)) = 0.36.0
+Provides: bundled(npm(react-redux)) = 7.2.2
+Provides: bundled(npm(react-use)) = 15.3.4
+Provides: bundled(npm(redux)) = 3.7.2
+Provides: bundled(npm(redux-persist)) = 4.10.2
+Provides: bundled(npm(redux-thunk)) = 2.3.0
+Provides: bundled(npm(ts-jest)) = 26.3.0
+Provides: bundled(npm(utility-types)) = 3.10.0
 
 
 %description
@@ -80,30 +133,83 @@ scalable time series from pmseries(1) and Redis, live PCP metrics and
 bpftrace scripts from pmdabpftrace(1), as well as several dashboards.
 
 %prep
-%setup -q
-%setup -q -a 1
+%setup -q -T -D -b 0
+%setup -q -T -D -b 1
+%if %{compile_frontend} == 0
+%setup -q -T -D -b 2
+%endif
+
+# Set up Go build subdir and links
+mkdir -p %{_builddir}/src/github.com/performancecopilot
+ln -s %{_builddir}/%{name}-%{version} \
+    %{_builddir}/src/github.com/performancecopilot/grafana-pcp
 
-%build
-rm -rf dist
-./node_modules/webpack/bin/webpack.js --config webpack.config.prod.js
 
+%build
+# Build frontend datasources
+%if %{compile_frontend}
+make dist-dashboards dist-frontend
 # webpack/copy-webpack-plugin sometimes outputs files with mode = 666 due to reasons unknown (race condition/umask issue afaics)
-chmod -Rf a+rX,u+w,g-w,o-w dist
+chmod -R g-w,o-w dist
+%endif
+
+# Build backend datasource
+cd %{_builddir}/src/github.com/performancecopilot/grafana-pcp
+export GOPATH=%{_builddir}
+%gobuild -o dist/datasources/redis/pcp_redis_datasource_$(go env GOOS)_$(go env GOARCH) ./pkg
 
-%check
-./node_modules/jest/bin/jest.js --silent
 
 %install
 install -d -m 755 %{buildroot}/%{install_dir}
 cp -a dist/* %{buildroot}/%{install_dir}
 
+%postun
+# uninstall of old package
+%systemd_postun_with_restart grafana-server.service
+
+%posttrans
+# install or upgrade of new package
+if [ -x /usr/bin/systemctl ]; then
+  /usr/bin/systemctl try-restart grafana-server.service || :
+fi
+
+
+%check
+# Test frontend datasources
+%if %{compile_frontend}
+yarn test
+%endif
+
+# Test backend datasource
+cd %{_builddir}/src/github.com/performancecopilot/grafana-pcp
+export GOPATH=%{_builddir}
+%gotest ./pkg/...
+
+
 %files
 %{install_dir}
 
 %license LICENSE NOTICE
 %doc README.md
 
+
 %changelog
+* Fri Jan 22 2021 Andreas Gerstmayr <agerstmayr@redhat.com> 3.0.2-1
+- update to 3.0.2 tagged upstream community sources, see CHANGELOG
+
+* Wed Dec 23 2020 Andreas Gerstmayr <agerstmayr@redhat.com> 3.0.1-1
+- update to 3.0.1 tagged upstream community sources, see CHANGELOG
+
+* Thu Nov 26 2020 Andreas Gerstmayr <agerstmayr@redhat.com> 3.0.0-1
+- update to 3.0.0 tagged upstream community sources, see CHANGELOG
+- bundle golang dependencies and (optionally) node.js dependencies
+
+* Fri Aug 07 2020 Andreas Gerstmayr <agerstmayr@redhat.com> 2.0.2-3
+- bpftrace: show process name and PID in flame graphs
+
+* Fri Jul 24 2020 Andreas Gerstmayr <agerstmayr@redhat.com> 2.0.2-2
+- vector: do not show all cgroups in the container overview dashboard in case no containers are present on the system
+
 * Mon May 11 2020 Andreas Gerstmayr <agerstmayr@redhat.com> 2.0.2-1
 - update to upstream version 2.0.2, see CHANGELOG