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

     1  package ccv3
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"io"
     7  
     8  	"code.cloudfoundry.org/cli/api/cloudcontroller"
     9  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
    10  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/internal"
    11  	"code.cloudfoundry.org/cli/api/cloudcontroller/uploads"
    12  	"code.cloudfoundry.org/cli/types"
    13  )
    14  
    15  // Buildpack represents a Cloud Controller V3 buildpack.
    16  type Buildpack struct {
    17  	// Enabled is true when the buildpack can be used for staging.
    18  	Enabled types.NullBool
    19  	// Filename is the uploaded filename of the buildpack.
    20  	Filename string
    21  	// GUID is the unique identifier for the buildpack.
    22  	GUID string
    23  	// Locked is true when the buildpack cannot be updated.
    24  	Locked types.NullBool
    25  	// Name is the name of the buildpack. To be used by app buildpack field.
    26  	// (only alphanumeric characters)
    27  	Name string
    28  	// Position is the order in which the buildpacks are checked during buildpack
    29  	// auto-detection.
    30  	Position types.NullInt
    31  	// Stack is the name of the stack that the buildpack will use.
    32  	Stack string
    33  	// State is the current state of the buildpack.
    34  	State string
    35  	// Links are links to related resources.
    36  	Links APILinks
    37  }
    38  
    39  // MarshalJSON converts a Package into a Cloud Controller Package.
    40  func (buildpack Buildpack) MarshalJSON() ([]byte, error) {
    41  	ccBuildpack := struct {
    42  		Name     string `json:"name,omitempty"`
    43  		Stack    string `json:"stack,omitempty"`
    44  		Position *int   `json:"position,omitempty"`
    45  		Enabled  *bool  `json:"enabled,omitempty"`
    46  		Locked   *bool  `json:"locked,omitempty"`
    47  	}{
    48  		Name:  buildpack.Name,
    49  		Stack: buildpack.Stack,
    50  	}
    51  
    52  	if buildpack.Position.IsSet {
    53  		ccBuildpack.Position = &buildpack.Position.Value
    54  	}
    55  	if buildpack.Enabled.IsSet {
    56  		ccBuildpack.Enabled = &buildpack.Enabled.Value
    57  	}
    58  	if buildpack.Locked.IsSet {
    59  		ccBuildpack.Locked = &buildpack.Locked.Value
    60  	}
    61  
    62  	return json.Marshal(ccBuildpack)
    63  }
    64  
    65  func (buildpack *Buildpack) UnmarshalJSON(data []byte) error {
    66  	var ccBuildpack struct {
    67  		GUID     string         `json:"guid,omitempty"`
    68  		Links    APILinks       `json:"links,omitempty"`
    69  		Name     string         `json:"name,omitempty"`
    70  		Filename string         `json:"filename,omitempty"`
    71  		Stack    string         `json:"stack,omitempty"`
    72  		State    string         `json:"state,omitempty"`
    73  		Enabled  types.NullBool `json:"enabled"`
    74  		Locked   types.NullBool `json:"locked"`
    75  		Position types.NullInt  `json:"position"`
    76  	}
    77  
    78  	err := cloudcontroller.DecodeJSON(data, &ccBuildpack)
    79  	if err != nil {
    80  		return err
    81  	}
    82  
    83  	buildpack.Enabled = ccBuildpack.Enabled
    84  	buildpack.Filename = ccBuildpack.Filename
    85  	buildpack.GUID = ccBuildpack.GUID
    86  	buildpack.Locked = ccBuildpack.Locked
    87  	buildpack.Name = ccBuildpack.Name
    88  	buildpack.Position = ccBuildpack.Position
    89  	buildpack.Stack = ccBuildpack.Stack
    90  	buildpack.State = ccBuildpack.State
    91  	buildpack.Links = ccBuildpack.Links
    92  
    93  	return nil
    94  }
    95  
    96  // CreateBuildpack creates a buildpack with the given settings, Type and the
    97  // ApplicationRelationship must be set.
    98  func (client *Client) CreateBuildpack(bp Buildpack) (Buildpack, Warnings, error) {
    99  	bodyBytes, err := json.Marshal(bp)
   100  	if err != nil {
   101  		return Buildpack{}, nil, err
   102  	}
   103  
   104  	request, err := client.newHTTPRequest(requestOptions{
   105  		RequestName: internal.PostBuildpackRequest,
   106  		Body:        bytes.NewReader(bodyBytes),
   107  	})
   108  	if err != nil {
   109  		return Buildpack{}, nil, err
   110  	}
   111  
   112  	var responseBuildpack Buildpack
   113  	response := cloudcontroller.Response{
   114  		DecodeJSONResponseInto: &responseBuildpack,
   115  	}
   116  	err = client.connection.Make(request, &response)
   117  
   118  	return responseBuildpack, response.Warnings, err
   119  }
   120  
   121  // DeleteBuildpack deletes the buildpack with the provided guid.
   122  func (client Client) DeleteBuildpack(buildpackGUID string) (JobURL, Warnings, error) {
   123  	request, err := client.newHTTPRequest(requestOptions{
   124  		RequestName: internal.DeleteBuildpackRequest,
   125  		URIParams: map[string]string{
   126  			"buildpack_guid": buildpackGUID,
   127  		},
   128  	})
   129  	if err != nil {
   130  		return "", nil, err
   131  	}
   132  
   133  	response := cloudcontroller.Response{}
   134  	err = client.connection.Make(request, &response)
   135  
   136  	return JobURL(response.ResourceLocationURL), response.Warnings, err
   137  }
   138  
   139  // GetBuildpacks lists buildpacks with optional filters.
   140  func (client *Client) GetBuildpacks(query ...Query) ([]Buildpack, Warnings, error) {
   141  	request, err := client.newHTTPRequest(requestOptions{
   142  		RequestName: internal.GetBuildpacksRequest,
   143  		Query:       query,
   144  	})
   145  	if err != nil {
   146  		return nil, nil, err
   147  	}
   148  
   149  	var fullBuildpacksList []Buildpack
   150  	warnings, err := client.paginate(request, Buildpack{}, func(item interface{}) error {
   151  		if buildpack, ok := item.(Buildpack); ok {
   152  			fullBuildpacksList = append(fullBuildpacksList, buildpack)
   153  		} else {
   154  			return ccerror.UnknownObjectInListError{
   155  				Expected:   Buildpack{},
   156  				Unexpected: item,
   157  			}
   158  		}
   159  		return nil
   160  	})
   161  
   162  	return fullBuildpacksList, warnings, err
   163  }
   164  
   165  func (client Client) UpdateBuildpack(buildpack Buildpack) (Buildpack, Warnings, error) {
   166  	bodyBytes, err := json.Marshal(buildpack)
   167  	if err != nil {
   168  		return Buildpack{}, nil, err
   169  	}
   170  
   171  	request, err := client.newHTTPRequest(requestOptions{
   172  		RequestName: internal.PatchBuildpackRequest,
   173  		Body:        bytes.NewReader(bodyBytes),
   174  		URIParams:   map[string]string{"buildpack_guid": buildpack.GUID},
   175  	})
   176  	if err != nil {
   177  		return Buildpack{}, nil, err
   178  	}
   179  
   180  	var responseBuildpack Buildpack
   181  	response := cloudcontroller.Response{
   182  		DecodeJSONResponseInto: &responseBuildpack,
   183  	}
   184  	err = client.connection.Make(request, &response)
   185  
   186  	return responseBuildpack, response.Warnings, err
   187  }
   188  
   189  // UploadBuildpack uploads the contents of a buildpack zip to the server.
   190  func (client *Client) UploadBuildpack(buildpackGUID string, buildpackPath string, buildpack io.Reader, buildpackLength int64) (JobURL, Warnings, error) {
   191  
   192  	contentLength, err := uploads.CalculateRequestSize(buildpackLength, buildpackPath, "bits")
   193  	if err != nil {
   194  		return "", nil, err
   195  	}
   196  
   197  	contentType, body, writeErrors := uploads.CreateMultipartBodyAndHeader(buildpack, buildpackPath, "bits")
   198  
   199  	request, err := client.newHTTPRequest(requestOptions{
   200  		RequestName: internal.PostBuildpackBitsRequest,
   201  		URIParams:   internal.Params{"buildpack_guid": buildpackGUID},
   202  		Body:        body,
   203  	})
   204  
   205  	if err != nil {
   206  		return "", nil, err
   207  	}
   208  
   209  	request.ContentLength = contentLength
   210  	request.Header.Set("Content-Type", contentType)
   211  
   212  	jobURL, warnings, err := client.uploadBuildpackAsynchronously(request, writeErrors)
   213  	if err != nil {
   214  		return "", warnings, err
   215  	}
   216  	return jobURL, warnings, nil
   217  }
   218  
   219  func (client *Client) uploadBuildpackAsynchronously(request *cloudcontroller.Request, writeErrors <-chan error) (JobURL, Warnings, error) {
   220  
   221  	var buildpack Buildpack
   222  	response := cloudcontroller.Response{
   223  		DecodeJSONResponseInto: &buildpack,
   224  	}
   225  
   226  	httpErrors := make(chan error)
   227  
   228  	go func() {
   229  		defer close(httpErrors)
   230  
   231  		err := client.connection.Make(request, &response)
   232  		if err != nil {
   233  			httpErrors <- err
   234  		}
   235  	}()
   236  
   237  	// The following section makes the following assumptions:
   238  	// 1) If an error occurs during file reading, an EOF is sent to the request
   239  	// object. Thus ending the request transfer.
   240  	// 2) If an error occurs during request transfer, an EOF is sent to the pipe.
   241  	// Thus ending the writing routine.
   242  	var firstError error
   243  	var writeClosed, httpClosed bool
   244  
   245  	for {
   246  		select {
   247  		case writeErr, ok := <-writeErrors:
   248  			if !ok {
   249  				writeClosed = true
   250  				break // for select
   251  			}
   252  			if firstError == nil {
   253  				firstError = writeErr
   254  			}
   255  		case httpErr, ok := <-httpErrors:
   256  			if !ok {
   257  				httpClosed = true
   258  				break // for select
   259  			}
   260  			if firstError == nil {
   261  				firstError = httpErr
   262  			}
   263  		}
   264  
   265  		if writeClosed && httpClosed {
   266  			break // for for
   267  		}
   268  	}
   269  	return JobURL(response.ResourceLocationURL), response.Warnings, firstError
   270  }