github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/docker_resource.go (about)

     1  // Copyright 2018 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/json"
     9  	"io"
    10  
    11  	"github.com/juju/errors"
    12  	"github.com/juju/mgo/v3"
    13  	"github.com/juju/mgo/v3/bson"
    14  	"github.com/juju/mgo/v3/txn"
    15  
    16  	"github.com/juju/juju/core/resources"
    17  	"github.com/juju/juju/docker"
    18  )
    19  
    20  // dockerMetadataStorage implements DockerMetadataStorage
    21  type dockerMetadataStorage struct {
    22  	st *State
    23  }
    24  
    25  type dockerMetadataDoc struct {
    26  	// Id holds the resource ID
    27  	Id string `bson:"_id"`
    28  
    29  	// RegistryPath holds the image name (including host) of the image in the docker registry.
    30  	RegistryPath string `bson:"registry-path"`
    31  
    32  	// Username holds the password string for a non-private image.
    33  	Username string `bson:"username"`
    34  
    35  	// Password holds the password string for a non-private image.
    36  	Password string `bson:"password"`
    37  }
    38  
    39  // DockerMetadataStorage provides the interface for storing Docker resource-type data
    40  type DockerMetadataStorage interface {
    41  	Save(resourceID string, drInfo resources.DockerImageDetails) error
    42  	Remove(resourceID string) error
    43  	Get(resourceID string) (io.ReadCloser, int64, error)
    44  }
    45  
    46  // NewDockerMetadataStorage returns a dockerMetadataStorage for persisting Docker resources.
    47  func NewDockerMetadataStorage(st *State) DockerMetadataStorage {
    48  	return &dockerMetadataStorage{
    49  		st: st,
    50  	}
    51  }
    52  
    53  // Save creates a new record for a Docker resource.
    54  func (dr *dockerMetadataStorage) Save(resourceID string, drInfo resources.DockerImageDetails) error {
    55  	doc := dockerMetadataDoc{
    56  		Id:           resourceID,
    57  		RegistryPath: drInfo.RegistryPath,
    58  		Username:     drInfo.Username,
    59  		Password:     drInfo.Password,
    60  	}
    61  
    62  	buildTxn := func(int) ([]txn.Op, error) {
    63  		existing, err := dr.get(resourceID)
    64  		if err != nil && !errors.IsNotFound(err) {
    65  			return nil, errors.Annotate(err, "failed to check for existing resource")
    66  
    67  		}
    68  		if !errors.IsNotFound(err) {
    69  			return []txn.Op{{
    70  				C:      dockerResourcesC,
    71  				Id:     existing.Id,
    72  				Assert: txn.DocExists,
    73  				Update: bson.D{
    74  					{"$set",
    75  						bson.D{
    76  							{"registry-path", doc.RegistryPath},
    77  							{"username", doc.Username},
    78  							{"password", doc.Password},
    79  						},
    80  					},
    81  				},
    82  			}}, nil
    83  		}
    84  
    85  		return []txn.Op{{
    86  			C:      dockerResourcesC,
    87  			Id:     doc.Id,
    88  			Assert: txn.DocMissing,
    89  			Insert: doc,
    90  		}}, nil
    91  	}
    92  
    93  	err := dr.st.db().Run(buildTxn)
    94  	return errors.Annotate(err, "failed to store Docker resource")
    95  }
    96  
    97  // Remove removes the Docker resource with the provided ID.
    98  func (dr *dockerMetadataStorage) Remove(resourceID string) error {
    99  	ops := []txn.Op{{
   100  		C:      dockerResourcesC,
   101  		Id:     resourceID,
   102  		Remove: true,
   103  	}}
   104  	err := dr.st.db().RunTransaction(ops)
   105  	return errors.Annotate(err, "failed to remove Docker resource")
   106  }
   107  
   108  // Get retrieves the requested stored Docker resource.
   109  func (dr *dockerMetadataStorage) Get(resourceID string) (io.ReadCloser, int64, error) {
   110  	doc, err := dr.get(resourceID)
   111  	if err != nil {
   112  		return nil, -1, errors.Trace(err)
   113  	}
   114  	details := resources.DockerImageDetails{
   115  		RegistryPath: doc.RegistryPath,
   116  		ImageRepoDetails: docker.ImageRepoDetails{
   117  			BasicAuthConfig: docker.BasicAuthConfig{
   118  				Username: doc.Username,
   119  				Password: doc.Password,
   120  			},
   121  		},
   122  	}
   123  	data, err := json.Marshal(details)
   124  	if err != nil {
   125  		return nil, -1, errors.Trace(err)
   126  	}
   127  	infoReader := bytes.NewReader(data)
   128  	length := infoReader.Len()
   129  	return &dockerResourceReadCloser{infoReader}, int64(length), nil
   130  }
   131  
   132  func (dr *dockerMetadataStorage) get(resourceID string) (*dockerMetadataDoc, error) {
   133  	coll, closer := dr.st.db().GetCollection(dockerResourcesC)
   134  	defer closer()
   135  
   136  	var doc dockerMetadataDoc
   137  	err := coll.FindId(resourceID).One(&doc)
   138  	if err == mgo.ErrNotFound {
   139  		return nil, errors.NotFoundf("Docker resource with ID: %s", resourceID)
   140  	}
   141  	if err != nil {
   142  		return nil, errors.Trace(err)
   143  	}
   144  	return &doc, nil
   145  }
   146  
   147  type dockerResourceReadCloser struct {
   148  	io.ReadSeeker
   149  }
   150  
   151  func (drrc *dockerResourceReadCloser) Close() error {
   152  	return nil
   153  }