github.com/arunkumar7540/cli@v6.45.0+incompatible/api/cloudcontroller/ccv3/droplet.go (about)

     1  package ccv3
     2  
     3  import (
     4  	"bytes"
     5  	"code.cloudfoundry.org/cli/api/cloudcontroller"
     6  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
     7  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant"
     8  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/internal"
     9  	"code.cloudfoundry.org/cli/api/cloudcontroller/uploads"
    10  	"encoding/json"
    11  	"io"
    12  )
    13  
    14  // Droplet represents a Cloud Controller droplet's metadata. A droplet is a set of
    15  // compiled bits for a given application.
    16  type Droplet struct {
    17  	//Buildpacks are the detected buildpacks from the staging process.
    18  	Buildpacks []DropletBuildpack `json:"buildpacks,omitempty"`
    19  	// CreatedAt is the timestamp that the Cloud Controller created the droplet.
    20  	CreatedAt string `json:"created_at"`
    21  	// GUID is the unique droplet identifier.
    22  	GUID string `json:"guid"`
    23  	// Image is the Docker image name.
    24  	Image string `json:"image"`
    25  	// Stack is the root filesystem to use with the buildpack.
    26  	Stack string `json:"stack,omitempty"`
    27  	// State is the current state of the droplet.
    28  	State constant.DropletState `json:"state"`
    29  }
    30  
    31  // DropletBuildpack is the name and output of a buildpack used to create a
    32  // droplet.
    33  type DropletBuildpack struct {
    34  	// Name is the buildpack name.
    35  	Name string `json:"name"`
    36  	//DetectOutput is the output during buildpack detect process.
    37  	DetectOutput string `json:"detect_output"`
    38  }
    39  
    40  type DropletCreateRequest struct {
    41  	Relationships Relationships `json:"relationships"`
    42  }
    43  
    44  // CreateDroplet creates a new droplet without a package for the app with
    45  // the given guid.
    46  func (client *Client) CreateDroplet(appGUID string) (Droplet, Warnings, error) {
    47  	requestBody := DropletCreateRequest{
    48  		Relationships: Relationships{
    49  			constant.RelationshipTypeApplication: Relationship{GUID: appGUID},
    50  		},
    51  	}
    52  
    53  	body, marshalErr := json.Marshal(requestBody)
    54  	if marshalErr != nil {
    55  		return Droplet{}, nil, marshalErr
    56  	}
    57  
    58  	request, createRequestErr := client.newHTTPRequest(requestOptions{
    59  		RequestName: internal.PostDropletRequest,
    60  		Body:        bytes.NewReader(body),
    61  	})
    62  	if createRequestErr != nil {
    63  		return Droplet{}, nil, createRequestErr
    64  	}
    65  
    66  	var responseDroplet Droplet
    67  	response := cloudcontroller.Response{
    68  		DecodeJSONResponseInto: &responseDroplet,
    69  	}
    70  	err := client.connection.Make(request, &response)
    71  
    72  	return responseDroplet, response.Warnings, err
    73  }
    74  
    75  // GetApplicationDropletCurrent returns the current droplet for a given
    76  // application.
    77  func (client *Client) GetApplicationDropletCurrent(appGUID string) (Droplet, Warnings, error) {
    78  	request, err := client.newHTTPRequest(requestOptions{
    79  		RequestName: internal.GetApplicationDropletCurrentRequest,
    80  		URIParams:   map[string]string{"app_guid": appGUID},
    81  	})
    82  	if err != nil {
    83  		return Droplet{}, nil, err
    84  	}
    85  
    86  	var responseDroplet Droplet
    87  	response := cloudcontroller.Response{
    88  		DecodeJSONResponseInto: &responseDroplet,
    89  	}
    90  	err = client.connection.Make(request, &response)
    91  	return responseDroplet, response.Warnings, err
    92  }
    93  
    94  // GetDroplet returns a droplet with the given GUID.
    95  func (client *Client) GetDroplet(dropletGUID string) (Droplet, Warnings, error) {
    96  	request, err := client.newHTTPRequest(requestOptions{
    97  		RequestName: internal.GetDropletRequest,
    98  		URIParams:   map[string]string{"droplet_guid": dropletGUID},
    99  	})
   100  	if err != nil {
   101  		return Droplet{}, nil, err
   102  	}
   103  
   104  	var responseDroplet Droplet
   105  	response := cloudcontroller.Response{
   106  		DecodeJSONResponseInto: &responseDroplet,
   107  	}
   108  	err = client.connection.Make(request, &response)
   109  
   110  	return responseDroplet, response.Warnings, err
   111  }
   112  
   113  // GetDroplets lists droplets with optional filters.
   114  func (client *Client) GetDroplets(query ...Query) ([]Droplet, Warnings, error) {
   115  	request, err := client.newHTTPRequest(requestOptions{
   116  		RequestName: internal.GetDropletsRequest,
   117  		Query:       query,
   118  	})
   119  	if err != nil {
   120  		return nil, nil, err
   121  	}
   122  
   123  	var responseDroplets []Droplet
   124  	warnings, err := client.paginate(request, Droplet{}, func(item interface{}) error {
   125  		if droplet, ok := item.(Droplet); ok {
   126  			responseDroplets = append(responseDroplets, droplet)
   127  		} else {
   128  			return ccerror.UnknownObjectInListError{
   129  				Expected:   Droplet{},
   130  				Unexpected: item,
   131  			}
   132  		}
   133  		return nil
   134  	})
   135  
   136  	return responseDroplets, warnings, err
   137  }
   138  
   139  // UploadDropletBits asynchronously uploads bits from a .tgz file located at dropletPath to the
   140  // droplet with guid dropletGUID. It returns a job URL pointing to the asynchronous upload job.
   141  func (client *Client) UploadDropletBits(dropletGUID string, dropletPath string, droplet io.Reader, dropletLength int64) (JobURL, Warnings, error) {
   142  	contentLength, err := uploads.CalculateRequestSize(dropletLength, dropletPath, "bits")
   143  	if err != nil {
   144  		return "", nil, err
   145  	}
   146  
   147  	contentType, body, writeErrors := uploads.CreateMultipartBodyAndHeader(droplet, dropletPath, "bits")
   148  
   149  	request, err := client.newHTTPRequest(requestOptions{
   150  		RequestName: internal.PostDropletBitsRequest,
   151  		URIParams:   internal.Params{"droplet_guid": dropletGUID},
   152  		Body:        body,
   153  	})
   154  	if err != nil {
   155  		return "", nil, err
   156  	}
   157  
   158  	request.ContentLength = contentLength
   159  	request.Header.Set("Content-Type", contentType)
   160  
   161  	jobURL, warnings, err := client.uploadDropletAsynchronously(request, writeErrors)
   162  	if err != nil {
   163  		return "", warnings, err
   164  	}
   165  
   166  	return jobURL, warnings, nil
   167  }
   168  
   169  func (client *Client) uploadDropletAsynchronously(request *cloudcontroller.Request, writeErrors <-chan error) (JobURL, Warnings, error) {
   170  	var droplet Droplet
   171  	response := cloudcontroller.Response{
   172  		DecodeJSONResponseInto: &droplet,
   173  	}
   174  
   175  	httpErrors := make(chan error)
   176  
   177  	go func() {
   178  		defer close(httpErrors)
   179  
   180  		err := client.connection.Make(request, &response)
   181  		if err != nil {
   182  			httpErrors <- err
   183  		}
   184  	}()
   185  
   186  	// The following section makes the following assumptions:
   187  	// 1) If an error occurs during file reading, an EOF is sent to the request
   188  	// object. Thus ending the request transfer.
   189  	// 2) If an error occurs during request transfer, an EOF is sent to the pipe.
   190  	// Thus ending the writing routine.
   191  	var firstError error
   192  	var writeClosed, httpClosed bool
   193  
   194  	for {
   195  		select {
   196  		case writeErr, ok := <-writeErrors:
   197  			if !ok {
   198  				writeClosed = true
   199  				break // for select
   200  			}
   201  			if firstError == nil {
   202  				firstError = writeErr
   203  			}
   204  		case httpErr, ok := <-httpErrors:
   205  			if !ok {
   206  				httpClosed = true
   207  				break // for select
   208  			}
   209  			if firstError == nil {
   210  				firstError = httpErr
   211  			}
   212  		}
   213  
   214  		if writeClosed && httpClosed {
   215  			break // for for
   216  		}
   217  	}
   218  
   219  	return JobURL(response.ResourceLocationURL), response.Warnings, firstError
   220  }