tag v0.155.0 Tagger: imagebuilder-bot <imagebuilder-bots+imagebuilder-bot@redhat.com> Changes with 0.155.0 ---------------- * Fedora 43: add shadow-utils when LockRoot is enabled, update cloud-init service name (osbuild/images#1618) * Author: Achilleas Koutsou, Reviewers: Gianluca Zuccarelli, Michael Vogt * Update osbuild dependency commit ID to latest (osbuild/images#1609) * Author: SchutzBot, Reviewers: Achilleas Koutsou, Simon de Vlieger, Tomáš Hozza * Update snapshots to 20250626 (osbuild/images#1623) * Author: SchutzBot, Reviewers: Achilleas Koutsou, Simon de Vlieger * distro/rhel9: xz compress azure-cvm image type [HMS-8587] (osbuild/images#1620) * Author: Achilleas Koutsou, Reviewers: Simon de Vlieger, Tomáš Hozza * distro/rhel: introduce new image type: Azure SAP Apps [HMS-8738] (osbuild/images#1612) * Author: Achilleas Koutsou, Reviewers: Simon de Vlieger, Tomáš Hozza * distro/rhel: move ansible-core to sap_extras_pkgset (osbuild/images#1624) * Author: Achilleas Koutsou, Reviewers: Brian C. Lane, Tomáš Hozza * github/create-tag: allow passing the version when run manually (osbuild/images#1621) * Author: Achilleas Koutsou, Reviewers: Lukáš Zapletal, Tomáš Hozza * rhel9: move image-config into pure YAML (HMS-8593) (osbuild/images#1616) * Author: Michael Vogt, Reviewers: Achilleas Koutsou, Simon de Vlieger * test: split manifest checksums into separate files (osbuild/images#1625) * Author: Achilleas Koutsou, Reviewers: Simon de Vlieger, Tomáš Hozza — Somewhere on the Internet, 2025-06-30 --- tag v0.156.0 Tagger: imagebuilder-bot <imagebuilder-bots+imagebuilder-bot@redhat.com> Changes with 0.156.0 ---------------- * Many: delete repositories for EOL distributions (HMS-7044) (osbuild/images#1607) * Author: Tomáš Hozza, Reviewers: Michael Vogt, Simon de Vlieger * RHSM/facts: add 'image-builder CLI' API type (osbuild/images#1640) * Author: Tomáš Hozza, Reviewers: Brian C. Lane, Simon de Vlieger * Update dependencies 2025-06-29 (osbuild/images#1628) * Author: SchutzBot, Reviewers: Simon de Vlieger, Tomáš Hozza * Update osbuild dependency commit ID to latest (osbuild/images#1627) * Author: SchutzBot, Reviewers: Simon de Vlieger, Tomáš Hozza * [RFC] image: drop `InstallWeakDeps` from image.DiskImage (osbuild/images#1642) * Author: Michael Vogt, Reviewers: Brian C. Lane, Simon de Vlieger, Tomáš Hozza * build(deps): bump the go-deps group across 1 directory with 3 updates (osbuild/images#1632) * Author: dependabot[bot], Reviewers: SchutzBot, Tomáš Hozza * distro/rhel10: xz compress azure-cvm image type (osbuild/images#1638) * Author: Achilleas Koutsou, Reviewers: Brian C. Lane, Simon de Vlieger * distro: cleanup/refactor distro/{defs,generic} (HMS-8744) (osbuild/images#1570) * Author: Michael Vogt, Reviewers: Simon de Vlieger, Tomáš Hozza * distro: remove some hardcoded values from generic/images.go (osbuild/images#1636) * Author: Michael Vogt, Reviewers: Simon de Vlieger, Tomáš Hozza * distro: small tweaks for the YAML based imagetypes (osbuild/images#1622) * Author: Michael Vogt, Reviewers: Brian C. Lane, Simon de Vlieger * fedora/wsl: packages and locale (osbuild/images#1635) * Author: Simon de Vlieger, Reviewers: Michael Vogt, Tomáš Hozza * image/many: make compression more generic (osbuild/images#1634) * Author: Simon de Vlieger, Reviewers: Brian C. Lane, Michael Vogt * manifest: handle content template name with spaces (osbuild/images#1641) * Author: Bryttanie, Reviewers: Brian C. Lane, Michael Vogt, Tomáš Hozza * many: implement gzip (osbuild/images#1633) * Author: Simon de Vlieger, Reviewers: Michael Vogt, Tomáš Hozza * rhel/azure: set GRUB_TERMINAL based on architecture [RHEL-91383] (osbuild/images#1626) * Author: Achilleas Koutsou, Reviewers: Simon de Vlieger, Tomáš Hozza — Somewhere on the Internet, 2025-07-07 ---
328 lines
12 KiB
Go
328 lines
12 KiB
Go
// © Broadcom. All Rights Reserved.
|
|
// The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package library
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"time"
|
|
|
|
"github.com/vmware/govmomi/object"
|
|
"github.com/vmware/govmomi/vapi/internal"
|
|
"github.com/vmware/govmomi/vapi/rest"
|
|
)
|
|
|
|
// StorageBacking defines a storage location where content in a library will be stored.
|
|
type StorageBacking struct {
|
|
DatastoreID string `json:"datastore_id,omitempty"`
|
|
Type string `json:"type,omitempty"`
|
|
StorageURI string `json:"storage_uri,omitempty"`
|
|
}
|
|
|
|
// Library provides methods to create, read, update, delete, and enumerate libraries.
|
|
type Library struct {
|
|
CreationTime *time.Time `json:"creation_time,omitempty"`
|
|
Description *string `json:"description,omitempty"`
|
|
ID string `json:"id,omitempty"`
|
|
LastModifiedTime *time.Time `json:"last_modified_time,omitempty"`
|
|
LastSyncTime *time.Time `json:"last_sync_time,omitempty"`
|
|
Name string `json:"name,omitempty"`
|
|
Storage []StorageBacking `json:"storage_backings,omitempty"`
|
|
Type string `json:"type,omitempty"`
|
|
Version string `json:"version,omitempty"`
|
|
Subscription *Subscription `json:"subscription_info,omitempty"`
|
|
Publication *Publication `json:"publish_info,omitempty"`
|
|
SecurityPolicyID string `json:"security_policy_id,omitempty"`
|
|
UnsetSecurityPolicyID bool `json:"unset_security_policy_id,omitempty"`
|
|
ServerGUID string `json:"server_guid,omitempty"`
|
|
StateInfo *StateInfo `json:"state_info,omitempty"`
|
|
}
|
|
|
|
// StateInfo provides the state info of a content library.
|
|
type StateInfo struct {
|
|
State string `json:"state"`
|
|
}
|
|
|
|
// Subscription info
|
|
type Subscription struct {
|
|
AuthenticationMethod string `json:"authentication_method"`
|
|
AutomaticSyncEnabled *bool `json:"automatic_sync_enabled,omitempty"`
|
|
OnDemand *bool `json:"on_demand,omitempty"`
|
|
Password string `json:"password,omitempty"`
|
|
SslThumbprint string `json:"ssl_thumbprint,omitempty"`
|
|
SubscriptionURL string `json:"subscription_url,omitempty"`
|
|
UserName string `json:"user_name,omitempty"`
|
|
}
|
|
|
|
// Publication info
|
|
type Publication struct {
|
|
AuthenticationMethod string `json:"authentication_method"`
|
|
UserName string `json:"user_name,omitempty"`
|
|
Password string `json:"password,omitempty"`
|
|
CurrentPassword string `json:"current_password,omitempty"`
|
|
PersistJSON *bool `json:"persist_json_enabled,omitempty"`
|
|
Published *bool `json:"published,omitempty"`
|
|
PublishURL string `json:"publish_url,omitempty"`
|
|
}
|
|
|
|
// SubscriberSummary as returned by ListSubscribers
|
|
type SubscriberSummary struct {
|
|
LibraryID string `json:"subscribed_library"`
|
|
LibraryName string `json:"subscribed_library_name"`
|
|
SubscriptionID string `json:"subscription"`
|
|
LibraryVcenterHostname string `json:"subscribed_library_vcenter_hostname,omitempty"`
|
|
}
|
|
|
|
// Placement information used to place a virtual machine template
|
|
type Placement struct {
|
|
ResourcePool string `json:"resource_pool,omitempty"`
|
|
Host string `json:"host,omitempty"`
|
|
Folder string `json:"folder,omitempty"`
|
|
Cluster string `json:"cluster,omitempty"`
|
|
Network string `json:"network,omitempty"`
|
|
}
|
|
|
|
// Vcenter contains information about the vCenter Server instance where a subscribed library associated with a subscription exists.
|
|
type Vcenter struct {
|
|
Hostname string `json:"hostname"`
|
|
Port int `json:"https_port,omitempty"`
|
|
ServerGUID string `json:"server_guid"`
|
|
}
|
|
|
|
// Subscriber contains the detailed info for a library subscriber.
|
|
type Subscriber struct {
|
|
LibraryID string `json:"subscribed_library"`
|
|
LibraryName string `json:"subscribed_library_name"`
|
|
LibraryLocation string `json:"subscribed_library_location"`
|
|
Placement *Placement `json:"subscribed_library_placement,omitempty"`
|
|
Vcenter *Vcenter `json:"subscribed_library_vcenter,omitempty"`
|
|
}
|
|
|
|
// SubscriberLibrary is the specification for a subscribed library to be associated with a subscription.
|
|
type SubscriberLibrary struct {
|
|
Target string `json:"target"`
|
|
LibraryID string `json:"subscribed_library,omitempty"`
|
|
Location string `json:"location"`
|
|
Vcenter *Vcenter `json:"vcenter,omitempty"`
|
|
Placement *Placement `json:"placement,omitempty"`
|
|
}
|
|
|
|
// Patch merges updates from the given src.
|
|
func (l *Library) Patch(src *Library) {
|
|
if src.Name != "" {
|
|
l.Name = src.Name
|
|
}
|
|
if src.Description != nil {
|
|
l.Description = src.Description
|
|
}
|
|
if src.Version != "" {
|
|
l.Version = src.Version
|
|
}
|
|
}
|
|
|
|
// Manager extends rest.Client, adding content library related methods.
|
|
type Manager struct {
|
|
*rest.Client
|
|
}
|
|
|
|
// NewManager creates a new Manager instance with the given client.
|
|
func NewManager(client *rest.Client) *Manager {
|
|
return &Manager{
|
|
Client: client,
|
|
}
|
|
}
|
|
|
|
// Find is the search criteria for finding libraries.
|
|
type Find struct {
|
|
Name string `json:"name,omitempty"`
|
|
Type string `json:"type,omitempty"`
|
|
}
|
|
|
|
// FindLibrary returns one or more libraries that match the provided search
|
|
// criteria.
|
|
//
|
|
// The provided name is case-insensitive.
|
|
//
|
|
// Either the name or type of library may be set to empty values in order
|
|
// to search for all libraries, all libraries with a specific name, regardless
|
|
// of type, or all libraries of a specified type.
|
|
func (c *Manager) FindLibrary(ctx context.Context, search Find) ([]string, error) {
|
|
url := c.Resource(internal.LibraryPath).WithAction("find")
|
|
spec := struct {
|
|
Spec Find `json:"spec"`
|
|
}{search}
|
|
var res []string
|
|
return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
|
}
|
|
|
|
// CreateLibrary creates a new library with the given Type, Name,
|
|
// Description, and CategoryID.
|
|
func (c *Manager) CreateLibrary(ctx context.Context, library Library) (string, error) {
|
|
spec := struct {
|
|
Library Library `json:"create_spec"`
|
|
}{library}
|
|
path := internal.LocalLibraryPath
|
|
if library.Type == "SUBSCRIBED" {
|
|
path = internal.SubscribedLibraryPath
|
|
sub := library.Subscription
|
|
u, err := url.Parse(sub.SubscriptionURL)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if u.Scheme == "https" && sub.SslThumbprint == "" {
|
|
thumbprint := c.Thumbprint(u.Host)
|
|
if thumbprint == "" {
|
|
t := c.DefaultTransport()
|
|
if t.TLSClientConfig.InsecureSkipVerify {
|
|
var info object.HostCertificateInfo
|
|
_ = info.FromURL(u, t.TLSClientConfig)
|
|
thumbprint = info.ThumbprintSHA1
|
|
}
|
|
sub.SslThumbprint = thumbprint
|
|
}
|
|
}
|
|
}
|
|
url := c.Resource(path)
|
|
var res string
|
|
return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
|
}
|
|
|
|
// SyncLibrary syncs a subscribed library.
|
|
func (c *Manager) SyncLibrary(ctx context.Context, library *Library) error {
|
|
path := internal.SubscribedLibraryPath
|
|
url := c.Resource(path).WithID(library.ID).WithAction("sync")
|
|
return c.Do(ctx, url.Request(http.MethodPost), nil)
|
|
}
|
|
|
|
// PublishLibrary publishes the library to specified subscriptions.
|
|
// If no subscriptions are specified, then publishes the library to all subscriptions.
|
|
func (c *Manager) PublishLibrary(ctx context.Context, library *Library, subscriptions []string) error {
|
|
path := internal.LocalLibraryPath
|
|
var spec internal.SubscriptionDestinationSpec
|
|
for i := range subscriptions {
|
|
spec.Subscriptions = append(spec.Subscriptions, internal.SubscriptionDestination{ID: subscriptions[i]})
|
|
}
|
|
url := c.Resource(path).WithID(library.ID).WithAction("publish")
|
|
return c.Do(ctx, url.Request(http.MethodPost, spec), nil)
|
|
}
|
|
|
|
// UpdateLibrary can update one or both of the tag Description and Name fields.
|
|
func (c *Manager) UpdateLibrary(ctx context.Context, l *Library) error {
|
|
spec := struct {
|
|
Library `json:"update_spec"`
|
|
}{
|
|
Library{
|
|
Name: l.Name,
|
|
Description: l.Description,
|
|
},
|
|
}
|
|
url := c.Resource(internal.LibraryPath).WithID(l.ID)
|
|
return c.Do(ctx, url.Request(http.MethodPatch, spec), nil)
|
|
}
|
|
|
|
// DeleteLibrary deletes an existing library.
|
|
func (c *Manager) DeleteLibrary(ctx context.Context, library *Library) error {
|
|
path := internal.LocalLibraryPath
|
|
if library.Type == "SUBSCRIBED" {
|
|
path = internal.SubscribedLibraryPath
|
|
}
|
|
url := c.Resource(path).WithID(library.ID)
|
|
return c.Do(ctx, url.Request(http.MethodDelete), nil)
|
|
}
|
|
|
|
// ListLibraries returns a list of all content library IDs in the system.
|
|
func (c *Manager) ListLibraries(ctx context.Context) ([]string, error) {
|
|
url := c.Resource(internal.LibraryPath)
|
|
var res []string
|
|
return res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
|
}
|
|
|
|
// GetLibraryByID returns information on a library for the given ID.
|
|
func (c *Manager) GetLibraryByID(ctx context.Context, id string) (*Library, error) {
|
|
url := c.Resource(internal.LibraryPath).WithID(id)
|
|
var res Library
|
|
return &res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
|
}
|
|
|
|
// GetLibraryByName returns information on a library for the given name.
|
|
func (c *Manager) GetLibraryByName(ctx context.Context, name string) (*Library, error) {
|
|
// Lookup by name
|
|
libraries, err := c.GetLibraries(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for i := range libraries {
|
|
if libraries[i].Name == name {
|
|
return &libraries[i], nil
|
|
}
|
|
}
|
|
return nil, fmt.Errorf("library name (%s) not found", name)
|
|
}
|
|
|
|
// GetLibraries returns a list of all content library details in the system.
|
|
func (c *Manager) GetLibraries(ctx context.Context) ([]Library, error) {
|
|
ids, err := c.ListLibraries(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("get libraries failed for: %s", err)
|
|
}
|
|
|
|
var libraries []Library
|
|
for _, id := range ids {
|
|
library, err := c.GetLibraryByID(ctx, id)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("get library %s failed for %s", id, err)
|
|
}
|
|
|
|
libraries = append(libraries, *library)
|
|
|
|
}
|
|
return libraries, nil
|
|
}
|
|
|
|
// ListSubscribers lists the subscriptions of the published library.
|
|
func (c *Manager) ListSubscribers(ctx context.Context, library *Library) ([]SubscriberSummary, error) {
|
|
url := c.Resource(internal.Subscriptions).WithParam("library", library.ID)
|
|
var res []SubscriberSummary
|
|
return res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
|
}
|
|
|
|
// CreateSubscriber creates a subscription of the published library.
|
|
func (c *Manager) CreateSubscriber(ctx context.Context, library *Library, s SubscriberLibrary) (string, error) {
|
|
var spec struct {
|
|
Sub struct {
|
|
SubscriberLibrary SubscriberLibrary `json:"subscribed_library"`
|
|
} `json:"spec"`
|
|
}
|
|
spec.Sub.SubscriberLibrary = s
|
|
url := c.Resource(internal.Subscriptions).WithID(library.ID)
|
|
var res string
|
|
return res, c.Do(ctx, url.Request(http.MethodPost, &spec), &res)
|
|
}
|
|
|
|
// GetSubscriber returns information about the specified subscriber of the published library.
|
|
func (c *Manager) GetSubscriber(ctx context.Context, library *Library, subscriber string) (*Subscriber, error) {
|
|
id := internal.SubscriptionDestination{ID: subscriber}
|
|
url := c.Resource(internal.Subscriptions).WithID(library.ID).WithAction("get")
|
|
var res Subscriber
|
|
return &res, c.Do(ctx, url.Request(http.MethodPost, &id), &res)
|
|
}
|
|
|
|
// DeleteSubscriber deletes the specified subscription of the published library.
|
|
// The subscribed library associated with the subscription will not be deleted.
|
|
func (c *Manager) DeleteSubscriber(ctx context.Context, library *Library, subscriber string) error {
|
|
id := internal.SubscriptionDestination{ID: subscriber}
|
|
url := c.Resource(internal.Subscriptions).WithID(library.ID).WithAction("delete")
|
|
return c.Do(ctx, url.Request(http.MethodPost, &id), nil)
|
|
}
|
|
|
|
// EvictSubscribedLibrary evicts the cached content of an on-demand subscribed library.
|
|
// This operation allows the cached content of a subscribed library to be removed to free up storage capacity.
|
|
func (c *Manager) EvictSubscribedLibrary(ctx context.Context, library *Library) error {
|
|
path := internal.SubscribedLibraryPath
|
|
url := c.Resource(path).WithID(library.ID).WithAction("evict")
|
|
return c.Do(ctx, url.Request(http.MethodPost), nil)
|
|
}
|