<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Developer guide on Blockstor</title><link>https://blockstor.org/developer-guide/</link><description>Recent content in Developer guide on Blockstor</description><generator>Hugo</generator><language>en</language><atom:link href="https://blockstor.org/developer-guide/index.xml" rel="self" type="application/rss+xml"/><item><title>Building</title><link>https://blockstor.org/developer-guide/building/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://blockstor.org/developer-guide/building/</guid><description>&lt;h2 id="binaries"&gt;Binaries&lt;/h2&gt;
&lt;p&gt;Blockstor is a Go module, &lt;code&gt;github.com/cozystack/blockstor&lt;/code&gt;. &lt;code&gt;make build&lt;/code&gt; regenerates the manifests and deep-copy code, runs &lt;code&gt;go fmt&lt;/code&gt; and &lt;code&gt;go vet&lt;/code&gt;, and builds the controller, the satellite, the CLI and the migration tool into &lt;code&gt;bin/&lt;/code&gt;. The API server builds the usual Go way:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;make build
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;go build -o bin/apiserver ./cmd/apiserver
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Binary&lt;/th&gt;
					&lt;th&gt;Source&lt;/th&gt;
					&lt;th&gt;What it is&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;controller&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;cmd/controller&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;controller-runtime manager with the controller-side reconcilers.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;apiserver&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;cmd/apiserver&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;the stateless LINSTOR-compatible REST front end.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;satellite&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;cmd/satellite&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;the per-node manager that drives DRBD, LUKS and storage.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;blockstor&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;cmd/blockstor&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;the operator CLI.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;linstor-migrate&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;cmd/linstor-migrate&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;the LINSTOR database converter.&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Other generators: &lt;code&gt;make manifests&lt;/code&gt; regenerates the CRDs and RBAC from the kubebuilder markers in &lt;code&gt;api/v1alpha1&lt;/code&gt;, and &lt;code&gt;make generate&lt;/code&gt; regenerates the deep-copy code.&lt;/p&gt;</description></item><item><title>Testing</title><link>https://blockstor.org/developer-guide/testing/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://blockstor.org/developer-guide/testing/</guid><description>&lt;p&gt;Each tier answers a different question, and a change is expected to come with tests at the lowest tier that can catch the bug.&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Tier&lt;/th&gt;
					&lt;th&gt;What it covers&lt;/th&gt;
					&lt;th&gt;Where&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;L1 unit&lt;/td&gt;
					&lt;td&gt;Logic, run on every commit&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;*_test.go&lt;/code&gt; next to the code&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;L2 contract, golden&lt;/td&gt;
					&lt;td&gt;Recorded &lt;code&gt;golinstor&lt;/code&gt; responses replayed against the server, byte diff&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;tests/contract/&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;L3 contract, oracle&lt;/td&gt;
					&lt;td&gt;The same requests to a real LINSTOR and to blockstor, JSON diff&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;tests/contract/&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Integration&lt;/td&gt;
					&lt;td&gt;Controllers and REST against a real Kubernetes API server (envtest), satellites faked&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;tests/integration/&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;L4 integration on DRBD&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;make smoke&lt;/code&gt; on the stand&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;tests/smoke*.sh&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;L5 e2e&lt;/td&gt;
					&lt;td&gt;csi-sanity and piraeus-operator e2e on the stand&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;tests/e2e/&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;L6 operator CLI&lt;/td&gt;
					&lt;td&gt;The real CLI, through REST and the satellite, down to the DRBD kernel state&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;tests/e2e/cli-matrix/&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;L7 replay&lt;/td&gt;
					&lt;td&gt;Operator workflows replayed, and the CLI parity diff against upstream&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;tests/operator-harness/&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="unit-tests-and-lint"&gt;Unit tests and lint&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;go &lt;span class="nb"&gt;test&lt;/span&gt; ./...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;make lint
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Tests that need envtest skip themselves when &lt;code&gt;KUBEBUILDER_ASSETS&lt;/code&gt; is not set. &lt;code&gt;make test&lt;/code&gt; downloads the envtest binaries and runs the suite with them. &lt;code&gt;make lint&lt;/code&gt; must be clean before a push.&lt;/p&gt;</description></item><item><title>Repository layout</title><link>https://blockstor.org/developer-guide/repository/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://blockstor.org/developer-guide/repository/</guid><description>&lt;pre tabindex="0"&gt;&lt;code&gt;api/v1alpha1/ CRD types and kubebuilder markers
cmd/ controller, apiserver, satellite, blockstor CLI, linstor-migrate
internal/controller/ controller-side reconcilers
internal/cli/ the blockstor CLI
pkg/ REST, stores, satellite, storage, DRBD, LUKS, placer, dispatcher
config/ generated CRDs, RBAC and kustomize bases
stand/ the Talos and QEMU dev stand and the reference manifests
tests/ contract, integration, e2e, cli-matrix and replay suites
docs/ design notes, parity deltas and audit history
site/ this website
&lt;/code&gt;&lt;/pre&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Path&lt;/th&gt;
					&lt;th&gt;What lives there&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;api/v1alpha1/&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The CRD types. &lt;code&gt;make manifests&lt;/code&gt; turns their markers into &lt;code&gt;config/crd/bases/&lt;/code&gt;.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;pkg/api/v1/&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The REST wire types, shaped like upstream LINSTOR&amp;rsquo;s, and the layer-stack resolver.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;pkg/rest/&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The LINSTOR-compatible REST handlers.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;pkg/store/&lt;/code&gt;, &lt;code&gt;pkg/store/k8s/&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The in-memory and CRD-backed stores behind one &lt;code&gt;store.Store&lt;/code&gt; interface, and the translation between wire and CRD shapes.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;internal/controller/&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Controller-side reconcilers: resources, definitions, groups, pools, snapshots, nodes, plus tiebreakers, auto-diskful, eviction, rebalancing and migration.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;pkg/satellite/controllers/&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Satellite-side reconcilers for resources, pools, snapshots and physical devices, and the &lt;code&gt;events2&lt;/code&gt; observer.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;pkg/satellite/&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The DRBD, LUKS and storage layer reconciler and snapshot shipping.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;pkg/storage/&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Storage providers: LVM, LVM-thin, ZFS, ZFS-thin, loopfile and file.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;pkg/drbd/&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;drbdadm&lt;/code&gt; and &lt;code&gt;drbdsetup&lt;/code&gt; wrappers, the &lt;code&gt;.res&lt;/code&gt; renderer, the &lt;code&gt;events2&lt;/code&gt; parser and the options resolver.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;pkg/luks/&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The &lt;code&gt;cryptsetup&lt;/code&gt; wrapper.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;pkg/placer/&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The autoplacer: capacity-weighted, anti-affinity aware.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;pkg/dispatcher/&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Translates CRDs into the desired state a satellite applies: layers, options, passphrases.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;internal/cli/&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The &lt;code&gt;blockstor&lt;/code&gt; CLI. Its design is in &lt;a href="https://github.com/cozystack/blockstor/blob/main/docs/cli-design.md"&gt;&lt;code&gt;docs/cli-design.md&lt;/code&gt;&lt;/a&gt;.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;cmd/linstor-migrate/&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The LINSTOR database converter.&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;</description></item><item><title>Internals</title><link>https://blockstor.org/developer-guide/internals/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://blockstor.org/developer-guide/internals/</guid><description>&lt;p&gt;This page is about the invariants whose violation would quietly corrupt cluster state. For the user-level picture, see &lt;a href="https://blockstor.org/docs/architecture/"&gt;Architecture&lt;/a&gt;. For a tour of the code, follow the call graph from &lt;code&gt;cmd/controller/main.go&lt;/code&gt; and &lt;code&gt;cmd/satellite/main.go&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="spec-and-status"&gt;Spec and Status&lt;/h2&gt;
&lt;p&gt;Every blockstor CRD has two halves:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Spec&lt;/strong&gt; is desired state. Users, REST handlers and the controller&amp;rsquo;s placement write it. The satellite writes Spec in two places: &lt;code&gt;Resource.Spec.Flags&lt;/code&gt; (and &lt;code&gt;ToggleDiskCancel&lt;/code&gt;) while it carries out a toggle-disk, and the discovered volume groups and ZFS pools in &lt;code&gt;Node.Spec.Props&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Status&lt;/strong&gt; is observed state. The satellite writes what it reads from the kernel (&lt;code&gt;drbdsetup events2&lt;/code&gt;, &lt;code&gt;drbdadm status&lt;/code&gt;, &lt;code&gt;lvs&lt;/code&gt;), and the controller&amp;rsquo;s allocators write values derived from Spec. The REST API writes Status in one place: the resource activate endpoint with &lt;code&gt;?reallocate-port=true&lt;/code&gt; clears &lt;code&gt;Resource.Status.DRBDPort&lt;/code&gt; through a Status patch. The allocator only fills an empty &lt;code&gt;Spec.DRBDPort&lt;/code&gt;, and the controller copies the spec value back into &lt;code&gt;Status&lt;/code&gt;, so the port does not change.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A whole-object &lt;code&gt;Update&lt;/code&gt; is unsafe whenever both halves have different writers: a Spec write in flight would overwrite a concurrent Status write, and the other way round. Status goes through the Status subresource. Fields with more than one writer, such as &lt;code&gt;Resource.Status.DRBDPort&lt;/code&gt; from the controller next to &lt;code&gt;DiskState&lt;/code&gt; and &lt;code&gt;CurrentGi&lt;/code&gt; from the satellite, go through Server-Side Apply with separate field managers, &lt;code&gt;blockstor-controller&lt;/code&gt; and &lt;code&gt;blockstor-satellite&lt;/code&gt;, so each side owns only its own fields.&lt;/p&gt;</description></item><item><title>Contributing</title><link>https://blockstor.org/developer-guide/contributing/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://blockstor.org/developer-guide/contributing/</guid><description>&lt;h2 id="licensing-apache-20-clean-room"&gt;Licensing: Apache 2.0, clean room&lt;/h2&gt;
&lt;p&gt;Blockstor is Apache 2.0. Do not copy structure or code from GPL sources, in particular &lt;code&gt;linstor-server&lt;/code&gt; and &lt;code&gt;drbd-utils&lt;/code&gt;. You may read them to understand behaviour, never to reproduce how they are written.&lt;/p&gt;
&lt;p&gt;Hard rules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No GPL-licensed spec files in the tree. LINSTOR&amp;rsquo;s &lt;code&gt;rest_v1_openapi.yaml&lt;/code&gt; declares GPLv3, and code generated from it is a derivative work.&lt;/li&gt;
&lt;li&gt;No code generated from a GPL source by any tool.&lt;/li&gt;
&lt;li&gt;No Go dependency under GPL, AGPL, LGPL or SSPL. CI enforces this through &lt;a href="https://github.com/cozystack/blockstor/blob/main/.github/workflows/license-check.yml"&gt;&lt;code&gt;license-check.yml&lt;/code&gt;&lt;/a&gt;, which runs &lt;code&gt;go-licenses check --disallowed_types=forbidden,restricted&lt;/code&gt; over &lt;code&gt;./cmd/...&lt;/code&gt;. That is a denylist: it fails on licenses &lt;code&gt;go-licenses&lt;/code&gt; classifies as forbidden or restricted, and lets everything else through, so check a new dependency&amp;rsquo;s license yourself.&lt;/li&gt;
&lt;li&gt;Every Go source file carries an &lt;code&gt;SPDX-License-Identifier: Apache-2.0&lt;/code&gt; header.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Apache-2.0 projects that are safe to study and depend on: &lt;a href="https://github.com/LINBIT/golinstor"&gt;&lt;code&gt;golinstor&lt;/code&gt;&lt;/a&gt;, the authoritative description of the REST wire shape; &lt;a href="https://github.com/piraeusdatastore/piraeus-operator"&gt;&lt;code&gt;piraeus-operator&lt;/code&gt;&lt;/a&gt;; &lt;a href="https://github.com/piraeusdatastore/linstor-csi"&gt;&lt;code&gt;linstor-csi&lt;/code&gt;&lt;/a&gt;; &lt;a href="https://github.com/LINBIT/drbd-reactor"&gt;&lt;code&gt;drbd-reactor&lt;/code&gt;&lt;/a&gt;; and the public DRBD 9 documentation and man pages.&lt;/p&gt;</description></item></channel></rss>