diff --git a/internal/controller/prefix_controller.go b/internal/controller/prefix_controller.go index 2b132d3e..230c7ed0 100644 --- a/internal/controller/prefix_controller.go +++ b/internal/controller/prefix_controller.go @@ -248,6 +248,7 @@ func (r *PrefixReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr } // check if the created prefix contains the entire description from spec + // TODO: implement a different way to check if the description was truncated if _, found := strings.CutPrefix(netboxPrefixModel.Description, req.NamespacedName.String()+" // "+prefix.Spec.Description); !found { r.EventStatusRecorder.Recorder().Event(prefix, corev1.EventTypeWarning, "PrefixDescriptionTruncated", "prefix was created with truncated description") } @@ -291,12 +292,13 @@ func generateNetboxPrefixModelFromPrefixSpec(spec *netboxv1.PrefixSpec, req ctrl } } + template := config.GetOperatorConfig().DescriptionFormat return &models.Prefix{ Prefix: spec.Prefix, Metadata: &models.NetboxMetadata{ Comments: spec.Comments, Custom: netboxCustomFields, - Description: req.NamespacedName.String() + " // " + spec.Description, + Description: api.FormatDescription(template, req.NamespacedName.String(), spec.Description), Site: spec.Site, Tenant: spec.Tenant, }, diff --git a/pkg/config/config.go b/pkg/config/config.go index 52f954f7..404f2dc1 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -38,6 +38,7 @@ type OperatorConfig struct { HttpsEnable bool `mapstructure:"HTTPS_ENABLE"` DebugEnable bool `mapstructure:"DEBUG_ENABLE"` NetboxRestorationHashFieldName string `mapstructure:"NETBOX_RESTORATION_HASH_FIELD_NAME"` + DescriptionFormat string `mapstructure:"DESCRIPTION_FORMAT"` } func (c *OperatorConfig) setDefaults() { @@ -47,6 +48,7 @@ func (c *OperatorConfig) setDefaults() { c.viper.SetDefault("HTTPS_ENABLE", true) c.viper.SetDefault("DEBUG_ENABLE", false) c.viper.SetDefault("NETBOX_RESTORATION_HASH_FIELD_NAME", "netboxOperatorRestorationHash") + c.viper.SetDefault("DESCRIPTION_FORMAT", "${name} ${description} ${warning}") } func (c *OperatorConfig) LoadCaCert() (cert []byte, err error) { diff --git a/pkg/netbox/api/helper.go b/pkg/netbox/api/helper.go index 01a97b9f..e00da055 100644 --- a/pkg/netbox/api/helper.go +++ b/pkg/netbox/api/helper.go @@ -114,3 +114,24 @@ func SetIpAddressMask(ip string, ipFamily int64) (string, error) { return "", errors.New("unknown IP family") } } + +// FormatDescription formats a description string using the configured format template. +// It supports substitution of the following placeholders: +// * ${name}: the name of the object (e.g., "test-namespace/test-resource-name") +// * ${description}: the description provided by the user in spec.description +// * ${warning}: the static warning comment provided by the operator +// +// The default format is "${name} ${description} ${warning}". +func FormatDescription(template, name, description string) string { + + // Replace placeholders + result := strings.ReplaceAll(template, "${name}", name) + result = strings.ReplaceAll(result, "${description}", description) + result = strings.ReplaceAll(result, "${warning}", warningComment) + + // Truncate to the max length that Netbox allows + if utf8.RuneCountInString(result) > maxAllowedDescriptionLength { + result = result[:maxAllowedDescriptionLength] + } + return result +} diff --git a/pkg/netbox/api/prefix.go b/pkg/netbox/api/prefix.go index 1c15830a..d0e77344 100644 --- a/pkg/netbox/api/prefix.go +++ b/pkg/netbox/api/prefix.go @@ -41,14 +41,13 @@ func (r *NetboxClient) ReserveOrUpdatePrefix(prefix *models.Prefix) (*netboxMode Prefix: &prefix.Prefix, Comments: prefix.Metadata.Comments + warningComment, CustomFields: prefix.Metadata.Custom, - Description: prefix.Metadata.Description + warningComment, + Description: prefix.Metadata.Description, Status: "active", } if prefix.Metadata != nil { desiredPrefix.CustomFields = prefix.Metadata.Custom desiredPrefix.Comments = prefix.Metadata.Comments + warningComment - desiredPrefix.Description = TruncateDescription(prefix.Metadata.Description) } if prefix.Metadata != nil && prefix.Metadata.Tenant != "" {