Many: factor out logger implementation from upload/koji
The upload/koji package functions were creating a logger and then were using it. This is not ideal for a library implementation. Signed-off-by: Tomáš Hozza <thozza@redhat.com>
This commit is contained in:
parent
60014b1218
commit
ca1e1dce36
7 changed files with 60 additions and 46 deletions
|
|
@ -53,7 +53,7 @@ func TestKojiRefund(t *testing.T) {
|
||||||
Principal: "osbuild-krb@LOCAL",
|
Principal: "osbuild-krb@LOCAL",
|
||||||
KeyTab: shareDir + "/client.keytab",
|
KeyTab: shareDir + "/client.keytab",
|
||||||
}
|
}
|
||||||
k, err := koji.NewFromGSSAPI(server, credentials, transport)
|
k, err := koji.NewFromGSSAPI(server, credentials, transport, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|
@ -114,7 +114,7 @@ func TestKojiImport(t *testing.T) {
|
||||||
Principal: "osbuild-krb@LOCAL",
|
Principal: "osbuild-krb@LOCAL",
|
||||||
KeyTab: shareDir + "/client.keytab",
|
KeyTab: shareDir + "/client.keytab",
|
||||||
}
|
}
|
||||||
k, err := koji.NewFromGSSAPI(server, credentials, transport)
|
k, err := koji.NewFromGSSAPI(server, credentials, transport, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|
|
||||||
|
|
@ -42,8 +42,8 @@ func main() {
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
transport := koji.CreateRetryableTransport()
|
transport := koji.CreateRetryableTransport(nil)
|
||||||
k, err := koji.NewFromPlain(server, "osbuild", "osbuildpass", transport)
|
k, err := koji.NewFromPlain(server, "osbuild", "osbuildpass", transport, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println(err.Error())
|
println(err.Error())
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,8 @@ func (impl *KojiFinalizeJobImpl) kojiImport(
|
||||||
return fmt.Errorf("Koji server has not been configured: %s", serverURL.Hostname())
|
return fmt.Errorf("Koji server has not been configured: %s", serverURL.Hostname())
|
||||||
}
|
}
|
||||||
|
|
||||||
transport := koji.CreateKojiTransport(kojiServer.relaxTimeoutFactor)
|
transport := koji.CreateKojiTransport(kojiServer.relaxTimeoutFactor, NewRHLeveledLogger(nil))
|
||||||
k, err := koji.NewFromGSSAPI(server, &kojiServer.creds, transport)
|
k, err := koji.NewFromGSSAPI(server, &kojiServer.creds, transport, NewRHLeveledLogger(nil))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -69,8 +69,8 @@ func (impl *KojiFinalizeJobImpl) kojiFail(server string, buildID int, token stri
|
||||||
return fmt.Errorf("Koji server has not been configured: %s", serverURL.Hostname())
|
return fmt.Errorf("Koji server has not been configured: %s", serverURL.Hostname())
|
||||||
}
|
}
|
||||||
|
|
||||||
transport := koji.CreateKojiTransport(kojiServer.relaxTimeoutFactor)
|
transport := koji.CreateKojiTransport(kojiServer.relaxTimeoutFactor, NewRHLeveledLogger(nil))
|
||||||
k, err := koji.NewFromGSSAPI(server, &kojiServer.creds, transport)
|
k, err := koji.NewFromGSSAPI(server, &kojiServer.creds, transport, NewRHLeveledLogger(nil))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,8 @@ func (impl *KojiInitJobImpl) kojiInit(server, name, version, release string) (st
|
||||||
return "", 0, fmt.Errorf("Koji server has not been configured: %s", serverURL.Hostname())
|
return "", 0, fmt.Errorf("Koji server has not been configured: %s", serverURL.Hostname())
|
||||||
}
|
}
|
||||||
|
|
||||||
transport := koji.CreateKojiTransport(kojiServer.relaxTimeoutFactor)
|
transport := koji.CreateKojiTransport(kojiServer.relaxTimeoutFactor, NewRHLeveledLogger(nil))
|
||||||
k, err := koji.NewFromGSSAPI(server, &kojiServer.creds, transport)
|
k, err := koji.NewFromGSSAPI(server, &kojiServer.creds, transport, NewRHLeveledLogger(nil))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", 0, err
|
return "", 0, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1012,9 +1012,9 @@ func (impl *OSBuildJobImpl) Run(job worker.Job) error {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
kojiTransport := koji.CreateKojiTransport(kojiServer.relaxTimeoutFactor)
|
kojiTransport := koji.CreateKojiTransport(kojiServer.relaxTimeoutFactor, NewRHLeveledLogger(nil))
|
||||||
|
|
||||||
kojiAPI, err := koji.NewFromGSSAPI(targetOptions.Server, &kojiServer.creds, kojiTransport)
|
kojiAPI, err := koji.NewFromGSSAPI(targetOptions.Server, &kojiServer.creds, kojiTransport, NewRHLeveledLogger(nil))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logWithId.Warnf("[Koji] 🔑 login failed: %v", err) // DON'T EDIT: Used for Splunk dashboard
|
logWithId.Warnf("[Koji] 🔑 login failed: %v", err) // DON'T EDIT: Used for Splunk dashboard
|
||||||
targetResult.TargetError = clienterrors.New(clienterrors.ErrorInvalidTargetConfig, fmt.Sprintf("failed to authenticate with Koji server %q: %v", kojiServerURL.Hostname(), err), nil)
|
targetResult.TargetError = clienterrors.New(clienterrors.ErrorInvalidTargetConfig, fmt.Sprintf("failed to authenticate with Koji server %q: %v", kojiServerURL.Hostname(), err), nil)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
package koji
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
rh "github.com/hashicorp/go-retryablehttp"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -10,6 +11,13 @@ type LeveledLogrus struct {
|
||||||
*logrus.Logger
|
*logrus.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewRHLeveledLogger(logger *logrus.Logger) rh.LeveledLogger {
|
||||||
|
if logger == nil {
|
||||||
|
logger = logrus.StandardLogger()
|
||||||
|
}
|
||||||
|
return rh.LeveledLogger(&LeveledLogrus{logger})
|
||||||
|
}
|
||||||
|
|
||||||
const monitoringKeyword = "retrying"
|
const monitoringKeyword = "retrying"
|
||||||
|
|
||||||
func fields(keysAndValues ...interface{}) map[string]interface{} {
|
func fields(keysAndValues ...interface{}) map[string]interface{} {
|
||||||
|
|
@ -23,7 +23,6 @@ import (
|
||||||
|
|
||||||
rh "github.com/hashicorp/go-retryablehttp"
|
rh "github.com/hashicorp/go-retryablehttp"
|
||||||
"github.com/kolo/xmlrpc"
|
"github.com/kolo/xmlrpc"
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/ubccr/kerby/khttp"
|
"github.com/ubccr/kerby/khttp"
|
||||||
|
|
||||||
"github.com/osbuild/images/pkg/rpmmd"
|
"github.com/osbuild/images/pkg/rpmmd"
|
||||||
|
|
@ -34,6 +33,7 @@ type Koji struct {
|
||||||
xmlrpc *xmlrpc.Client
|
xmlrpc *xmlrpc.Client
|
||||||
server string
|
server string
|
||||||
transport http.RoundTripper
|
transport http.RoundTripper
|
||||||
|
logger rh.LeveledLogger
|
||||||
}
|
}
|
||||||
|
|
||||||
// BUILD METADATA
|
// BUILD METADATA
|
||||||
|
|
@ -242,7 +242,7 @@ type loginReply struct {
|
||||||
SessionKey string `xmlrpc:"session-key"`
|
SessionKey string `xmlrpc:"session-key"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func newKoji(server string, transport http.RoundTripper, reply loginReply) (*Koji, error) {
|
func newKoji(server string, transport http.RoundTripper, reply loginReply, logger rh.LeveledLogger) (*Koji, error) {
|
||||||
// Create the final xmlrpc client with our custom RoundTripper handling
|
// Create the final xmlrpc client with our custom RoundTripper handling
|
||||||
// sessionID, sessionKey and callnum
|
// sessionID, sessionKey and callnum
|
||||||
kojiTransport := &Transport{
|
kojiTransport := &Transport{
|
||||||
|
|
@ -261,13 +261,14 @@ func newKoji(server string, transport http.RoundTripper, reply loginReply) (*Koj
|
||||||
xmlrpc: client,
|
xmlrpc: client,
|
||||||
server: server,
|
server: server,
|
||||||
transport: kojiTransport,
|
transport: kojiTransport,
|
||||||
|
logger: logger,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFromPlain creates a new Koji sessions =authenticated using the plain
|
// NewFromPlain creates a new Koji sessions =authenticated using the plain
|
||||||
// username/password method. If you want to speak to a public koji instance,
|
// username/password method. If you want to speak to a public koji instance,
|
||||||
// you probably cannot use this method.
|
// you probably cannot use this method.
|
||||||
func NewFromPlain(server, user, password string, transport http.RoundTripper) (*Koji, error) {
|
func NewFromPlain(server, user, password string, transport http.RoundTripper, logger rh.LeveledLogger) (*Koji, error) {
|
||||||
// Create a temporary xmlrpc client.
|
// Create a temporary xmlrpc client.
|
||||||
// The API doesn't require sessionID, sessionKey and callnum yet,
|
// The API doesn't require sessionID, sessionKey and callnum yet,
|
||||||
// so there's no need to use the custom Koji RoundTripper,
|
// so there's no need to use the custom Koji RoundTripper,
|
||||||
|
|
@ -284,13 +285,17 @@ func NewFromPlain(server, user, password string, transport http.RoundTripper) (*
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return newKoji(server, transport, reply)
|
return newKoji(server, transport, reply, logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFromGSSAPI creates a new Koji session authenticated using GSSAPI.
|
// NewFromGSSAPI creates a new Koji session authenticated using GSSAPI.
|
||||||
// Principal and keytab used for the session is passed using credentials
|
// Principal and keytab used for the session is passed using credentials
|
||||||
// parameter.
|
// parameter.
|
||||||
func NewFromGSSAPI(server string, credentials *GSSAPICredentials, transport http.RoundTripper) (*Koji, error) {
|
func NewFromGSSAPI(
|
||||||
|
server string,
|
||||||
|
credentials *GSSAPICredentials,
|
||||||
|
transport http.RoundTripper,
|
||||||
|
logger rh.LeveledLogger) (*Koji, error) {
|
||||||
// Create a temporary xmlrpc client with kerberos transport.
|
// Create a temporary xmlrpc client with kerberos transport.
|
||||||
// The API doesn't require sessionID, sessionKey and callnum yet,
|
// The API doesn't require sessionID, sessionKey and callnum yet,
|
||||||
// so there's no need to use the custom Koji RoundTripper,
|
// so there's no need to use the custom Koji RoundTripper,
|
||||||
|
|
@ -310,7 +315,7 @@ func NewFromGSSAPI(server string, credentials *GSSAPICredentials, transport http
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return newKoji(server, transport, reply)
|
return newKoji(server, transport, reply, logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAPIVersion gets the version of the API of the remote Koji instance
|
// GetAPIVersion gets the version of the API of the remote Koji instance
|
||||||
|
|
@ -418,7 +423,9 @@ func (k *Koji) CGImport(build Build, buildRoots []BuildRoot, outputs []BuildOutp
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Infof("CGImport succeeded after %d attempts", attempt+1)
|
if k.logger != nil {
|
||||||
|
k.logger.Info(fmt.Sprintf("CGImport succeeded after %d attempts", attempt+1))
|
||||||
|
}
|
||||||
|
|
||||||
return &result, nil
|
return &result, nil
|
||||||
}
|
}
|
||||||
|
|
@ -444,7 +451,7 @@ func (k *Koji) uploadChunk(chunk []byte, filepath, filename string, offset uint6
|
||||||
q.Add("overwrite", "true")
|
q.Add("overwrite", "true")
|
||||||
u.RawQuery = q.Encode()
|
u.RawQuery = q.Encode()
|
||||||
|
|
||||||
client := createCustomRetryableClient()
|
client := createCustomRetryableClient(k.logger)
|
||||||
|
|
||||||
client.HTTPClient = &http.Client{
|
client.HTTPClient = &http.Client{
|
||||||
Transport: k.transport,
|
Transport: k.transport,
|
||||||
|
|
@ -573,10 +580,10 @@ func GSSAPICredentialsFromEnv() (*GSSAPICredentials, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateKojiTransport(relaxTimeout time.Duration) http.RoundTripper {
|
func CreateKojiTransport(relaxTimeout time.Duration, logger rh.LeveledLogger) http.RoundTripper {
|
||||||
// Koji for some reason needs TLS renegotiation enabled.
|
// Koji for some reason needs TLS renegotiation enabled.
|
||||||
// Clone the default http rt and enable renegotiation.
|
// Clone the default http rt and enable renegotiation.
|
||||||
rt := CreateRetryableTransport()
|
rt := CreateRetryableTransport(logger)
|
||||||
|
|
||||||
transport := rt.Client.HTTPClient.Transport.(*http.Transport)
|
transport := rt.Client.HTTPClient.Transport.(*http.Transport)
|
||||||
|
|
||||||
|
|
@ -597,36 +604,35 @@ func CreateKojiTransport(relaxTimeout time.Duration) http.RoundTripper {
|
||||||
return rt
|
return rt
|
||||||
}
|
}
|
||||||
|
|
||||||
func customCheckRetry(ctx context.Context, resp *http.Response, err error) (bool, error) {
|
func createCustomRetryableClient(logger rh.LeveledLogger) *rh.Client {
|
||||||
shouldRetry, retErr := rh.DefaultRetryPolicy(ctx, resp, err)
|
client := rh.NewClient()
|
||||||
|
client.Logger = logger
|
||||||
|
|
||||||
// DefaultRetryPolicy denies retrying for any certificate related error.
|
client.CheckRetry = func(ctx context.Context, resp *http.Response, err error) (bool, error) {
|
||||||
// Override it in case the error is a timeout.
|
shouldRetry, retErr := rh.DefaultRetryPolicy(ctx, resp, err)
|
||||||
if !shouldRetry && err != nil {
|
|
||||||
if v, ok := err.(*url.Error); ok {
|
// DefaultRetryPolicy denies retrying for any certificate related error.
|
||||||
if _, ok := v.Err.(x509.UnknownAuthorityError); ok {
|
// Override it in case the error is a timeout.
|
||||||
// retry if it's a timeout
|
if !shouldRetry && err != nil {
|
||||||
return strings.Contains(strings.ToLower(v.Error()), "timeout"), v
|
if v, ok := err.(*url.Error); ok {
|
||||||
|
if _, ok := v.Err.(x509.UnknownAuthorityError); ok {
|
||||||
|
// retry if it's a timeout
|
||||||
|
return strings.Contains(strings.ToLower(v.Error()), "timeout"), v
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if logger != nil && (!shouldRetry && !(resp.StatusCode >= 200 && resp.StatusCode < 300)) {
|
||||||
|
logger.Info(fmt.Sprintf("Not retrying: %v", resp.Status))
|
||||||
|
}
|
||||||
|
|
||||||
|
return shouldRetry, retErr
|
||||||
}
|
}
|
||||||
|
|
||||||
if !shouldRetry && !(resp.StatusCode >= 200 && resp.StatusCode < 300) {
|
|
||||||
logrus.Info("Not retrying: ", resp.Status)
|
|
||||||
}
|
|
||||||
|
|
||||||
return shouldRetry, retErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func createCustomRetryableClient() *rh.Client {
|
|
||||||
client := rh.NewClient()
|
|
||||||
client.Logger = rh.LeveledLogger(&LeveledLogrus{logrus.StandardLogger()})
|
|
||||||
client.CheckRetry = customCheckRetry
|
|
||||||
return client
|
return client
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateRetryableTransport() *rh.RoundTripper {
|
func CreateRetryableTransport(logger rh.LeveledLogger) *rh.RoundTripper {
|
||||||
rt := rh.RoundTripper{}
|
rt := rh.RoundTripper{}
|
||||||
rt.Client = createCustomRetryableClient()
|
rt.Client = createCustomRetryableClient(logger)
|
||||||
return &rt
|
return &rt
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue