Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add persistent storage to agent #131

Merged
merged 1 commit into from
Feb 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/kind.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ jobs:
- name: Checkout the code
uses: actions/checkout@v2

- name: Install qemu-img
run: sudo apt update && sudo apt install -y qemu-utils

- name: Set env variables
run: |
export "REGISTRY_IP=$(ip addr show eth0 | grep -oP '(?<=inet\s)\d+\.\d+\.\d+\.\d+')"
Expand Down Expand Up @@ -73,11 +76,13 @@ jobs:

- name: Deploy assisted-migration
run: |
make deploy-on-kind MIGRATION_PLANNER_API_IMAGE=$MIGRATION_PLANNER_API_IMAGE MIGRATION_PLANNER_AGENT_IMAGE=$MIGRATION_PLANNER_AGENT_IMAGE MIGRATION_PLANNER_API_IMAGE_PULL_POLICY=$MIGRATION_PLANNER_API_IMAGE_PULL_POLICY INSECURE_REGISTRY=$INSECURE_REGISTRY MIGRATION_PLANNER_NAMESPACE=default
make deploy-on-kind MIGRATION_PLANNER_API_IMAGE=$MIGRATION_PLANNER_API_IMAGE MIGRATION_PLANNER_AGENT_IMAGE=$MIGRATION_PLANNER_AGENT_IMAGE MIGRATION_PLANNER_API_IMAGE_PULL_POLICY=$MIGRATION_PLANNER_API_IMAGE_PULL_POLICY INSECURE_REGISTRY=$INSECURE_REGISTRY MIGRATION_PLANNER_NAMESPACE=default PERSISTENT_DISK_DEVICE=/dev/vda
kubectl wait --for=condition=Ready pods --all --timeout=240s
kubectl port-forward --address 0.0.0.0 service/migration-planner-agent 7443:7443 &
kubectl port-forward --address 0.0.0.0 service/migration-planner 3443:3443 &

- name: Run test
run: |
mkdir /tmp/untarova
qemu-img convert -f vmdk -O qcow2 data/persistence-disk.vmdk /tmp/untarova/persistence-disk.qcow2
sudo make integration-test PLANNER_IP=${REGISTRY_IP}
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ deploy-on-kind:
sed "s|@MIGRATION_PLANNER_AGENT_IMAGE@|$(MIGRATION_PLANNER_AGENT_IMAGE)|g; \
s|@INSECURE_REGISTRY@|$(INSECURE_REGISTRY)|g; \
s|@MIGRATION_PLANNER_API_IMAGE_PULL_POLICY@|$(MIGRATION_PLANNER_API_IMAGE_PULL_POLICY)|g; \
s|@MIGRATION_PLANNER_API_IMAGE@|$(MIGRATION_PLANNER_API_IMAGE)|g" \
s|@MIGRATION_PLANNER_API_IMAGE@|$(MIGRATION_PLANNER_API_IMAGE)|g; \
s|@PERSISTENT_DISK_DEVICE@|$(PERSISTENT_DISK_DEVICE)|g" \
deploy/k8s/migration-planner.yaml.template > deploy/k8s/migration-planner.yaml
$(KUBECTL) apply -n "${MIGRATION_PLANNER_NAMESPACE}" -f 'deploy/k8s/*-service.yaml'
$(KUBECTL) apply -n "${MIGRATION_PLANNER_NAMESPACE}" -f 'deploy/k8s/*-secret.yaml'
Expand Down
24 changes: 15 additions & 9 deletions cmd/planner-agent/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package main
import (
"bytes"
"context"
"errors"
"flag"
"fmt"
"os"
Expand Down Expand Up @@ -77,15 +76,15 @@ func (a *agentCmd) Execute() error {
undo := zap.ReplaceGlobals(logger)
defer undo()

agentID, err := a.readFile(agentFilename)
agentID, err := a.readFileFromPersistent(agentFilename)
if err != nil {
zap.S().Fatalf("failed to retreive agent_id: %v", err)
}

// Try to read jwt from file.
// We're assuming the jwt is valid.
// The agent will not try to validate the jwt. The backend is responsible for validating the token.
jwt, err := a.readFile(jwtFilename)
jwt, err := a.readFileFromVolatile(jwtFilename)
if err != nil {
zap.S().Errorf("failed to read jwt: %v", err)
}
Expand All @@ -97,16 +96,23 @@ func (a *agentCmd) Execute() error {
return nil
}

func (a *agentCmd) readFile(filename string) (string, error) {
// look for it in data dir
confDirPath := path.Join(a.config.DataDir, filename)
if _, err := os.Stat(confDirPath); err == nil {
content, err := os.ReadFile(confDirPath)
func (a *agentCmd) readFile(baseDir string, filename string) (string, error) {
filePath := path.Join(baseDir, filename)
if _, err := os.Stat(filePath); err == nil {
content, err := os.ReadFile(filePath)
if err != nil {
return "", err
}
return string(bytes.TrimSpace(content)), nil
}

return "", errors.New("agent_id not found")
return "", fmt.Errorf("file not found: %s", filePath)
}

func (a *agentCmd) readFileFromVolatile(filename string) (string, error) {
return a.readFile(a.config.DataDir, filename)
}

func (a *agentCmd) readFileFromPersistent(filename string) (string, error) {
return a.readFile(a.config.PersistentDataDir, filename)
}
28 changes: 23 additions & 5 deletions data/MigrationAssessment.ovf
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
<?xml version='1.0' encoding='UTF-8'?>
<Envelope xmlns="http://schemas.dmtf.org/ovf/envelope/1" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1" xmlns:vmw="http://www.vmware.com/schema/ovf" xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData">
<References>
<File ovf:id="file1" ovf:href="MigrationAssessment.iso" ovf:size="1220542464"/>
<File ovf:href="MigrationAssessment.iso" ovf:id="file1" ovf:size="1244659712"/>
<File ovf:href="persistence-disk.vmdk" ovf:id="file2" ovf:size="68096"/>
</References>
<DiskSection>
<Info>Virtual disk information</Info>
<Disk ovf:capacity="50" ovf:capacityAllocationUnits="byte * 2^20" ovf:diskId="vmdisk1" ovf:fileRef="file2" ovf:format="http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized" ovf:populatedSize="0"/>
</DiskSection>
<NetworkSection>
<Info>The list of logical networks</Info>
<Network ovf:name="routable-network">
Expand All @@ -12,15 +17,16 @@
<VirtualSystem ovf:id="MigrationAssessment">
<Info>A Virtual system</Info>
<Name>MigrationAssessment</Name>
<OperatingSystemSection ovf:id="80" ovf:version="8" vmw:osType="rhel9_64Guest">
<Info>The operating system installed</Info>
<Description>Other Linux (64-bit)</Description>
<OperatingSystemSection ovf:id="80" ovf:version="9" vmw:osType="rhel9_64Guest">
<Info>The kind of installed guest operating system</Info>
<Description>Red Hat Enterprise Linux 9 (64-bit)</Description>
</OperatingSystemSection>
<VirtualHardwareSection>
<Info>Virtual hardware requirements</Info>
<System>
<vssd:ElementName>Virtual Hardware Family</vssd:ElementName>
<vssd:InstanceID>0</vssd:InstanceID>
<vssd:VirtualSystemIdentifier>MigrationAssessment</vssd:VirtualSystemIdentifier>
<vssd:VirtualSystemType>vmx-20</vssd:VirtualSystemType>
</System>
<Item>
Expand All @@ -44,11 +50,12 @@
<rasd:Description>SCSI Controller</rasd:Description>
<rasd:ElementName>SCSI Controller 1</rasd:ElementName>
<rasd:InstanceID>3</rasd:InstanceID>
<rasd:ResourceSubType>VirtualSCSI</rasd:ResourceSubType>
<rasd:ResourceType>6</rasd:ResourceType>
<vmw:Config ovf:required="false" vmw:key="slotInfo.pciSlotNumber" vmw:value="16"/>
</Item>
<Item>
<rasd:Address>0</rasd:Address>
<rasd:Address>1</rasd:Address>
<rasd:Description>IDE Controller</rasd:Description>
<rasd:ElementName>IDE Controller 1</rasd:ElementName>
<rasd:InstanceID>4</rasd:InstanceID>
Expand Down Expand Up @@ -76,6 +83,17 @@
<vmw:Config ovf:required="false" vmw:key="wakeOnLanEnabled" vmw:value="true"/>
<vmw:Config ovf:required="false" vmw:key="connectable.allowGuestControl" vmw:value="false"/>
</Item>
<Item>
<rasd:AddressOnParent>0</rasd:AddressOnParent>
<rasd:ElementName>Hard disk 1</rasd:ElementName>
<rasd:HostResource>ovf:/disk/vmdisk1</rasd:HostResource>
<rasd:InstanceID>10</rasd:InstanceID>
<rasd:Parent>3</rasd:Parent>
<rasd:ResourceType>17</rasd:ResourceType>
<vmw:Config ovf:required="false" vmw:key="backing.writeThrough" vmw:value="false"/>
<vmw:Config ovf:required="false" vmw:key="backing.diskMode" vmw:value="independent_persistent"/>
<vmw:Config ovf:required="false" vmw:key="guestReadOnly" vmw:value="false"/>
</Item>
<Item ovf:required="false">
<rasd:ElementName>Video card</rasd:ElementName>
<rasd:InstanceID>7</rasd:InstanceID>
Expand Down
25 changes: 22 additions & 3 deletions data/ignition.template
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,32 @@ systemd:
contents: |
[Unit]
Description=Service to retrieve system uuid
Requires=var-lib-data.mount
After=var-lib-data.mount
[Service]
Type=oneshot
ExecStart=/bin/bash -c 'cat /sys/class/dmi/id/product_uuid > /var/home/core/.migration-planner/data/agent_id'
ExecStartPost=/bin/bash -c 'chown core:core /var/home/core/.migration-planner/data/agent_id'
ExecStart=/bin/bash -c 'cat /sys/class/dmi/id/product_uuid > /var/lib/data/agent_id'
ExecStartPost=/bin/bash -c 'chown -R core:core /var/lib/data'
RemainAfterExit=true
[Install]
WantedBy=multi-user.target
- name: var-lib-data.mount
enabled: true
contents: |
[Mount]
What=/dev/disk/by-label/DATA
Where=/var/lib/data
Type=ext4
[Install]
WantedBy=local-fs.target

storage:
filesystems:
- path: /var/lib/data
device: {{ .PersistentDiskDevice }}
format: ext4
label: "DATA"

links:
- path: /home/core/.config/systemd/user/timers.target.wants/podman-auto-update.timer
target: /usr/lib/systemd/user/podman-auto-update.timer
Expand Down Expand Up @@ -88,6 +105,7 @@ storage:
inline: |
config-dir: /agent/config
data-dir: /agent/data
persistent-data-dir: /agent/persistent-data
www-dir: /app/www
log-level: debug
update-interval: 5s
Expand Down Expand Up @@ -116,14 +134,15 @@ storage:
Exec= -config /agent/config/config.yaml
PublishPort=3333:3333
Volume=/home/core/.migration-planner:/agent:Z
Volume=/var/lib/data:/agent/persistent-data:Z
Environment=OPA_SERVER=127.0.0.1:8181
Network=host
UserNS=keep-id:uid=1001

[Service]
Restart=on-failure
RestartSec=5
ExecStartPre=/bin/bash -c 'until [ -f /var/home/core/.migration-planner/data/agent_id ]; do sleep 1; done'
ExecStartPre=/bin/bash -c 'until [ -f /var/lib/data/agent_id ]; do sleep 1; done'

[Install]
WantedBy=multi-user.target default.target
Expand Down
Binary file added data/persistence-disk.vmdk
Binary file not shown.
2 changes: 2 additions & 0 deletions deploy/k8s/migration-planner.yaml.template
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ spec:
value: @MIGRATION_PLANNER_AGENT_IMAGE@
- name: INSECURE_REGISTRY
value: "@INSECURE_REGISTRY@"
- name: PERSISTENT_DISK_DEVICE
value: "@PERSISTENT_DISK_DEVICE@"
volumeMounts:
volumeMounts:
- name: migration-planner-config
Expand Down
2 changes: 1 addition & 1 deletion internal/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ func (a *Agent) start(ctx context.Context, plannerClient client.Planner) {
// TODO refactor health checker to call it from the main goroutine
healthChecker.Start(ctx, a.healtCheckStopCh)

collector := service.NewCollector(a.config.DataDir)
collector := service.NewCollector(a.config.DataDir, a.config.PersistentDataDir)
collector.Collect(ctx)

updateTicker := jitterbug.New(time.Duration(a.config.UpdateInterval.Duration), &jitterbug.Norm{Stdev: 30 * time.Millisecond, Mean: 0})
Expand Down
1 change: 1 addition & 0 deletions internal/agent/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ var _ = Describe("Agent", func() {
config := config.Config{
PlannerService: config.PlannerService{Config: *client.NewDefault()},
DataDir: agentTmpFolder,
PersistentDataDir: agentTmpFolder,
ConfigDir: agentTmpFolder,
UpdateInterval: util.Duration{Duration: updateInterval},
HealthCheckInterval: 10,
Expand Down
7 changes: 6 additions & 1 deletion internal/agent/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const (
DefaultConfigFile = DefaultConfigDir + "/config.yaml"
// DefaultDataDir is the default directory where the agent's data is stored
DefaultDataDir = "/var/lib/planner"
// DefaultPersistentDataDir is the default directory where the agent's data is stored
DefaultPersistentDataDir = "/var/lib/data"
// DefaultWwwDir is the default directory from which the agent serves static files
DefaultWwwDir = "/var/www/planner"
// DefaultPlannerEndpoint is the default address of the migration planner server
Expand All @@ -48,6 +50,8 @@ type Config struct {
ConfigDir string `json:"config-dir"`
// DataDir is the directory where the agent's data is stored
DataDir string `json:"data-dir"`
// PersistentDataDir is the directory where persistent data is stored
PersistentDataDir string `json:"persistent-data-dir"`
// WwwDir is the directory from which the agent serves static files
WwwDir string `json:"www-dir"`
// SourceID is the ID of this source in the planner
Expand Down Expand Up @@ -85,6 +89,7 @@ func NewDefault() *Config {
c := &Config{
ConfigDir: DefaultConfigDir,
DataDir: DefaultDataDir,
PersistentDataDir: DefaultPersistentDataDir,
WwwDir: DefaultWwwDir,
PlannerService: PlannerService{Config: *client.NewDefault()},
UpdateInterval: util.Duration{Duration: DefaultUpdateInterval},
Expand All @@ -102,14 +107,14 @@ func (cfg *Config) Validate() error {
if err := cfg.PlannerService.Validate(); err != nil {
return err
}

requiredFields := []struct {
value string
name string
checkPath bool
}{
{cfg.ConfigDir, "config-dir", true},
{cfg.DataDir, "data-dir", true},
{cfg.PersistentDataDir, "persistent-data-dir", true},
}

for _, field := range requiredFields {
Expand Down
2 changes: 1 addition & 1 deletion internal/agent/rest.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func RegisterApi(router *chi.Mux, statusUpdater *service.StatusUpdater, configur
_ = render.Render(w, r, ServiceUIReply{URL: configuration.PlannerService.Service.UI})
})
router.Put("/api/v1/credentials", func(w http.ResponseWriter, r *http.Request) {
credentialHandler(configuration.DataDir, w, r)
credentialHandler(configuration.PersistentDataDir, w, r)
})
router.Get("/api/v1/inventory", func(w http.ResponseWriter, r *http.Request) {
data, err := getInventory(configuration.DataDir)
Expand Down
12 changes: 7 additions & 5 deletions internal/agent/service/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@ import (
)

type Collector struct {
dataDir string
once sync.Once
dataDir string
credentialsDir string
once sync.Once
}

func NewCollector(dataDir string) *Collector {
func NewCollector(dataDir string, credentialsDir string) *Collector {
return &Collector{
dataDir: dataDir,
dataDir: dataDir,
credentialsDir: credentialsDir,
}
}

Expand All @@ -56,7 +58,7 @@ func (c *Collector) Collect(ctx context.Context) {
}

func (c *Collector) run() {
credentialsFilePath := filepath.Join(c.dataDir, config.CredentialsFile)
credentialsFilePath := filepath.Join(c.credentialsDir, config.CredentialsFile)
zap.S().Named("collector").Infof("Waiting for credentials")
waitForFile(credentialsFilePath)

Expand Down
2 changes: 1 addition & 1 deletion internal/agent/service/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (s *StatusUpdater) UpdateStatus(ctx context.Context, status api.AgentStatus

func (s *StatusUpdater) CalculateStatus() (api.AgentStatus, string, *api.Inventory) {
inventoryFilePath := filepath.Join(s.config.DataDir, config.InventoryFile)
credentialsFilePath := filepath.Join(s.config.DataDir, config.CredentialsFile)
credentialsFilePath := filepath.Join(s.config.PersistentDataDir, config.CredentialsFile)
reader := fileio.NewReader()

err := reader.CheckPathExists(credentialsFilePath)
Expand Down
Loading