github.com/joshdk/godel@v0.0.0-20170529232908-862138a45aee/apps/distgo/cmd/publish/artifactory_publish.go (about)

     1  // Copyright 2016 Palantir Technologies, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package publish
    16  
    17  import (
    18  	"encoding/json"
    19  	"fmt"
    20  	"io"
    21  	"io/ioutil"
    22  	"net/http"
    23  	"net/url"
    24  	"path"
    25  	"strings"
    26  
    27  	"github.com/pkg/errors"
    28  
    29  	"github.com/palantir/godel/apps/distgo/params"
    30  )
    31  
    32  type ArtifactoryConnectionInfo struct {
    33  	BasicConnectionInfo
    34  	Repository string
    35  }
    36  
    37  func (a *ArtifactoryConnectionInfo) Publish(buildSpec params.ProductBuildSpec, paths ProductPaths, stdout io.Writer) (rURLs []string, rErr error) {
    38  	artifactoryURL := strings.Join([]string{a.URL, "artifactory"}, "/")
    39  	baseURL := strings.Join([]string{artifactoryURL, a.Repository, paths.productPath}, "/")
    40  
    41  	artifactExists := func(fi fileInfo, dstFileName, username, password string) bool {
    42  		rawCheckArtifactURL := strings.Join([]string{artifactoryURL, "api", "storage", a.Repository, paths.productPath, dstFileName}, "/")
    43  		checkArtifactURL, err := url.Parse(rawCheckArtifactURL)
    44  		if err != nil {
    45  			return false
    46  		}
    47  
    48  		header := http.Header{}
    49  		req := http.Request{
    50  			Method: http.MethodGet,
    51  			URL:    checkArtifactURL,
    52  			Header: header,
    53  		}
    54  		req.SetBasicAuth(username, password)
    55  
    56  		if resp, err := http.DefaultClient.Do(&req); err == nil {
    57  			defer func() {
    58  				if err := resp.Body.Close(); err != nil && rErr == nil {
    59  					rErr = errors.Wrapf(err, "failed to close response body for URL %s", rawCheckArtifactURL)
    60  				}
    61  			}()
    62  
    63  			if bytes, err := ioutil.ReadAll(resp.Body); err == nil {
    64  				var jsonMap map[string]*json.RawMessage
    65  				if err := json.Unmarshal(bytes, &jsonMap); err == nil {
    66  					if checksumJSON, ok := jsonMap["checksums"]; ok && checksumJSON != nil {
    67  						var dstChecksums checksums
    68  						if err := json.Unmarshal(*checksumJSON, &dstChecksums); err == nil {
    69  							return fi.checksums.match(dstChecksums)
    70  						}
    71  					}
    72  				}
    73  			}
    74  			return false
    75  		}
    76  		return false
    77  	}
    78  
    79  	artifactURLs, err := a.uploadArtifacts(baseURL, paths, artifactExists, stdout)
    80  	if err != nil {
    81  		return artifactURLs, err
    82  	}
    83  
    84  	// compute SHA-256 checksums for artifacts
    85  	if err := computeArtifactChecksums(artifactoryURL, a.Repository, a.Username, a.Password, paths, stdout); err != nil {
    86  		// if triggering checksum computation fails, print message but don't throw error
    87  		fmt.Fprintln(stdout, "Uploading artifacts succeeded, but failed to trigger computation of SHA-256 checksums:", err)
    88  	}
    89  	return artifactURLs, err
    90  }
    91  
    92  func computeArtifactChecksums(artifactoryURL, repoKey, username, password string, paths ProductPaths, stdout io.Writer) error {
    93  	for _, currArtifactPath := range paths.artifactPaths {
    94  		currArtifactURL := strings.Join([]string{paths.productPath, path.Base(currArtifactPath)}, "/")
    95  		if err := artifactorySetSHA256Checksum(artifactoryURL, repoKey, currArtifactURL, username, password, stdout); err != nil {
    96  			return errors.Wrapf(err, "")
    97  		}
    98  	}
    99  	pomPath := strings.Join([]string{paths.productPath, path.Base(paths.pomFilePath)}, "/")
   100  	if err := artifactorySetSHA256Checksum(artifactoryURL, repoKey, pomPath, username, password, stdout); err != nil {
   101  		return errors.Wrapf(err, "")
   102  	}
   103  	return nil
   104  }
   105  
   106  func artifactorySetSHA256Checksum(baseURLString, repoKey, filePath, username, password string, stdout io.Writer) (rErr error) {
   107  	apiURLString := baseURLString + "/api/checksum/sha256"
   108  	uploadURL, err := url.Parse(apiURLString)
   109  	if err != nil {
   110  		return errors.Wrapf(err, "failed to parse %v as URL", apiURLString)
   111  	}
   112  
   113  	jsonContent := fmt.Sprintf(`{"repoKey":"%v","path":"%v"}`, repoKey, filePath)
   114  	reader := strings.NewReader(jsonContent)
   115  
   116  	header := http.Header{}
   117  	header.Set("Content-Type", "application/json")
   118  	req := http.Request{
   119  		Method:        http.MethodPost,
   120  		URL:           uploadURL,
   121  		Header:        header,
   122  		Body:          ioutil.NopCloser(reader),
   123  		ContentLength: int64(len([]byte(jsonContent))),
   124  	}
   125  	req.SetBasicAuth(username, password)
   126  
   127  	resp, err := http.DefaultClient.Do(&req)
   128  	if err != nil {
   129  		return errors.Wrapf(err, "failed to trigger computation of SHA-256 checksum for %v", filePath)
   130  	}
   131  	defer func() {
   132  		if err := resp.Body.Close(); err != nil && rErr == nil {
   133  			rErr = errors.Wrapf(err, "failed to close response body for URL %s", apiURLString)
   134  		}
   135  	}()
   136  
   137  	if resp.StatusCode >= http.StatusBadRequest {
   138  		return errors.Errorf("triggering computation of SHA-256 checksum for %v resulted in response: %v", filePath, resp.Status)
   139  	}
   140  	return nil
   141  }