github.com/vmware/govmomi@v0.37.2/vapi/library/library_item_updatesession_file.go (about)

     1  /*
     2  Copyright (c) 2019-2024 VMware, Inc. All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package library
    18  
    19  import (
    20  	"bufio"
    21  	"context"
    22  	"fmt"
    23  	"io"
    24  	"net/http"
    25  	"strings"
    26  
    27  	"github.com/vmware/govmomi/vapi/internal"
    28  	"github.com/vmware/govmomi/vapi/rest"
    29  	"github.com/vmware/govmomi/vim25/soap"
    30  )
    31  
    32  // TransferEndpoint provides information on the source of a library item file.
    33  type TransferEndpoint struct {
    34  	URI                      string `json:"uri,omitempty"`
    35  	SSLCertificate           string `json:"ssl_certificate,omitempty"`
    36  	SSLCertificateThumbprint string `json:"ssl_certificate_thumbprint,omitempty"`
    37  }
    38  
    39  type ProbeResult struct {
    40  	Status         string                    `json:"status"`
    41  	SSLThumbprint  string                    `json:"ssl_thumbprint,omitempty"`
    42  	SSLCertificate string                    `json:"ssl_certificate,omitempty"`
    43  	ErrorMessages  []rest.LocalizableMessage `json:"error_messages,omitempty"`
    44  }
    45  
    46  // UpdateFile is the specification for the updatesession
    47  // operations file:add, file:get, and file:list.
    48  type UpdateFile struct {
    49  	BytesTransferred int64                    `json:"bytes_transferred,omitempty"`
    50  	Checksum         *Checksum                `json:"checksum_info,omitempty"`
    51  	ErrorMessage     *rest.LocalizableMessage `json:"error_message,omitempty"`
    52  	Name             string                   `json:"name"`
    53  	Size             int64                    `json:"size,omitempty"`
    54  	SourceEndpoint   *TransferEndpoint        `json:"source_endpoint,omitempty"`
    55  	SourceType       string                   `json:"source_type"`
    56  	Status           string                   `json:"status,omitempty"`
    57  	UploadEndpoint   *TransferEndpoint        `json:"upload_endpoint,omitempty"`
    58  }
    59  
    60  // FileValidationError contains the validation error of a file in the update session
    61  type FileValidationError struct {
    62  	Name         string                  `json:"name"`
    63  	ErrorMessage rest.LocalizableMessage `json:"error_message"`
    64  }
    65  
    66  // UpdateFileValidation contains the result of validating the files in the update session
    67  type UpdateFileValidation struct {
    68  	HasErrors    bool                  `json:"has_errors"`
    69  	MissingFiles []string              `json:"missing_files,omitempty"`
    70  	InvalidFiles []FileValidationError `json:"invalid_files,omitempty"`
    71  }
    72  
    73  // AddLibraryItemFile adds a file
    74  func (c *Manager) AddLibraryItemFile(ctx context.Context, sessionID string, updateFile UpdateFile) (*UpdateFile, error) {
    75  	url := c.Resource(internal.LibraryItemUpdateSessionFile).WithID(sessionID).WithAction("add")
    76  	spec := struct {
    77  		FileSpec UpdateFile `json:"file_spec"`
    78  	}{updateFile}
    79  	var res UpdateFile
    80  	err := c.Do(ctx, url.Request(http.MethodPost, spec), &res)
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  	if res.Status == "ERROR" {
    85  		return nil, res.ErrorMessage
    86  	}
    87  	return &res, nil
    88  }
    89  
    90  // AddLibraryItemFileFromURI adds a file from a remote URI.
    91  func (c *Manager) AddLibraryItemFileFromURI(ctx context.Context, sessionID, name, uri string, checksum ...Checksum) (*UpdateFile, error) {
    92  	source := &TransferEndpoint{
    93  		URI: uri,
    94  	}
    95  
    96  	file := UpdateFile{
    97  		Name:           name,
    98  		SourceType:     "PULL",
    99  		SourceEndpoint: source,
   100  	}
   101  
   102  	if len(checksum) == 1 && checksum[0].Checksum != "" {
   103  		file.Checksum = &checksum[0]
   104  	} else if len(checksum) > 1 {
   105  		return nil, fmt.Errorf("expected 0 or 1 checksum, got %d", len(checksum))
   106  	}
   107  
   108  	if res, err := c.Head(uri); err == nil {
   109  		file.Size = res.ContentLength
   110  		if res.TLS != nil {
   111  			source.SSLCertificateThumbprint = soap.ThumbprintSHA1(res.TLS.PeerCertificates[0])
   112  		}
   113  	} else {
   114  		res, err := c.ProbeTransferEndpoint(ctx, *source)
   115  		if err != nil {
   116  			return nil, err
   117  		}
   118  		if res.SSLCertificate != "" {
   119  			source.SSLCertificate = res.SSLCertificate
   120  		} else {
   121  			source.SSLCertificateThumbprint = res.SSLThumbprint
   122  		}
   123  	}
   124  
   125  	return c.AddLibraryItemFile(ctx, sessionID, file)
   126  }
   127  
   128  // GetLibraryItemUpdateSessionFile retrieves information about a specific file
   129  // that is a part of an update session.
   130  func (c *Manager) GetLibraryItemUpdateSessionFile(ctx context.Context, sessionID string, fileName string) (*UpdateFile, error) {
   131  	url := c.Resource(internal.LibraryItemUpdateSessionFile).WithID(sessionID).WithAction("get")
   132  	spec := struct {
   133  		Name string `json:"file_name"`
   134  	}{fileName}
   135  	var res UpdateFile
   136  	return &res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
   137  }
   138  
   139  // ListLibraryItemUpdateSessionFile lists all files in the library item associated with the update session
   140  func (c *Manager) ListLibraryItemUpdateSessionFile(ctx context.Context, sessionID string) ([]UpdateFile, error) {
   141  	url := c.Resource(internal.LibraryItemUpdateSessionFile).WithParam("update_session_id", sessionID)
   142  	var res []UpdateFile
   143  	return res, c.Do(ctx, url.Request(http.MethodGet), &res)
   144  }
   145  
   146  // ValidateLibraryItemUpdateSessionFile validates all files in the library item associated with the update session
   147  func (c *Manager) ValidateLibraryItemUpdateSessionFile(ctx context.Context, sessionID string) (*UpdateFileValidation, error) {
   148  	url := c.Resource(internal.LibraryItemUpdateSessionFile).WithID(sessionID).WithAction("validate")
   149  	var res UpdateFileValidation
   150  	return &res, c.Do(ctx, url.Request(http.MethodPost), &res)
   151  }
   152  
   153  // RemoveLibraryItemUpdateSessionFile requests a file to be removed. The file will only be effectively removed when the update session is completed.
   154  func (c *Manager) RemoveLibraryItemUpdateSessionFile(ctx context.Context, sessionID string, fileName string) error {
   155  	url := c.Resource(internal.LibraryItemUpdateSessionFile).WithID(sessionID).WithAction("remove")
   156  	spec := struct {
   157  		Name string `json:"file_name"`
   158  	}{fileName}
   159  	return c.Do(ctx, url.Request(http.MethodPost, spec), nil)
   160  }
   161  
   162  func (c *Manager) ProbeTransferEndpoint(ctx context.Context, endpoint TransferEndpoint) (*ProbeResult, error) {
   163  	url := c.Resource(internal.LibraryItemUpdateSessionFile).WithAction("probe")
   164  	spec := struct {
   165  		SourceEndpoint TransferEndpoint `json:"source_endpoint"`
   166  	}{endpoint}
   167  	var res ProbeResult
   168  	return &res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
   169  }
   170  
   171  // ReadManifest converts an ovf manifest to a map of file name -> Checksum.
   172  func ReadManifest(m io.Reader) (map[string]*Checksum, error) {
   173  	// expected format: openssl sha1 *.{ovf,vmdk}
   174  	c := make(map[string]*Checksum)
   175  
   176  	scanner := bufio.NewScanner(m)
   177  	for scanner.Scan() {
   178  		line := strings.SplitN(scanner.Text(), ")=", 2)
   179  		if len(line) != 2 {
   180  			continue
   181  		}
   182  		name := strings.SplitN(line[0], "(", 2)
   183  		if len(name) != 2 {
   184  			continue
   185  		}
   186  		sum := &Checksum{
   187  			Algorithm: strings.TrimSpace(name[0]),
   188  			Checksum:  strings.TrimSpace(line[1]),
   189  		}
   190  		c[name[1]] = sum
   191  	}
   192  
   193  	return c, scanner.Err()
   194  }