mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2025-01-11 18:15:28 +00:00
59ba8538a1
* Add configuration extension flags to server Add httpsignatures dependency Signed-off-by: Lukas Bachschwell <lukas@lbsfilm.at> * Add http fetching to config fetcher Signed-off-by: Lukas Bachschwell <lukas@lbsfilm.at> * Refetch config on rebuild Signed-off-by: Lukas Bachschwell <lukas@lbsfilm.at> * - Ensure multipipeline compatiblity - Send original config in http request Signed-off-by: Lukas Bachschwell <lukas@lbsfilm.at> * Basic tests of config api Signed-off-by: Lukas Bachschwell <lukas@lbsfilm.at> * Simple docs page Signed-off-by: Lukas Bachschwell <lukas@lbsfilm.at> * Better flag naming Signed-off-by: Lukas Bachschwell <lukas@lbsfilm.at> * Rename usages of the term yaml Rename ConfigAPI struct Signed-off-by: Lukas Bachschwell <lukas@lbsfilm.at> * Doc adjustments Signed-off-by: Lukas Bachschwell <lukas@lbsfilm.at> * More docs touchups Signed-off-by: Lukas Bachschwell <lukas@lbsfilm.at> * Fix env vars in docs Signed-off-by: Lukas Bachschwell <lukas@lbsfilm.at> * fix json tags for api calls Signed-off-by: Lukas Bachschwell <lukas@lbsfilm.at> * Add example config service Signed-off-by: Lukas Bachschwell <lukas@lbsfilm.at> * Consistent naming for configService Signed-off-by: Lukas Bachschwell <lukas@lbsfilm.at> * Docs: Change example repository location Signed-off-by: Lukas Bachschwell <lukas@lbsfilm.at> * Fix tests after response field rename Signed-off-by: Lukas Bachschwell <lukas@lbsfilm.at> * Revert accidential unrelated change in api hook Signed-off-by: Lukas Bachschwell <lukas@lbsfilm.at> * Update server flag descriptions Co-authored-by: Anbraten <anton@ju60.de> Co-authored-by: Anbraten <anton@ju60.de>
127 lines
3.3 KiB
Go
127 lines
3.3 KiB
Go
package configuration
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"net/url"
|
|
|
|
"github.com/99designs/httpsignatures-go"
|
|
|
|
"github.com/woodpecker-ci/woodpecker/server/model"
|
|
"github.com/woodpecker-ci/woodpecker/server/remote"
|
|
)
|
|
|
|
type ConfigService struct {
|
|
endpoint string
|
|
secret string
|
|
}
|
|
|
|
// Same as remote.FileMeta but with json tags and string data
|
|
type config struct {
|
|
Name string `json:"name"`
|
|
Data string `json:"data"`
|
|
}
|
|
|
|
type requestStructure struct {
|
|
Repo *model.Repo `json:"repo"`
|
|
Build *model.Build `json:"build"`
|
|
Configuration []*config `json:"configs"`
|
|
}
|
|
|
|
type responseStructure struct {
|
|
Configs []config `json:"configs"`
|
|
}
|
|
|
|
func NewAPI(endpoint, secret string) ConfigService {
|
|
return ConfigService{endpoint: endpoint, secret: secret}
|
|
}
|
|
|
|
func (cp *ConfigService) IsConfigured() bool {
|
|
return cp.endpoint != ""
|
|
}
|
|
|
|
func (cp *ConfigService) FetchExternalConfig(ctx context.Context, repo *model.Repo, build *model.Build, currentFileMeta []*remote.FileMeta) (configData []*remote.FileMeta, useOld bool, err error) {
|
|
currentConfigs := make([]*config, len(currentFileMeta))
|
|
for i, pipe := range currentFileMeta {
|
|
currentConfigs[i] = &config{Name: pipe.Name, Data: string(pipe.Data)}
|
|
}
|
|
|
|
response, status, err := sendRequest(ctx, "POST", cp.endpoint, cp.secret, requestStructure{Repo: repo, Build: build, Configuration: currentConfigs})
|
|
if err != nil {
|
|
return nil, false, fmt.Errorf("Failed to fetch config via http (%d) %w", status, err)
|
|
}
|
|
|
|
var newFileMeta []*remote.FileMeta
|
|
if response != nil {
|
|
newFileMeta = make([]*remote.FileMeta, len(response.Configs))
|
|
for i, pipe := range response.Configs {
|
|
newFileMeta[i] = &remote.FileMeta{Name: pipe.Name, Data: []byte(pipe.Data)}
|
|
}
|
|
} else {
|
|
newFileMeta = make([]*remote.FileMeta, 0)
|
|
}
|
|
|
|
return newFileMeta, status == 204, nil
|
|
}
|
|
|
|
func sendRequest(ctx context.Context, method, path, signkey string, in interface{}) (response *responseStructure, statuscode int, err error) {
|
|
uri, err := url.Parse(path)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
// if we are posting or putting data, we need to
|
|
// write it to the body of the request.
|
|
var buf io.ReadWriter
|
|
if in != nil {
|
|
buf = new(bytes.Buffer)
|
|
jsonerr := json.NewEncoder(buf).Encode(in)
|
|
if jsonerr != nil {
|
|
return nil, 0, jsonerr
|
|
}
|
|
}
|
|
|
|
// creates a new http request to bitbucket.
|
|
req, err := http.NewRequestWithContext(ctx, method, uri.String(), buf)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
if in != nil {
|
|
req.Header.Set("Content-Type", "application/json")
|
|
}
|
|
|
|
// Sign using the 'Signature' header
|
|
err = httpsignatures.DefaultSha256Signer.SignRequest("hmac-key", signkey, req)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != 200 && resp.StatusCode != 204 {
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, resp.StatusCode, err
|
|
}
|
|
|
|
return nil, resp.StatusCode, fmt.Errorf("Response: %s", string(body))
|
|
}
|
|
|
|
if resp.StatusCode == 204 {
|
|
return nil, resp.StatusCode, nil
|
|
}
|
|
|
|
// if no other errors parse and return the json response.
|
|
decodedResponse := new(responseStructure)
|
|
err = json.NewDecoder(resp.Body).Decode(decodedResponse)
|
|
return decodedResponse, resp.StatusCode, err
|
|
}
|