diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 8e30358..1990d4b 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -43,3 +43,15 @@ jobs: uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a # v9.3.0 with: version: v2.9.0 + commit-lint: + runs-on: ubuntu-latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - name: Check out repository + # https://github.com/actions/checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + - name: Commitlint + uses: wagoid/commitlint-github-action@b948419dd99f3fd78a6548d48f94e3df7f6bf3ed # v6.2.1 diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index a7b5f6a..16f0346 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -18,7 +18,7 @@ jobs: strategy: matrix: cobbler_version: - - 2f0ff03f17ae957cac65d9ef033a98e8d597145a # v4.0.0a4 + - v4.0.0b1 fail-fast: false steps: - name: Check out code into the Go module directory @@ -36,9 +36,9 @@ jobs: - name: Get dependencies run: | go mod download - - name: Replace git version hash + - name: Replace Cobbler image tag run: | - sed -i "s/cobbler_commit=.*/cobbler_commit=${{ matrix.cobbler_version }}/" testing/start.sh + sed -i "s/cobbler_image_tag=.*/cobbler_image_tag=${{ matrix.cobbler_version }}/" testing/start.sh - name: Restore OS ISO id: cache-iso-restore # https://github.com/actions/cache @@ -59,7 +59,7 @@ jobs: *.iso key: ${{ steps.cache-iso-restore.outputs.cache-primary-key }} - name: Upload coverage report to GH artifacts - if: matrix.cobbler_version == 'df356046f3cf27be62a61001b982d5983800cfd9' + if: matrix.cobbler_version == 'v4.0.0b1' # https://github.com/actions/upload-artifact uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f73274a..bc0d4bf 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # vim: ft=yaml --- -default_stages: [commit] +default_stages: [pre-commit] repos: - repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook rev: v9.26.0 diff --git a/Makefile b/Makefile index 98e50ec..c6454d4 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ BINARY_NAME=cobbler EXECUTOR?=docker -COBBLER_SERVER_URL=http://localhost:8081/cobbler_api +COBBLER_SERVER_URL=http://localhost:25151/ TEST?=$$(go list ./... |grep -v 'vendor') VERSION=0.0.1 diff --git a/cmd/distro.go b/cmd/distro.go index b49aaac..e176ef9 100644 --- a/cmd/distro.go +++ b/cmd/distro.go @@ -211,6 +211,13 @@ func updateDistroFromFlags(cmd *cobra.Command, distro *cobbler.Distro) error { return } distro.RedhatManagementKey = distroNewRedhatManagementKey + case "source-tree-path": + var distroNewSourceTreePath string + distroNewSourceTreePath, err = cmd.Flags().GetString("source-tree-path") + if err != nil { + return + } + distro.SourceTreePath = distroNewSourceTreePath case "template-files": var newTemplateFiles map[string]string newTemplateFiles, err = cmd.Flags().GetStringToString("template-files") diff --git a/cmd/distro_test.go b/cmd/distro_test.go index 6ec50ea..5d040d3 100644 --- a/cmd/distro_test.go +++ b/cmd/distro_test.go @@ -183,6 +183,62 @@ func Test_DistroEditCmd(t *testing.T) { } } +func Test_DistroEditCmd_SourceTreePath(t *testing.T) { + type args struct { + command []string + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + // The path must be an absolute path that exists on the server; /extracted_iso_image + // is bind-mounted into the test Cobbler container by testing/compose.yml. + name: "plain", + args: args{command: []string{"--config", "../testing/.cobbler.yaml", "distro", "edit", "--name", "test-distro-edit-source-tree-path", "--source-tree-path", "/extracted_iso_image"}}, + want: "Event ID:", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Cleanup + var err error + defer func() { + // Client is initialized since this is the cleanup + cleanupErr := removeDistro(Client, tt.args.command[5]) + cobbler.FailOnError(t, cleanupErr) + }() + // Arrange + setupClient(t) + _, err = createDistro(Client, tt.args.command[5]) + cobbler.FailOnError(t, err) + cobra.OnInitialize(initConfig, setupLogger) + rootCmd := NewRootCmd() + rootCmd.SetArgs(tt.args.command) + stdout := bytes.NewBufferString("") + stderr := bytes.NewBufferString("") + rootCmd.SetOut(stdout) + rootCmd.SetErr(stderr) + + // Act + err = rootCmd.Execute() + + // Assert + cobbler.FailOnError(t, err) + FailOnNonEmptyStream(t, stderr) + FailOnNonEmptyStream(t, stdout) + updatedDistro, err := Client.GetDistro(tt.args.command[5], false, false) + cobbler.FailOnError(t, err) + if updatedDistro.SourceTreePath != "/extracted_iso_image" { + t.Fatal("distro source-tree-path update wasn't successful") + } + }) + } +} + func Test_DistroFindCmd(t *testing.T) { type args struct { command []string diff --git a/cmd/image.go b/cmd/image.go index 4f3c1ab..24da094 100644 --- a/cmd/image.go +++ b/cmd/image.go @@ -287,6 +287,13 @@ func updateImageFromFlags(cmd *cobra.Command, image *cobbler.Image) error { return } image.Virt.Type = imageNewVirtType + case "virt-uefi": + var imageNewVirtUEFI bool + imageNewVirtUEFI, err = cmd.Flags().GetBool("virt-uefi") + if err != nil { + return + } + image.Virt.UEFI = imageNewVirtUEFI } }) return err diff --git a/cmd/image_test.go b/cmd/image_test.go index 5959231..9ad8146 100644 --- a/cmd/image_test.go +++ b/cmd/image_test.go @@ -181,6 +181,60 @@ func Test_ImageEditCmd(t *testing.T) { } } +func Test_ImageEditCmd_VirtUEFI(t *testing.T) { + type args struct { + command []string + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "plain", + args: args{command: []string{"--config", "../testing/.cobbler.yaml", "image", "edit", "--name", "test-image-edit-virt-uefi", "--virt-uefi=true"}}, + want: "Event ID:", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Cleanup + var err error + defer func() { + // Client is initialized since this is the cleanup + cleanupErr := removeImage(Client, tt.args.command[5]) + cobbler.FailOnError(t, cleanupErr) + }() + // Arrange + setupClient(t) + _, err = createImage(Client, tt.args.command[5]) + cobbler.FailOnError(t, err) + cobra.OnInitialize(initConfig, setupLogger) + rootCmd := NewRootCmd() + rootCmd.SetArgs(tt.args.command) + stdout := bytes.NewBufferString("") + stderr := bytes.NewBufferString("") + rootCmd.SetOut(stdout) + rootCmd.SetErr(stderr) + + // Act + err = rootCmd.Execute() + + // Assert + cobbler.FailOnError(t, err) + FailOnNonEmptyStream(t, stderr) + FailOnNonEmptyStream(t, stdout) + updatedImage, err := Client.GetImage(tt.args.command[5], false, false) + cobbler.FailOnError(t, err) + if !updatedImage.Virt.UEFI { + t.Fatal("image virt-uefi update wasn't successful") + } + }) + } +} + func Test_ImageFindCmd(t *testing.T) { type args struct { command []string diff --git a/cmd/metadata.go b/cmd/metadata.go index 7a55440..1cdc181 100644 --- a/cmd/metadata.go +++ b/cmd/metadata.go @@ -78,6 +78,11 @@ var distroStringFlagMetadata = map[string]FlagMetadata[string]{ DefaultValue: "", Usage: "RedHat management key (registration key for RHN, Spacewalk, or Satellite)", }, + "source-tree-path": { + Name: "source-tree-path", + DefaultValue: "", + Usage: "the original location of the distro's source tree on disk (for use by the dynamic_httpd manager)", + }, } var distroStringSliceFlagMetadata = map[string]FlagMetadata[[]string]{ @@ -209,6 +214,11 @@ var profileBoolFlagMetadata = map[string]FlagMetadata[bool]{ DefaultValue: false, Usage: "auto boot this VM?", }, + "virt-uefi": { + Name: "virt-uefi", + DefaultValue: false, + Usage: "boot this VM via UEFI firmware instead of legacy BIOS?", + }, } var profileIntFlagMetadata = map[string]FlagMetadata[int]{ @@ -369,6 +379,11 @@ var systemBoolFlagMetadata = map[string]FlagMetadata[bool]{ DefaultValue: false, Usage: "use PXE to build this VM?", }, + "virt-uefi": { + Name: "virt-uefi", + DefaultValue: false, + Usage: "boot this VM via UEFI firmware instead of legacy BIOS?", + }, } var systemIntFlagMetadata = map[string]FlagMetadata[int]{ @@ -704,6 +719,11 @@ var imageBoolFlagMetadata = map[string]FlagMetadata[bool]{ DefaultValue: false, Usage: "auto boot this VM?", }, + "virt-uefi": { + Name: "virt-uefi", + DefaultValue: false, + Usage: "boot this VM via UEFI firmware instead of legacy BIOS?", + }, } var imageStringSliceFlagMetadata = map[string]FlagMetadata[[]string]{ diff --git a/cmd/profile.go b/cmd/profile.go index af02e9e..9565ba4 100644 --- a/cmd/profile.go +++ b/cmd/profile.go @@ -400,6 +400,13 @@ func updateProfileFromFlags(cmd *cobra.Command, profile *cobbler.Profile) error return } profile.Virt.Type = profileNewVirtType + case "virt-uefi": + var profileNewVirtUEFI bool + profileNewVirtUEFI, err = cmd.Flags().GetBool("virt-uefi") + if err != nil { + return + } + profile.Virt.UEFI = profileNewVirtUEFI } }) return nil diff --git a/cmd/profile_test.go b/cmd/profile_test.go index 70a8834..05fa723 100644 --- a/cmd/profile_test.go +++ b/cmd/profile_test.go @@ -191,6 +191,60 @@ func Test_ProfileEditCmd(t *testing.T) { } } +func Test_ProfileEditCmd_VirtUEFI(t *testing.T) { + type args struct { + command []string + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "plain", + args: args{command: []string{"--config", "../testing/.cobbler.yaml", "profile", "edit", "--name", "test-profile-edit-virt-uefi", "--virt-uefi=true"}}, + want: "Event ID:", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Cleanup + var err error + defer func() { + // Client is initialized since this is the cleanup + cleanupErr := removeProfile(Client, tt.args.command[5]) + cobbler.FailOnError(t, cleanupErr) + }() + // Arrange + setupClient(t) + _, err = createProfile(Client, tt.args.command[5]) + cobbler.FailOnError(t, err) + cobra.OnInitialize(initConfig, setupLogger) + rootCmd := NewRootCmd() + rootCmd.SetArgs(tt.args.command) + stdout := bytes.NewBufferString("") + stderr := bytes.NewBufferString("") + rootCmd.SetOut(stdout) + rootCmd.SetErr(stderr) + + // Act + err = rootCmd.Execute() + + // Assert + cobbler.FailOnError(t, err) + FailOnNonEmptyStream(t, stderr) + FailOnNonEmptyStream(t, stdout) + updatedProfile, err := Client.GetProfile(tt.args.command[5], false, false) + cobbler.FailOnError(t, err) + if !updatedProfile.Virt.UEFI { + t.Fatal("profile virt-uefi update wasn't successful") + } + }) + } +} + func Test_ProfileFindCmd(t *testing.T) { type args struct { command []string diff --git a/cmd/signature.go b/cmd/signature.go index 58c3a22..147af4d 100644 --- a/cmd/signature.go +++ b/cmd/signature.go @@ -112,7 +112,8 @@ func NewSignatureReloadCmd() *cobra.Command { return err } - _, _ = fmt.Fprintln(cmd.OutOrStdout(), "This functionality cannot be used in the new CLI until https://github.com/cobbler/cobbler/issues/3791 is implemented!") + eventId, _ := Client.BackgroundSignatureReload() + _, _ = fmt.Fprintf(cmd.OutOrStdout(), "Event ID: %s\n", eventId) return nil }, } diff --git a/cmd/signature_test.go b/cmd/signature_test.go index fbd07eb..cc2bb17 100644 --- a/cmd/signature_test.go +++ b/cmd/signature_test.go @@ -31,9 +31,9 @@ func Test_SignatureReloadCmd(t *testing.T) { t.Fatal(err) } stdoutString := string(stdoutBytes) - if !strings.Contains(stdoutString, "This functionality cannot be used in the new CLI") { + if !strings.Contains(stdoutString, "Event ID:") { fmt.Println(stdoutString) - t.Fatal("No missing feature message present") + t.Fatal("No Event ID present") } } diff --git a/cmd/system.go b/cmd/system.go index 0379b40..9e00ea3 100644 --- a/cmd/system.go +++ b/cmd/system.go @@ -297,6 +297,13 @@ func updateSystemFromFlags(cmd *cobra.Command, system *cobbler.System) error { return } system.Virt.Type = systemNewVirtType + case "virt-uefi": + var systemNewVirtUEFI bool + systemNewVirtUEFI, err = cmd.Flags().GetBool("virt-uefi") + if err != nil { + return + } + system.Virt.UEFI = systemNewVirtUEFI case "gateway": var systemNewGateway string systemNewGateway, err = cmd.Flags().GetString("gateway") diff --git a/cmd/system_test.go b/cmd/system_test.go index 14b3973..a08c76b 100644 --- a/cmd/system_test.go +++ b/cmd/system_test.go @@ -191,6 +191,60 @@ func Test_SystemEditCmd(t *testing.T) { } } +func Test_SystemEditCmd_VirtUEFI(t *testing.T) { + type args struct { + command []string + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "plain", + args: args{command: []string{"--config", "../testing/.cobbler.yaml", "system", "edit", "--name", "test-system-edit-virt-uefi", "--virt-uefi=true"}}, + want: "Event ID:", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Cleanup + var err error + defer func() { + // Client is initialized since this is the cleanup + cleanupErr := removeSystem(Client, tt.args.command[5]) + cobbler.FailOnError(t, cleanupErr) + }() + // Arrange + setupClient(t) + _, err = createSystem(Client, tt.args.command[5]) + cobbler.FailOnError(t, err) + cobra.OnInitialize(initConfig, setupLogger) + rootCmd := NewRootCmd() + rootCmd.SetArgs(tt.args.command) + stdout := bytes.NewBufferString("") + stderr := bytes.NewBufferString("") + rootCmd.SetOut(stdout) + rootCmd.SetErr(stderr) + + // Act + err = rootCmd.Execute() + + // Assert + cobbler.FailOnError(t, err) + FailOnNonEmptyStream(t, stderr) + FailOnNonEmptyStream(t, stdout) + updatedSystem, err := Client.GetSystem(tt.args.command[5], false, false) + cobbler.FailOnError(t, err) + if !updatedSystem.Virt.UEFI { + t.Fatal("system virt-uefi update wasn't successful") + } + }) + } +} + func Test_SystemFindCmd(t *testing.T) { type args struct { command []string diff --git a/go.mod b/go.mod index 5212e7e..4aec781 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ module github.com/cobbler/cli go 1.24 require ( - github.com/cobbler/cobblerclient v1.0.0-rc2 + github.com/cobbler/cobblerclient v1.0.0-rc3 github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.19.0 diff --git a/go.sum b/go.sum index 6380c89..6e2954c 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/cobbler/cobblerclient v1.0.0-rc2 h1:zJWEhIzy6l33nyv5TOc7/mKmTyY8e6x2Ql71lGuq37E= -github.com/cobbler/cobblerclient v1.0.0-rc2/go.mod h1:BoS4GMBvwnypRHcOMedyipm+0Pu7IP5pSX6A/DqU6mo= +github.com/cobbler/cobblerclient v1.0.0-rc3 h1:2ZSThkEUx/Y0CJjZm+LDlZHQ5wVGE053kdsppAdUtlo= +github.com/cobbler/cobblerclient v1.0.0-rc3/go.mod h1:BoS4GMBvwnypRHcOMedyipm+0Pu7IP5pSX6A/DqU6mo= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/testing/.cobbler.yaml b/testing/.cobbler.yaml index 6531712..e772ce3 100644 --- a/testing/.cobbler.yaml +++ b/testing/.cobbler.yaml @@ -1,3 +1,3 @@ -server_url: "http://127.0.0.1:8081/cobbler_api" +server_url: "http://127.0.0.1:25151/" server_username: "cobbler" server_password: "cobbler" diff --git a/testing/compose.yml b/testing/compose.yml index c06ea37..b59f02e 100644 --- a/testing/compose.yml +++ b/testing/compose.yml @@ -1,20 +1,11 @@ services: cobbler: - image: cobbler-dev + image: ghcr.io/cobbler/cobblerd:${COBBLER_IMAGE_TAG:-latest} container_name: cobbler-dev - privileged: true # Required for Cobbler 3.3.2 and newer + environment: + - COBBLER_XMLRPC_BIND_ADDRESS=0.0.0.0 volumes: - - ./cobbler_source:/code - - ../extracted_iso_image:/extracted_iso_image - - ./import.py:/import.py:ro + - ../extracted_iso_image:/extracted_iso_image:ro,z ports: - - 8081:80 - # We chmod the code, otherwise some files are read-only and cannot be cleaned up: - # The Cobbler 4.0.0 container no longer ships the `cobbler` CLI (superseded by this - # project), so the import is done via a direct XML-RPC call instead (see import.py). - command: bash -c " - /code/docker/develop/scripts/setup-supervisor.sh && - chmod -R o+w /code && - python3 /import.py && - tail -F /dev/null - " + - 25151:25151 + command: cobblerd -F diff --git a/testing/start.sh b/testing/start.sh index c9260c0..45ed9bc 100755 --- a/testing/start.sh +++ b/testing/start.sh @@ -6,30 +6,13 @@ if [ -z "$1" ] echo "No cobbler server url supplied" fi -cobbler_commit=2f0ff03f17ae957cac65d9ef033a98e8d597145a # 4.0.0a4 -cobbler_branch=main +cobbler_image_tag=v4.0.0b1 iso_url=https://cdimage.ubuntu.com/ubuntu-legacy-server/releases/20.04/release/ubuntu-20.04.1-legacy-server-amd64.iso iso_os=ubuntu valid_iso_checksum=00a9d46306fbe9beb3581853a289490bc231c51f iso_filename=$(echo ${iso_url##*/}) valid_extracted_iso_checksum=dd0b3148e1f071fb86aee4b0395fd63b -if [ -d "./testing/cobbler_source" ]; then - current_commit=$(cd ./testing/cobbler_source && git rev-parse HEAD 2>/dev/null || echo unknown) - if [ "$current_commit" = "$cobbler_commit" ]; then - echo "Cobbler code already cloned and the correct commit is checked out" - else - rm -rf ./testing/cobbler_source - fi -fi -if [ ! -d "./testing/cobbler_source" ]; then - git clone https://github.com/cobbler/cobbler.git -b $cobbler_branch testing/cobbler_source - cd ./testing/cobbler_source - printf "Changing to version of Cobbler being tested.\n\n" - git checkout $cobbler_commit > /dev/null 2>&1 - cd - -fi - echo $(pwd) if [ -f "$iso_filename" ] && [ $(sha1sum $iso_filename | awk '{print $1}') == "$valid_iso_checksum" ]; then echo "ISO already downloaded" @@ -45,8 +28,7 @@ else xorriso -osirrox on -indev $iso_filename -extract / extracted_iso_image fi -docker build -f ./testing/cobbler_source/docker/develop/develop.dockerfile -t cobbler-dev . -docker compose -f testing/compose.yml up -d +COBBLER_IMAGE_TAG=$cobbler_image_tag docker compose -f testing/compose.yml up -d SERVER_URL=$1 printf "### Waiting for Cobbler to become available on ${SERVER_URL} \n\n" @@ -66,7 +48,9 @@ until $(curl --connect-timeout 1 --output /dev/null --silent ${SERVER_URL}); do sleep 5 done -# Sleep 10 seconds to let the "cobbler import" succeed -sleep 10 +# Cobbler 4.0.0 no longer ships the Python `cobbler` CLI (superseded by this repository's Go +# CLI), so seed the test distro via a direct XML-RPC call from the host instead of `cobbler +# import` - see import.py. +python3 testing/import.py -docker compose -f testing/compose.yml logs \ No newline at end of file +docker compose -f testing/compose.yml logs