Repository: osbuild/osbuild-composer
Pull request #2097 (2021-12-06 09:56:48)

osbuild-auth-tests: add a build constraint also to certificates.go
These tests can be run only in our CI environment, and not as pure unit tests
launched by go test ./... Let's make sure that all files that belong to these
tests are properly marked with build constraints.

This should fix the following error in Coverity Scan runs:

 # github.com/osbuild/osbuild-composer/cmd/osbuild-auth-tests
runtime.main_main·f: function main is undeclared in the main package
Warning:  Build command go build -o bin/ ./... exited with code 2. Please verify that the build completed successfully.

See https://github.com/osbuild/osbuild-composer/runs/4427232580?check_suite_focus=true

Signed-off-by: Ondřej Budai <ondrej@budai.cz>


This pull request includes:

- [ ] adequate testing for the new functionality or fixed issue
- [ ] adequate documentation informing people about the change such as
  - [ ] submit a PR for the [guides](https://github.com/osbuild/guides) repository if this PR changed any behavior described there: https://www.osbuild.org/guides/

<!--
Thanks for proposing a change to osbuild-composer!

Please don't remove the above check list. These are things that each pull
request must have before it is merged. It helps maintainers to not forget
anything.

If the reason for ticking any of the boxes is ambiguous, please add a short
note explaining why.

In addition, if this pull request fixes a downstream issue, please refer to
test/README.md and add these additional items:

- [ ] 1st commit of any `rhbz#` related PR contains bug reproducer; CI reports FAIL or
- [ ] PR contains automated tests for new functionality and
- [ ] QE has approved reproducer/new tests and
- [ ] Subsequent commits provide bug fixes without modifying the reproducer; CI reports PASS and
- [ ] QE approves this PR; RHBZ status is set to `MODIFIED + Verified=Tested`
-->


ondrejbudai: I had to rebase to get the new terraform config.

Patch:
diff --git a/internal/distro/rhel86/pipelines.go b/internal/distro/rhel86/pipelines.go
index f9a2b9a5c..22c517501 100644
--- a/internal/distro/rhel86/pipelines.go
+++ b/internal/distro/rhel86/pipelines.go
@@ -571,6 +571,7 @@ func ec2SapPipelines(t *imageType, customizations *blueprint.Customizations, opt
 				Value: "8.6",
 			},
 		},
+		nil,
 	)))
 
 	treePipeline = prependKernelCmdlineStage(treePipeline, t, &partitionTable)
diff --git a/internal/distro/rhel90/pipelines.go b/internal/distro/rhel90/pipelines.go
index 87e83659a..a5b31f4cb 100644
--- a/internal/distro/rhel90/pipelines.go
+++ b/internal/distro/rhel90/pipelines.go
@@ -564,6 +564,7 @@ func ec2SapPipelines(t *imageType, customizations *blueprint.Customizations, opt
 				Value: t.arch.distro.osVersion,
 			},
 		},
+		nil,
 	)))
 
 	treePipeline = prependKernelCmdlineStage(treePipeline, t, &partitionTable)
diff --git a/internal/distro/rhel90beta/pipelines.go b/internal/distro/rhel90beta/pipelines.go
index 4048f5642..17b5a6d88 100644
--- a/internal/distro/rhel90beta/pipelines.go
+++ b/internal/distro/rhel90beta/pipelines.go
@@ -548,6 +548,7 @@ func ec2SapPipelines(t *imageType, customizations *blueprint.Customizations, opt
 				Value: "9.0",
 			},
 		},
+		nil,
 	)))
 
 	treePipeline = prependKernelCmdlineStage(treePipeline, t, &partitionTable)
diff --git a/internal/jobqueue/dbjobqueue/dbjobqueue.go b/internal/jobqueue/dbjobqueue/dbjobqueue.go
index 7a3cb2542..e13769e37 100644
--- a/internal/jobqueue/dbjobqueue/dbjobqueue.go
+++ b/internal/jobqueue/dbjobqueue/dbjobqueue.go
@@ -321,7 +321,7 @@ func (q *DBJobQueue) FinishJob(id uuid.UUID, result interface{}) error {
 	var started, finished *time.Time
 	var jobType string
 	canceled := false
-	err = conn.QueryRow(context.Background(), sqlQueryJob, id).Scan(&jobType, nil, &started, nil, &canceled)
+	err = conn.QueryRow(context.Background(), sqlQueryJob, id).Scan(&jobType, nil, &started, &finished, &canceled)
 	if err == pgx.ErrNoRows {
 		return jobqueue.ErrNotExist
 	}
diff --git a/internal/osbuild2/dnf_config_stage.go b/internal/osbuild2/dnf_config_stage.go
index fd112d13c..88863cf01 100644
--- a/internal/osbuild2/dnf_config_stage.go
+++ b/internal/osbuild2/dnf_config_stage.go
@@ -1,22 +1,50 @@
 package osbuild2
 
+import (
+	"fmt"
+)
+
 // DNFConfigStageOptions represents persistent DNF configuration.
 type DNFConfigStageOptions struct {
 	// List of DNF variables.
 	Variables []DNFVariable `json:"variables,omitempty"`
+	Config    *DNFConfig    `json:"config,omitempty"`
 }
 
 func (DNFConfigStageOptions) isStageOptions() {}
 
 // NewDNFConfigStageOptions creates a new DNFConfig Stage options object.
-func NewDNFConfigStageOptions(variables []DNFVariable) *DNFConfigStageOptions {
+func NewDNFConfigStageOptions(variables []DNFVariable, config *DNFConfig) *DNFConfigStageOptions {
 	return &DNFConfigStageOptions{
 		Variables: variables,
+		Config:    config,
+	}
+}
+
+func (o DNFConfigStageOptions) validate() error {
+	if o.Config != nil && o.Config.Main != nil {
+		valid := false
+		allowedIPR := []string{"4", "IPv4", "6", "IPv6", ""}
+		for _, v := range allowedIPR {
+			if o.Config.Main.IPResolve == v {
+				valid = true
+				break
+			}
+		}
+		if !valid {
+			return fmt.Errorf("DNF config parameter ip_resolve does not allow '%s' as a value", o.Config.Main.IPResolve)
+		}
 	}
+
+	return nil
 }
 
 // NewDNFConfigStage creates a new DNFConfig Stage object.
 func NewDNFConfigStage(options *DNFConfigStageOptions) *Stage {
+	if err := options.validate(); err != nil {
+		panic(err)
+	}
+
 	return &Stage{
 		Type:    "org.osbuild.dnf.config",
 		Options: options,
@@ -30,3 +58,11 @@ type DNFVariable struct {
 	// Value of the variable.
 	Value string `json:"value"`
 }
+
+type DNFConfig struct {
+	Main *DNFConfigMain `json:"main,omitempty"`
+}
+
+type DNFConfigMain struct {
+	IPResolve string `json:"ip_resolve,omitempty"`
+}


Test patch:
diff --git a/cmd/osbuild-auth-tests/certificates.go b/cmd/osbuild-auth-tests/certificates.go
index 120c094d4..57f82883d 100644
--- a/cmd/osbuild-auth-tests/certificates.go
+++ b/cmd/osbuild-auth-tests/certificates.go
@@ -1,3 +1,5 @@
+// +build integration
+
 package main
 
 import (
diff --git a/internal/osbuild2/dnf_config_stage_test.go b/internal/osbuild2/dnf_config_stage_test.go
index 827cf2d98..cfbe65fc1 100644
--- a/internal/osbuild2/dnf_config_stage_test.go
+++ b/internal/osbuild2/dnf_config_stage_test.go
@@ -1,9 +1,12 @@
 package osbuild2
 
 import (
+	"encoding/json"
+	"reflect"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 func TestNewDNFConfigStageOptions(t *testing.T) {
@@ -14,10 +17,17 @@ func TestNewDNFConfigStageOptions(t *testing.T) {
 		},
 	}
 
+	dnfconfig := &DNFConfig{
+		Main: &DNFConfigMain{
+			IPResolve: "4",
+		},
+	}
+
 	expectedOptions := &DNFConfigStageOptions{
 		Variables: variables,
+		Config:    dnfconfig,
 	}
-	actualOptions := NewDNFConfigStageOptions(variables)
+	actualOptions := NewDNFConfigStageOptions(variables, dnfconfig)
 	assert.Equal(t, expectedOptions, actualOptions)
 }
 
@@ -29,3 +39,95 @@ func TestNewDNFConfigStage(t *testing.T) {
 	actualStage := NewDNFConfigStage(&DNFConfigStageOptions{})
 	assert.Equal(t, expectedStage, actualStage)
 }
+
+func TestJSONDNFConfigStage(t *testing.T) {
+	expectedOptions := DNFConfigStageOptions{
+		Variables: []DNFVariable{
+			{
+				Name:  "release",
+				Value: "8.4",
+			},
+		},
+		Config: &DNFConfig{
+			Main: &DNFConfigMain{
+				IPResolve: "4",
+			},
+		},
+	}
+
+	inputString := `{"variables":[{"name":"release","value":"8.4"}],"config":{"main":{"ip_resolve":"4"}}}`
+	var inputOptions DNFConfigStageOptions
+	err := json.Unmarshal([]byte(inputString), &inputOptions)
+	assert.NoError(t, err, "failed to parse JSON dnf config")
+	assert.True(t, reflect.DeepEqual(expectedOptions, inputOptions))
+
+	inputBytes, err := json.Marshal(expectedOptions)
+	assert.NoError(t, err, "failed to marshal YUM config into JSON")
+	assert.Equal(t, inputString, string(inputBytes))
+}
+
+func TestDNFConfigValidate(t *testing.T) {
+	variables := []DNFVariable{
+		{
+			Name:  "release",
+			Value: "8.4",
+		},
+	}
+
+	tests := []struct {
+		options DNFConfigStageOptions
+		valid   bool
+	}{
+		{
+			DNFConfigStageOptions{},
+			true,
+		},
+		{
+			DNFConfigStageOptions{
+				Variables: variables,
+				Config: &DNFConfig{
+					Main: nil,
+				},
+			},
+			true,
+		},
+		{
+			DNFConfigStageOptions{
+				Variables: variables,
+				Config: &DNFConfig{
+					Main: &DNFConfigMain{},
+				},
+			},
+			true,
+		},
+		{
+			DNFConfigStageOptions{
+				Variables: variables,
+				Config: &DNFConfig{
+					Main: &DNFConfigMain{
+						IPResolve: "4",
+					},
+				},
+			},
+			true,
+		},
+		{
+			DNFConfigStageOptions{
+				Variables: variables,
+				Config: &DNFConfig{
+					Main: &DNFConfigMain{
+						IPResolve: "urgh",
+					},
+				},
+			},
+			false,
+		},
+	}
+	for _, test := range tests {
+		if test.valid {
+			require.NotPanics(t, func() { NewDNFConfigStage(&test.options) })
+		} else {
+			require.Panics(t, func() { NewDNFConfigStage(&test.options) })
+		}
+	}
+}
diff --git a/test/cases/azure.sh b/test/cases/azure.sh
index 49cccb4b8..fdad40dde 100755
--- a/test/cases/azure.sh
+++ b/test/cases/azure.sh
@@ -178,6 +178,17 @@ name = "cloud-init"
 enabled = ["sshd", "cloud-init", "cloud-init-local", "cloud-config", "cloud-final"]
 EOF
 
+# Make sure the specified storage account exists
+if [ "$(az resource list --name "$AZURE_STORAGE_ACCOUNT")" == "[]" ]; then
+	echo "The storage account ${AZURE_STORAGE_ACCOUNT} was removed!"
+	az storage account create \
+		--name "${AZURE_STORAGE_ACCOUNT}" \
+		--resource-group "${AZURE_RESOURCE_GROUP}" \
+		--location  "${AZURE_LOCATION}" \
+		--sku Standard_RAGRS \
+		--kind StorageV2
+fi
+
 # Prepare the blueprint for the compose.
 greenprint "📋 Preparing blueprint"
 sudo composer-cli blueprints push "$BLUEPRINT_FILE"