github.com/ijc/docker-app@v0.6.1-0.20181012090447-c7ca8bc483fb/internal/packager/fork.go (about) 1 package packager 2 3 import ( 4 "context" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "strings" 9 10 "github.com/docker/app/internal" 11 "github.com/docker/app/internal/yaml" 12 "github.com/docker/app/pkg/resto" 13 "github.com/docker/app/types/metadata" 14 "github.com/pkg/errors" 15 log "github.com/sirupsen/logrus" 16 ) 17 18 // Fork pulls an application and creates a local fork for the user to modify 19 func Fork(originName, forkName, outputDir string, maintainers []string) error { 20 imgRef, err := splitImageName(originName) 21 if err != nil { 22 return errors.Wrapf(err, "origin %q is not a valid image name", originName) 23 } 24 if forkName == "" { 25 forkName = internal.AppNameFromDir(imgRef.Name) 26 } 27 log.Debugf("Pulling latest version of package %s", originName) 28 payload, err := resto.PullConfigMulti(context.Background(), originName, resto.RegistryOptions{}) 29 if err != nil { 30 return err 31 } 32 33 // create app dir in output-dir 34 namespace, name := splitPackageName(forkName) 35 appDir := filepath.Join(outputDir, internal.DirNameFromAppName(name)) 36 if err := os.MkdirAll(appDir, 0755); err != nil { 37 return errors.Wrap(err, "failed to create output application directory") 38 } 39 40 // Create all the files on disk 41 if err := ExtractImagePayloadToDiskFiles(appDir, payload); err != nil { 42 return err 43 } 44 45 // Update the metadata file 46 fullFilepath := filepath.Join(appDir, internal.MetadataFileName) 47 bytes, err := ioutil.ReadFile(fullFilepath) 48 if err != nil { 49 return errors.Wrapf(err, "failed to read metadata file from: %s", fullFilepath) 50 } 51 bytes, err = updateMetadata(bytes, namespace, name, maintainers) 52 if err != nil { 53 return err 54 } 55 if err := ioutil.WriteFile(fullFilepath, bytes, 0644); err != nil { 56 return errors.Wrapf(err, "failed to write metadata file: %s", fullFilepath) 57 } 58 59 return nil 60 } 61 62 func updateMetadata(raw []byte, namespace, name string, maintainers []string) ([]byte, error) { 63 // retrieve original metadata (maintainer/app name/app tag) 64 meta, err := loadMetadata(raw) 65 if err != nil { 66 return nil, err 67 } 68 // insert retrieved data in fork history section 69 log.Debug("Generating fork metadata") 70 newMeta := metadata.From( 71 meta, 72 metadata.WithName(name), 73 metadata.WithNamespace(namespace), 74 metadata.WithMaintainers(parseMaintainersData(maintainers)), 75 ) 76 77 // update metadata file 78 yamlMeta, err := yaml.Marshal(newMeta) 79 if err != nil { 80 return nil, errors.Wrap(err, "failed to render metadata structure") 81 } 82 return yamlMeta, nil 83 } 84 85 func loadMetadata(raw []byte) (metadata.AppMetadata, error) { 86 var meta metadata.AppMetadata 87 if err := yaml.Unmarshal(raw, &meta); err != nil { 88 return meta, errors.Wrap(err, "failed to parse application metadata") 89 } 90 return meta, nil 91 } 92 93 func splitPackageName(name string) (string, string) { 94 ls := strings.LastIndexByte(name, '/') 95 if ls == -1 { 96 return "", name 97 } 98 99 return name[:ls], name[ls+1:] 100 }