github.com/turbot/steampipe@v1.7.0-rc.0.0.20240517123944-7cef272d4458/pkg/ociinstaller/steampipeimage.go (about) 1 package ociinstaller 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "log" 8 "strings" 9 10 "github.com/containerd/containerd/remotes" 11 ocispec "github.com/opencontainers/image-spec/specs-go/v1" 12 "github.com/turbot/steampipe/pkg/constants" 13 ) 14 15 type SteampipeImage struct { 16 OCIDescriptor *ocispec.Descriptor 17 ImageRef *SteampipeImageRef 18 Config *config 19 Plugin *PluginImage 20 Database *DbImage 21 Fdw *HubImage 22 Assets *AssetsImage 23 resolver *remotes.Resolver 24 } 25 26 type PluginImage struct { 27 BinaryFile string 28 BinaryDigest string 29 BinaryArchitecture string 30 DocsDir string 31 ConfigFileDir string 32 LicenseFile string 33 } 34 35 type DbImage struct { 36 ArchiveDir string 37 ReadmeFile string 38 LicenseFile string 39 } 40 type HubImage struct { 41 BinaryFile string 42 ReadmeFile string 43 LicenseFile string 44 ControlFile string 45 SqlFile string 46 } 47 type AssetsImage struct { 48 ReportUI string 49 } 50 51 func (o *ociDownloader) newSteampipeImage() *SteampipeImage { 52 SteampipeImage := &SteampipeImage{ 53 resolver: &o.resolver, 54 } 55 o.Images = append(o.Images, SteampipeImage) 56 return SteampipeImage 57 } 58 59 type ImageType string 60 61 const ( 62 ImageTypeDatabase ImageType = "db" 63 ImageTypeFdw ImageType = "fdw" 64 ImageTypeAssets ImageType = "assets" 65 ImageTypePlugin ImageType = "plugin" 66 ) 67 68 func (o *ociDownloader) Download(ctx context.Context, ref *SteampipeImageRef, imageType ImageType, destDir string) (*SteampipeImage, error) { 69 var mediaTypes []string 70 Image := o.newSteampipeImage() 71 Image.ImageRef = ref 72 mediaType, err := MediaTypeForPlatform(imageType) 73 if err != nil { 74 return nil, err 75 } 76 77 mediaTypes = append(mediaTypes, mediaType...) 78 mediaTypes = append(mediaTypes, SharedMediaTypes(imageType)...) 79 mediaTypes = append(mediaTypes, ConfigMediaTypes()...) 80 81 log.Println("[TRACE] ociDownloader.Download:", "downloading", ref.ActualImageRef()) 82 83 // Download the files 84 imageDesc, _, configBytes, layers, err := o.Pull(ctx, ref.ActualImageRef(), mediaTypes, destDir) 85 if err != nil { 86 return nil, err 87 } 88 89 Image.OCIDescriptor = imageDesc 90 Image.Config, err = newSteampipeImageConfig(configBytes) 91 if err != nil { 92 return nil, errors.New("invalid image - missing $config") 93 } 94 95 // Get the metadata 96 switch imageType { 97 case ImageTypeDatabase: 98 Image.Database, err = getDBImageData(layers) 99 case ImageTypeFdw: 100 Image.Fdw, err = getFdwImageData(layers) 101 case ImageTypePlugin: 102 Image.Plugin, err = getPluginImageData(layers) 103 case ImageTypeAssets: 104 Image.Assets, err = getAssetImageData(layers) 105 106 default: 107 return nil, errors.New("invalid Type - image types are: plugin, db, fdw") 108 } 109 110 if err != nil { 111 return nil, err 112 } 113 return Image, nil 114 } 115 116 func getAssetImageData(layers []ocispec.Descriptor) (*AssetsImage, error) { 117 var assetImage AssetsImage 118 119 // get the report dir 120 foundLayers := findLayersForMediaType(layers, MediaTypeAssetReportLayer) 121 if len(foundLayers) > 0 { 122 assetImage.ReportUI = foundLayers[0].Annotations["org.opencontainers.image.title"] 123 } 124 125 return &assetImage, nil 126 } 127 128 func getDBImageData(layers []ocispec.Descriptor) (*DbImage, error) { 129 res := &DbImage{} 130 131 // get the binary jar file 132 mediaType, err := MediaTypeForPlatform("db") 133 if err != nil { 134 return nil, err 135 } 136 foundLayers := findLayersForMediaType(layers, mediaType[0]) 137 if len(foundLayers) != 1 { 138 return nil, fmt.Errorf("invalid Image - should contain 1 installation file per platform, found %d", len(foundLayers)) 139 } 140 res.ArchiveDir = foundLayers[0].Annotations["org.opencontainers.image.title"] 141 142 // get the readme file info 143 foundLayers = findLayersForMediaType(layers, MediaTypeDbDocLayer) 144 if len(foundLayers) > 0 { 145 res.ReadmeFile = foundLayers[0].Annotations["org.opencontainers.image.title"] 146 } 147 148 // get the license file info 149 foundLayers = findLayersForMediaType(layers, MediaTypeDbLicenseLayer) 150 if len(foundLayers) > 0 { 151 res.LicenseFile = foundLayers[0].Annotations["org.opencontainers.image.title"] 152 } 153 return res, nil 154 } 155 156 func getFdwImageData(layers []ocispec.Descriptor) (*HubImage, error) { 157 res := &HubImage{} 158 159 // get the binary (steampipe-postgres-fdw.so) info 160 mediaType, err := MediaTypeForPlatform("fdw") 161 if err != nil { 162 return nil, err 163 } 164 foundLayers := findLayersForMediaType(layers, mediaType[0]) 165 if len(foundLayers) != 1 { 166 return nil, fmt.Errorf("invalid image - image should contain 1 binary file per platform, found %d", len(foundLayers)) 167 } 168 res.BinaryFile = foundLayers[0].Annotations["org.opencontainers.image.title"] 169 170 //sourcePath := filepath.Join(tempDir.Path, fileName) 171 172 // get the control file info 173 foundLayers = findLayersForMediaType(layers, MediaTypeFdwControlLayer) 174 if len(foundLayers) != 1 { 175 return nil, fmt.Errorf("invalid image - image should contain 1 control file, found %d", len(foundLayers)) 176 } 177 res.ControlFile = foundLayers[0].Annotations["org.opencontainers.image.title"] 178 179 // get the sql file info 180 foundLayers = findLayersForMediaType(layers, MediaTypeFdwSqlLayer) 181 if len(foundLayers) != 1 { 182 return nil, fmt.Errorf("invalid image - image should contain 1 SQL file, found %d", len(foundLayers)) 183 } 184 res.SqlFile = foundLayers[0].Annotations["org.opencontainers.image.title"] 185 186 // get the readme file info 187 foundLayers = findLayersForMediaType(layers, MediaTypeFdwDocLayer) 188 if len(foundLayers) > 0 { 189 res.ReadmeFile = foundLayers[0].Annotations["org.opencontainers.image.title"] 190 } 191 192 // get the license file info 193 foundLayers = findLayersForMediaType(layers, MediaTypeFdwLicenseLayer) 194 if len(foundLayers) > 0 { 195 res.LicenseFile = foundLayers[0].Annotations["org.opencontainers.image.title"] 196 } 197 return res, nil 198 } 199 200 func getPluginImageData(layers []ocispec.Descriptor) (*PluginImage, error) { 201 res := &PluginImage{} 202 var foundLayers []ocispec.Descriptor 203 // get the binary plugin file info 204 // iterate in order of mediatypes - as given by MediaTypeForPlatform (see function docs) 205 mediaTypes, err := MediaTypeForPlatform("plugin") 206 if err != nil { 207 return nil, err 208 } 209 210 for _, mediaType := range mediaTypes { 211 // find out the layer with the correct media type 212 foundLayers = findLayersForMediaType(layers, mediaType) 213 if len(foundLayers) == 1 { 214 // when found, assign and exit 215 res.BinaryFile = foundLayers[0].Annotations["org.opencontainers.image.title"] 216 res.BinaryDigest = string(foundLayers[0].Digest) 217 res.BinaryArchitecture = constants.ArchAMD64 218 if strings.Contains(mediaType, constants.ArchARM64) { 219 res.BinaryArchitecture = constants.ArchARM64 220 } 221 break 222 } 223 // loop over to the next one 224 log.Println("[TRACE] could not find data for", mediaType) 225 log.Println("[TRACE] falling back to the next one, if any") 226 } 227 if len(res.BinaryFile) == 0 { 228 return nil, fmt.Errorf("invalid image - should contain 1 binary file per platform, found %d", len(foundLayers)) 229 } 230 231 // get the docs dir 232 foundLayers = findLayersForMediaType(layers, MediaTypePluginDocsLayer) 233 if len(foundLayers) > 0 { 234 res.DocsDir = foundLayers[0].Annotations["org.opencontainers.image.title"] 235 } 236 237 // get the .spc config / connections file dir 238 foundLayers = findLayersForMediaType(layers, MediaTypePluginSpcLayer) 239 if len(foundLayers) > 0 { 240 res.ConfigFileDir = foundLayers[0].Annotations["org.opencontainers.image.title"] 241 } 242 243 // get the license file info 244 foundLayers = findLayersForMediaType(layers, MediaTypePluginLicenseLayer) 245 if len(foundLayers) > 0 { 246 res.LicenseFile = foundLayers[0].Annotations["org.opencontainers.image.title"] 247 } 248 249 return res, nil 250 } 251 252 func findLayersForMediaType(layers []ocispec.Descriptor, mediaType string) []ocispec.Descriptor { 253 log.Println("[TRACE] looking for", mediaType) 254 var matchedLayers []ocispec.Descriptor 255 256 for _, layer := range layers { 257 if layer.MediaType == mediaType { 258 matchedLayers = append(matchedLayers, layer) 259 } 260 } 261 return matchedLayers 262 }