diff --git a/GNUmakefile b/GNUmakefile index ce29c69..e1c8f85 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -9,6 +9,9 @@ default: build build: fmtcheck go install +release-test: + goreleaser release --snapshot --clean + test: fmtcheck go test -i $(TEST) || exit 1 echo $(TEST) | \ @@ -63,5 +66,5 @@ ifeq (,$(wildcard $(GOPATH)/src/$(WEBSITE_REPO))) endif @$(MAKE) -C $(GOPATH)/src/$(WEBSITE_REPO) website-provider-test PROVIDER_PATH=$(shell pwd) PROVIDER_NAME=$(PKG_NAME) -.PHONY: build test testacc vet fmt fmtcheck errcheck test-compile website website-test +.PHONY: build release-test test testacc vet fmt fmtcheck errcheck test-compile website website-test diff --git a/cobbler/resource_cobbler_distro.go b/cobbler/resource_cobbler_distro.go index 021064a..89cf9c2 100644 --- a/cobbler/resource_cobbler_distro.go +++ b/cobbler/resource_cobbler_distro.go @@ -22,21 +22,18 @@ func resourceDistro() *schema.Resource { Description: "The architecture of the distro. Valid options are: i386, x86_64, ia64, ppc, ppc64, s390, arm.", Type: schema.TypeString, Optional: true, - ForceNew: true, Computed: true, }, "breed": { Description: "The \"breed\" of distribution. Valid options are: redhat, fedora, centos, scientific linux, suse, debian, and ubuntu. These choices may vary depending on the version of Cobbler in use.", Type: schema.TypeString, Required: true, - ForceNew: true, }, "boot_files": { Description: "Files copied into tftpboot beyond the kernel/initrd.", Type: schema.TypeMap, Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, - ForceNew: true, Computed: true, ConflictsWith: []string{"boot_files_inherit"}, }, @@ -52,7 +49,6 @@ func resourceDistro() *schema.Resource { Type: schema.TypeList, Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, - ForceNew: true, Computed: true, }, "boot_loaders_inherit": { @@ -345,40 +341,40 @@ func buildDistro(d *schema.ResourceData, meta interface{}) (cobbler.Distro, erro distro.Breed = d.Get("breed").(string) distro.BootFiles = cobbler.Value[map[string]interface{}]{ Data: bootFiles, - IsInherited: d.Get("boot_files_inherit").(bool), + IsInherited: IsOptionInherited(d, "boot_files"), } distro.BootLoaders = cobbler.Value[[]string]{ Data: bootLoaders, - IsInherited: d.Get("boot_loaders_inherit").(bool), + IsInherited: IsOptionInherited(d, "boot_loaders"), } distro.Comment = d.Get("comment").(string) distro.FetchableFiles = cobbler.Value[map[string]interface{}]{ Data: fetchableFiles, - IsInherited: d.Get("fetchable_files_inherit").(bool), + IsInherited: IsOptionInherited(d, "fetchable_files"), } distro.Kernel = d.Get("kernel").(string) distro.KernelOptions = cobbler.Value[map[string]interface{}]{ Data: kernelOptions, - IsInherited: d.Get("kernel_options_inherit").(bool), + IsInherited: IsOptionInherited(d, "kernel_options"), } distro.KernelOptionsPost = cobbler.Value[map[string]interface{}]{ Data: kernelOptionsPost, - IsInherited: d.Get("kernel_options_post_inherit").(bool), + IsInherited: IsOptionInherited(d, "kernel_options_post"), } distro.Initrd = d.Get("initrd").(string) distro.MgmtClasses = cobbler.Value[[]string]{ Data: mgmtClasses, - IsInherited: d.Get("mgmt_classes_inherit").(bool), + IsInherited: IsOptionInherited(d, "mgmt_classes"), } distro.Name = d.Get("name").(string) distro.OSVersion = d.Get("os_version").(string) distro.Owners = cobbler.Value[[]string]{ Data: owners, - IsInherited: d.Get("owners_inherit").(bool), + IsInherited: IsOptionInherited(d, "owners"), } distro.TemplateFiles = cobbler.Value[map[string]interface{}]{ Data: templateFiles, - IsInherited: d.Get("template_files_inherit").(bool), + IsInherited: IsOptionInherited(d, "template_files"), } return distro, nil diff --git a/cobbler/resource_cobbler_distro_test.go b/cobbler/resource_cobbler_distro_test.go index e4cc2f3..bcdbdc6 100644 --- a/cobbler/resource_cobbler_distro_test.go +++ b/cobbler/resource_cobbler_distro_test.go @@ -44,6 +44,30 @@ func TestAccCobblerDistro_basic_inherit(t *testing.T) { }) } +func TestAccCobblerDistro_basic_inheritConcrete(t *testing.T) { + var distro cobbler.Distro + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccCobblerPreCheck(t) }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCobblerCheckDistroDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCobblerDistroBasicInherit, + Check: resource.ComposeTestCheckFunc( + testAccCobblerCheckDistroExists("cobbler_distro.foo", &distro), + ), + }, + { + Config: testAccCobblerDistroBasicInheritConcrete, + Check: resource.ComposeTestCheckFunc( + testAccCobblerCheckDistroExists("cobbler_distro.foo", &distro), + ), + }, + }, + }) +} + func TestAccCobblerDistro_change(t *testing.T) { var distro cobbler.Distro @@ -125,6 +149,17 @@ var testAccCobblerDistroBasicInherit = ` boot_loaders_inherit = true }` +var testAccCobblerDistroBasicInheritConcrete = ` + resource "cobbler_distro" "foo" { + name = "foo" + breed = "ubuntu" + os_version = "focal" + arch = "x86_64" + kernel = "/srv/www/cobbler/distro_mirror/Ubuntu-20.04/install/vmlinuz" + initrd = "/srv/www/cobbler/distro_mirror/Ubuntu-20.04/install/initrd.gz" + boot_loaders = ["ipxe"] + }` + var testAccCobblerDistroChange1 = ` resource "cobbler_distro" "foo" { name = "foo" diff --git a/cobbler/resource_cobbler_profile.go b/cobbler/resource_cobbler_profile.go index 8e7fb66..d2aa640 100644 --- a/cobbler/resource_cobbler_profile.go +++ b/cobbler/resource_cobbler_profile.go @@ -62,6 +62,7 @@ func resourceProfile() *schema.Resource { Description: "DHCP tag.", Type: schema.TypeString, Optional: true, + Computed: true, }, "distro": { Description: "Parent distribution.", @@ -579,80 +580,80 @@ func buildProfile(d *schema.ResourceData, meta interface{}) (cobbler.Profile, er profile.Autoinstall = d.Get("autoinstall").(string) profile.AutoinstallMeta = cobbler.Value[map[string]interface{}]{ Data: autoinstallMeta, - IsInherited: false, + IsInherited: IsOptionInherited(d, "autoinstall_meta"), } profile.BootFiles = cobbler.Value[map[string]interface{}]{ Data: bootFiles, - IsInherited: d.Get("boot_files_inherit").(bool), + IsInherited: IsOptionInherited(d, "boot_files"), } profile.Comment = d.Get("comment").(string) profile.DHCPTag = d.Get("dhcp_tag").(string) profile.Distro = d.Get("distro").(string) profile.EnableIPXE = cobbler.Value[bool]{ Data: d.Get("enable_ipxe").(bool), - IsInherited: d.Get("enable_ipxe_inherit").(bool), + IsInherited: IsOptionInherited(d, "enable_ipxe"), } profile.EnableMenu = cobbler.Value[bool]{ Data: d.Get("enable_menu").(bool), - IsInherited: d.Get("enable_menu_inherit").(bool), + IsInherited: IsOptionInherited(d, "enable_menu"), } profile.FetchableFiles = cobbler.Value[map[string]interface{}]{ Data: fetchableFiles, - IsInherited: d.Get("fetchable_files_inherit").(bool), + IsInherited: IsOptionInherited(d, "fetchable_files"), } profile.KernelOptions = cobbler.Value[map[string]interface{}]{ Data: kernelOptions, - IsInherited: d.Get("kernel_options_inherit").(bool), + IsInherited: IsOptionInherited(d, "kernel_options"), } profile.KernelOptionsPost = cobbler.Value[map[string]interface{}]{ Data: kernelOptionsPost, - IsInherited: d.Get("kernel_options_post_inherit").(bool), + IsInherited: IsOptionInherited(d, "kernel_options_post"), } profile.MgmtClasses = cobbler.Value[[]string]{ Data: mgmtClasses, - IsInherited: d.Get("mgmt_classes_inherit").(bool), + IsInherited: IsOptionInherited(d, "mgmt_classes"), } profile.MgmtParameters = cobbler.Value[map[string]interface{}]{ Data: mgmtParameters, - IsInherited: d.Get("mgmt_parameters_inherit").(bool), + IsInherited: IsOptionInherited(d, "mgmt_parameters"), } profile.Name = d.Get("name").(string) profile.NameServersSearch = cobbler.Value[[]string]{ Data: nameServersSearch, - IsInherited: d.Get("name_servers_search_inherit").(bool), + IsInherited: IsOptionInherited(d, "name_servers_search"), } profile.NameServers = cobbler.Value[[]string]{ Data: nameServers, - IsInherited: d.Get("name_servers_inherit").(bool), + IsInherited: IsOptionInherited(d, "name_servers"), } profile.NextServerv4 = d.Get("next_server_v4").(string) profile.NextServerv6 = d.Get("next_server_v6").(string) profile.Owners = cobbler.Value[[]string]{ Data: owners, - IsInherited: d.Get("owners_inherit").(bool), + IsInherited: IsOptionInherited(d, "owners"), } profile.Proxy = d.Get("proxy").(string) profile.Repos = repos profile.Server = d.Get("server").(string) profile.TemplateFiles = cobbler.Value[map[string]interface{}]{ Data: templateFiles, - IsInherited: d.Get("template_files_inherit").(bool), + IsInherited: IsOptionInherited(d, "template_files"), } profile.VirtAutoBoot = cobbler.Value[bool]{ Data: d.Get("virt_auto_boot").(bool), - IsInherited: d.Get("virt_auto_boot_inherit").(bool), + IsInherited: IsOptionInherited(d, "virt_auto_boot"), } profile.VirtBridge = d.Get("virt_bridge").(string) profile.VirtCPUs = d.Get("virt_cpus").(int) profile.VirtDiskDriver = d.Get("virt_disk_driver").(string) profile.VirtFileSize = cobbler.Value[float64]{ Data: d.Get("virt_file_size").(float64), - IsInherited: d.Get("virt_file_size_inherit").(bool), + IsInherited: IsOptionInherited(d, "virt_file_size"), } profile.VirtPath = d.Get("virt_path").(string) profile.VirtRAM = cobbler.Value[int]{ Data: d.Get("virt_ram").(int), - IsInherited: d.Get("virt_ram_inherit").(bool), + IsInherited: IsOptionInherited(d, "virt_ram"), } profile.VirtType = d.Get("virt_type").(string) diff --git a/cobbler/resource_cobbler_repo.go b/cobbler/resource_cobbler_repo.go index c3aa9e0..e80db6c 100644 --- a/cobbler/resource_cobbler_repo.go +++ b/cobbler/resource_cobbler_repo.go @@ -36,14 +36,12 @@ func resourceRepo() *schema.Resource { Description: "The architecture of the repo. Valid options are: i386, x86_64, ia64, ppc, ppc64, s390, arm.", Type: schema.TypeString, Optional: true, - ForceNew: true, Computed: true, }, "breed": { Description: "The \"breed\" of distribution. Valid options are: rsync, rhn, yum, apt, and wget. These choices may vary depending on the version of Cobbler in use.", Type: schema.TypeString, Required: true, - ForceNew: true, }, "comment": { Description: "Free form text description.", @@ -291,7 +289,7 @@ func buildRepo(d *schema.ResourceData, meta interface{}) (cobbler.Repo, error) { repo.Comment = d.Get("comment").(string) repo.CreateRepoFlags = cobbler.Value[string]{ Data: d.Get("createrepo_flags").(string), - IsInherited: d.Get("createrepo_flags_inherit").(bool), + IsInherited: IsOptionInherited(d, "createrepo_flags"), } repo.KeepUpdated = d.Get("keep_updated").(bool) repo.Mirror = d.Get("mirror").(string) @@ -299,11 +297,11 @@ func buildRepo(d *schema.ResourceData, meta interface{}) (cobbler.Repo, error) { repo.Name = d.Get("name").(string) repo.Owners = cobbler.Value[[]string]{ Data: owners, - IsInherited: d.Get("owners_inherit").(bool), + IsInherited: IsOptionInherited(d, "owners"), } repo.Proxy = cobbler.Value[string]{ Data: d.Get("proxy").(string), - IsInherited: d.Get("proxy_inherit").(bool), + IsInherited: IsOptionInherited(d, "proxy"), } repo.RpmList = rpmList diff --git a/cobbler/resource_cobbler_system.go b/cobbler/resource_cobbler_system.go index 7eaab2a..e6ea9cd 100644 --- a/cobbler/resource_cobbler_system.go +++ b/cobbler/resource_cobbler_system.go @@ -939,24 +939,24 @@ func buildSystem(d *schema.ResourceData) (cobbler.System, error) { system.Autoinstall = d.Get("autoinstall").(string) system.AutoinstallMeta = cobbler.Value[map[string]interface{}]{ Data: autoinstallMeta, - IsInherited: false, + IsInherited: IsOptionInherited(d, "autoinstall_meta"), } system.BootFiles = cobbler.Value[map[string]interface{}]{ Data: bootFiles, - IsInherited: d.Get("boot_files_inherit").(bool), + IsInherited: IsOptionInherited(d, "boot_files"), } system.BootLoaders = cobbler.Value[[]string]{ Data: bootLoaders, - IsInherited: d.Get("boot_loaders_inherit").(bool), + IsInherited: IsOptionInherited(d, "boot_loaders"), } system.Comment = d.Get("comment").(string) system.EnableIPXE = cobbler.Value[bool]{ Data: d.Get("enable_ipxe").(bool), - IsInherited: d.Get("enable_ipxe_inherit").(bool), + IsInherited: IsOptionInherited(d, "enable_ipxe"), } system.FetchableFiles = cobbler.Value[map[string]interface{}]{ Data: fetchableFiles, - IsInherited: d.Get("fetchable_files_inherit").(bool), + IsInherited: IsOptionInherited(d, "fetchable_files"), } system.Gateway = d.Get("gateway").(string) system.Hostname = d.Get("hostname").(string) @@ -964,19 +964,19 @@ func buildSystem(d *schema.ResourceData) (cobbler.System, error) { system.IPv6DefaultDevice = d.Get("ipv6_default_device").(string) system.KernelOptions = cobbler.Value[map[string]interface{}]{ Data: kernelOptions, - IsInherited: d.Get("kernel_options_inherit").(bool), + IsInherited: IsOptionInherited(d, "kernel_options"), } system.KernelOptionsPost = cobbler.Value[map[string]interface{}]{ Data: kernelOptionsPost, - IsInherited: d.Get("kernel_options_post_inherit").(bool), + IsInherited: IsOptionInherited(d, "kernel_options_post"), } system.MgmtClasses = cobbler.Value[[]string]{ Data: mgmtClasses, - IsInherited: d.Get("mgmt_classes_inherit").(bool), + IsInherited: IsOptionInherited(d, "mgmt_classes"), } system.MgmtParameters = cobbler.Value[map[string]interface{}]{ Data: mgmtParameters, - IsInherited: d.Get("mgmt_parameters_inherit").(bool), + IsInherited: IsOptionInherited(d, "mgmt_parameters"), } system.Name = d.Get("name").(string) system.NameServersSearch = nameServersSearch @@ -986,7 +986,7 @@ func buildSystem(d *schema.ResourceData) (cobbler.System, error) { system.NextServerv6 = d.Get("next_server_v6").(string) system.Owners = cobbler.Value[[]string]{ Data: owners, - IsInherited: d.Get("owners_inherit").(bool), + IsInherited: IsOptionInherited(d, "owners"), } system.PowerAddress = d.Get("power_address").(string) system.PowerID = d.Get("power_id").(string) @@ -998,26 +998,26 @@ func buildSystem(d *schema.ResourceData) (cobbler.System, error) { system.Status = d.Get("status").(string) system.TemplateFiles = cobbler.Value[map[string]interface{}]{ Data: templateFiles, - IsInherited: d.Get("template_files_inherit").(bool), + IsInherited: IsOptionInherited(d, "template_files"), } system.VirtAutoBoot = cobbler.Value[bool]{ Data: d.Get("virt_auto_boot").(bool), - IsInherited: d.Get("virt_auto_boot_inherit").(bool), + IsInherited: IsOptionInherited(d, "virt_auto_boot"), } system.VirtFileSize = cobbler.Value[float64]{ Data: d.Get("virt_file_size").(float64), - IsInherited: d.Get("virt_file_size_inherit").(bool), + IsInherited: IsOptionInherited(d, "virt_file_size"), } system.VirtCPUs = cobbler.Value[int]{ Data: d.Get("virt_cpus").(int), - IsInherited: d.Get("virt_cpus_inherit").(bool), + IsInherited: IsOptionInherited(d, "virt_cpus"), } system.VirtType = d.Get("virt_type").(string) system.VirtPath = d.Get("virt_path").(string) system.VirtPXEBoot = d.Get("virt_pxe_boot").(bool) system.VirtRAM = cobbler.Value[int]{ Data: d.Get("virt_ram").(int), - IsInherited: d.Get("virt_ram_inherit").(bool), + IsInherited: IsOptionInherited(d, "virt_ram"), } system.VirtDiskDriver = d.Get("virt_disk_driver").(string) diff --git a/cobbler/utils.go b/cobbler/utils.go index c1c597c..2e2d2e7 100644 --- a/cobbler/utils.go +++ b/cobbler/utils.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/mitchellh/go-homedir" "hash/crc32" + "log" "os" ) @@ -117,3 +118,17 @@ func SetInherit[K any](d *schema.ResourceData, key string, value cobbler.Value[K } return nil } + +// IsOptionInherited calculates if a given key inherits or not. +func IsOptionInherited(d *schema.ResourceData, key string) bool { + inheritKeyValue := d.Get(fmt.Sprintf("%s_inherit", key)).(bool) + valueKeyHasChange := d.HasChange(key) + if valueKeyHasChange && inheritKeyValue { + log.Println("[INFO] edge case for IsOptionInherit explicitly returned false") + // The inherit key is true due to the tfstate but the user is changing the map in the tf file. + // Due to this the inherit key must be false now. + return false + } + + return inheritKeyValue +} diff --git a/docs/index.md b/docs/index.md index c81231b..63c7f54 100644 --- a/docs/index.md +++ b/docs/index.md @@ -18,10 +18,35 @@ Use the navigation to the left to read about the available resources. ## Example Usage ```terraform +terraform { + required_providers { + cobbler = { + source = "cobbler/cobbler" + version = "3.0.0" + } + } +} + +variable "username" { + type = string +} + +variable "password" { + type = string +} + +variable "url" { + type = string +} + +variable "insecure" { + type = bool +} + provider "cobbler" { username = var.username # optionally use COBBLER_USERNAME env var password = var.password # optionally use COBBLER_PASSWORD env var - api_url = var.url # optionally use COBBLER_URL env var + url = var.url # optionally use COBBLER_URL env var # You may need to allow insecure TLS communications unless you # have configured certificates diff --git a/examples/provider/provider.tf b/examples/provider/provider.tf index ea9463b..f8bc155 100644 --- a/examples/provider/provider.tf +++ b/examples/provider/provider.tf @@ -1,9 +1,34 @@ +terraform { + required_providers { + cobbler = { + source = "cobbler/cobbler" + version = "3.0.0" + } + } +} + +variable "username" { + type = string +} + +variable "password" { + type = string +} + +variable "url" { + type = string +} + +variable "insecure" { + type = bool +} + provider "cobbler" { username = var.username # optionally use COBBLER_USERNAME env var password = var.password # optionally use COBBLER_PASSWORD env var - api_url = var.url # optionally use COBBLER_URL env var + url = var.url # optionally use COBBLER_URL env var # You may need to allow insecure TLS communications unless you # have configured certificates insecure = var.insecure # optionally use COBBLER_INSECURE env var -} \ No newline at end of file +}