github.com/mook-as/cf-cli@v7.0.0-beta.28.0.20200120190804-b91c115fae48+incompatible/api/cloudcontroller/ccv2/buildpack.go (about)

     1  package ccv2
     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/ccv2/internal"
    11  	"code.cloudfoundry.org/cli/api/cloudcontroller/uploads"
    12  	"code.cloudfoundry.org/cli/types"
    13  )
    14  
    15  // Buildpack represents a Cloud Controller Buildpack.
    16  type Buildpack struct {
    17  	Locked   types.NullBool
    18  	Enabled  types.NullBool
    19  	GUID     string
    20  	Name     string
    21  	Position types.NullInt
    22  	Stack    string
    23  }
    24  
    25  func (buildpack Buildpack) MarshalJSON() ([]byte, error) {
    26  	ccBuildpack := struct {
    27  		Locked   *bool  `json:"locked,omitempty"`
    28  		Enabled  *bool  `json:"enabled,omitempty"`
    29  		Name     string `json:"name"`
    30  		Position *int   `json:"position,omitempty"`
    31  		Stack    string `json:"stack,omitempty"`
    32  	}{
    33  		Name:  buildpack.Name,
    34  		Stack: buildpack.Stack,
    35  	}
    36  
    37  	if buildpack.Position.IsSet {
    38  		ccBuildpack.Position = &buildpack.Position.Value
    39  	}
    40  	if buildpack.Enabled.IsSet {
    41  		ccBuildpack.Enabled = &buildpack.Enabled.Value
    42  	}
    43  	if buildpack.Locked.IsSet {
    44  		ccBuildpack.Locked = &buildpack.Locked.Value
    45  	}
    46  
    47  	return json.Marshal(ccBuildpack)
    48  }
    49  
    50  func (buildpack *Buildpack) UnmarshalJSON(data []byte) error {
    51  	var alias struct {
    52  		Metadata internal.Metadata `json:"metadata"`
    53  		Entity   struct {
    54  			Locked   types.NullBool `json:"locked"`
    55  			Enabled  types.NullBool `json:"enabled"`
    56  			Name     string         `json:"name"`
    57  			Position types.NullInt  `json:"position"`
    58  			Stack    string         `json:"stack"`
    59  		} `json:"entity"`
    60  	}
    61  
    62  	err := json.Unmarshal(data, &alias)
    63  	if err != nil {
    64  		return err
    65  	}
    66  
    67  	buildpack.Locked = alias.Entity.Locked
    68  	buildpack.Enabled = alias.Entity.Enabled
    69  	buildpack.GUID = alias.Metadata.GUID
    70  	buildpack.Name = alias.Entity.Name
    71  	buildpack.Position = alias.Entity.Position
    72  	buildpack.Stack = alias.Entity.Stack
    73  	return nil
    74  }
    75  
    76  // CreateBuildpack creates a new buildpack.
    77  func (client *Client) CreateBuildpack(buildpack Buildpack) (Buildpack, Warnings, error) {
    78  	body, err := json.Marshal(buildpack)
    79  	if err != nil {
    80  		return Buildpack{}, nil, err
    81  	}
    82  
    83  	request, err := client.newHTTPRequest(requestOptions{
    84  		RequestName: internal.PostBuildpackRequest,
    85  		Body:        bytes.NewReader(body),
    86  	})
    87  	if err != nil {
    88  		return Buildpack{}, nil, err
    89  	}
    90  
    91  	var createdBuildpack Buildpack
    92  	response := cloudcontroller.Response{
    93  		DecodeJSONResponseInto: &createdBuildpack,
    94  	}
    95  
    96  	err = client.connection.Make(request, &response)
    97  	return createdBuildpack, response.Warnings, err
    98  }
    99  
   100  // GetBuildpacks searches for a buildpack with the given name and returns it if it exists.
   101  func (client *Client) GetBuildpacks(filters ...Filter) ([]Buildpack, Warnings, error) {
   102  	request, err := client.newHTTPRequest(requestOptions{
   103  		RequestName: internal.GetBuildpacksRequest,
   104  		Query:       ConvertFilterParameters(filters),
   105  	})
   106  
   107  	if err != nil {
   108  		return nil, nil, err
   109  	}
   110  
   111  	var buildpacks []Buildpack
   112  	warnings, err := client.paginate(request, Buildpack{}, func(item interface{}) error {
   113  		if buildpack, ok := item.(Buildpack); ok {
   114  			buildpacks = append(buildpacks, buildpack)
   115  		} else {
   116  			return ccerror.UnknownObjectInListError{
   117  				Expected:   Buildpack{},
   118  				Unexpected: item,
   119  			}
   120  		}
   121  		return nil
   122  	})
   123  
   124  	return buildpacks, warnings, err
   125  }
   126  
   127  // UpdateBuildpack updates the buildpack with the provided GUID and returns the
   128  // updated buildpack. Note: Stack cannot be updated without uploading a new
   129  // buildpack.
   130  func (client *Client) UpdateBuildpack(buildpack Buildpack) (Buildpack, Warnings, error) {
   131  	body, err := json.Marshal(buildpack)
   132  	if err != nil {
   133  		return Buildpack{}, nil, err
   134  	}
   135  
   136  	request, err := client.newHTTPRequest(requestOptions{
   137  		RequestName: internal.PutBuildpackRequest,
   138  		URIParams:   Params{"buildpack_guid": buildpack.GUID},
   139  		Body:        bytes.NewReader(body),
   140  	})
   141  	if err != nil {
   142  		return Buildpack{}, nil, err
   143  	}
   144  
   145  	var updatedBuildpack Buildpack
   146  	response := cloudcontroller.Response{
   147  		DecodeJSONResponseInto: &updatedBuildpack,
   148  	}
   149  
   150  	err = client.connection.Make(request, &response)
   151  	if err != nil {
   152  		return Buildpack{}, response.Warnings, err
   153  	}
   154  
   155  	return updatedBuildpack, response.Warnings, nil
   156  }
   157  
   158  // UploadBuildpack uploads the contents of a buildpack zip to the server.
   159  func (client *Client) UploadBuildpack(buildpackGUID string, buildpackPath string, buildpack io.Reader, buildpackLength int64) (Warnings, error) {
   160  
   161  	contentLength, err := uploads.CalculateRequestSize(buildpackLength, buildpackPath, "buildpack")
   162  	if err != nil {
   163  		return nil, err
   164  	}
   165  
   166  	contentType, body, writeErrors := uploads.CreateMultipartBodyAndHeader(buildpack, buildpackPath, "buildpack")
   167  
   168  	request, err := client.newHTTPRequest(requestOptions{
   169  		RequestName: internal.PutBuildpackBitsRequest,
   170  		URIParams:   Params{"buildpack_guid": buildpackGUID},
   171  		Body:        body,
   172  	})
   173  
   174  	if err != nil {
   175  		return nil, err
   176  	}
   177  
   178  	request.Header.Set("Content-Type", contentType)
   179  	request.ContentLength = contentLength
   180  
   181  	_, warnings, err := client.uploadBuildpackAsynchronously(request, writeErrors)
   182  	if err != nil {
   183  		return warnings, err
   184  	}
   185  	return warnings, nil
   186  }
   187  
   188  func (client *Client) uploadBuildpackAsynchronously(request *cloudcontroller.Request, writeErrors <-chan error) (Buildpack, Warnings, error) {
   189  
   190  	var buildpack Buildpack
   191  	response := cloudcontroller.Response{
   192  		DecodeJSONResponseInto: &buildpack,
   193  	}
   194  
   195  	httpErrors := make(chan error)
   196  
   197  	go func() {
   198  		defer close(httpErrors)
   199  
   200  		err := client.connection.Make(request, &response)
   201  		if err != nil {
   202  			httpErrors <- err
   203  		}
   204  	}()
   205  
   206  	// The following section makes the following assumptions:
   207  	// 1) If an error occurs during file reading, an EOF is sent to the request
   208  	// object. Thus ending the request transfer.
   209  	// 2) If an error occurs during request transfer, an EOF is sent to the pipe.
   210  	// Thus ending the writing routine.
   211  	var firstError error
   212  	var writeClosed, httpClosed bool
   213  
   214  	for {
   215  		select {
   216  		case writeErr, ok := <-writeErrors:
   217  			if !ok {
   218  				writeClosed = true
   219  				break // for select
   220  			}
   221  			if firstError == nil {
   222  				firstError = writeErr
   223  			}
   224  		case httpErr, ok := <-httpErrors:
   225  			if !ok {
   226  				httpClosed = true
   227  				break // for select
   228  			}
   229  			if firstError == nil {
   230  				firstError = httpErr
   231  			}
   232  		}
   233  
   234  		if writeClosed && httpClosed {
   235  			break // for for
   236  		}
   237  	}
   238  	return buildpack, response.Warnings, firstError
   239  }