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 }