github.com/criteo/command-launcher@v0.0.0-20230407142452-fb616f546e98/internal/repository/default-repo-index.go (about) 1 package repository 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 "sort" 8 9 log "github.com/sirupsen/logrus" 10 11 "github.com/criteo/command-launcher/internal/command" 12 "github.com/criteo/command-launcher/internal/config" 13 "github.com/criteo/command-launcher/internal/pkg" 14 "github.com/spf13/viper" 15 ) 16 17 /* 18 Internal data structure to represent the local repository index 19 You can find an example of the file in the path specified by 20 config "local_command_repository_dirname" 21 22 Current implementation of the repoIndex is to scan all manifest.mf files 23 in one level subfolders 24 25 Further improvements could store commands as indexes, and laze load 26 further information when necessary to reduce the startup time 27 */ 28 type defaultRepoIndex struct { 29 id string 30 packages map[string]command.PackageManifest 31 packageDirs map[string]string // key is the package name, value is the package directory 32 groupCmds map[string]command.Command // key is in form of [repo]>[package]>[group]>[cmd name] 33 executableCmds map[string]command.Command // key is in form of [repo]>[package]>[group]>[cmd name] 34 systemCmds map[string]command.Command // key is the predefined system command name 35 } 36 37 func newDefaultRepoIndex(id string) (RepoIndex, error) { 38 repoIndex := defaultRepoIndex{ 39 id: id, 40 packages: make(map[string]command.PackageManifest), 41 packageDirs: make(map[string]string), 42 groupCmds: map[string]command.Command{}, 43 executableCmds: map[string]command.Command{}, 44 systemCmds: make(map[string]command.Command), 45 } 46 47 return &repoIndex, nil 48 } 49 50 func (repoIndex *defaultRepoIndex) loadPackages(repoDir string) error { 51 _, err := os.Stat(repoDir) 52 if !os.IsNotExist(err) { 53 files, err := os.ReadDir(repoDir) 54 if err != nil { 55 log.Errorf("cannot read the repo dir: %v", err) 56 return err 57 } 58 59 for _, f := range files { 60 if !f.IsDir() && f.Type()&os.ModeSymlink != os.ModeSymlink { 61 continue 62 } 63 if manifestFile, err := os.Open(filepath.Join(repoDir, f.Name(), "manifest.mf")); err == nil { 64 defer manifestFile.Close() 65 manifest, err := pkg.ReadManifest(manifestFile) 66 if err == nil { 67 repoIndex.packages[manifest.Name()] = manifest 68 repoIndex.packageDirs[manifest.Name()] = filepath.Join(repoDir, f.Name()) 69 } 70 } 71 } 72 } 73 return err 74 } 75 76 func (repoIndex *defaultRepoIndex) extractCmds(repoDir string) { 77 sysPkgName := viper.GetString(config.SYSTEM_PACKAGE_KEY) 78 repoIndex.groupCmds = make(map[string]command.Command) 79 repoIndex.executableCmds = make(map[string]command.Command) 80 // initiate group cmds and exectuable cmds map 81 // the key is in format of [group]#[cmd name] 82 for _, pkg := range repoIndex.packages { 83 if pkg.Commands() != nil { 84 for _, cmd := range pkg.Commands() { 85 cmd.SetPackageDir(repoIndex.packageDirs[pkg.Name()]) 86 cmd.SetNamespace(repoIndex.id, pkg.Name()) 87 repoIndex.registerCmd(pkg, cmd, 88 sysPkgName != "" && pkg.Name() == sysPkgName && repoIndex.id == "default", // always use default repository for system package 89 ) 90 } 91 } 92 } 93 } 94 95 func (repoIndex *defaultRepoIndex) Load(repoDir string) error { 96 err := repoIndex.loadPackages(repoDir) 97 if err != nil { 98 return err 99 } 100 repoIndex.extractCmds(repoDir) 101 return nil 102 } 103 104 func (repoIndex *defaultRepoIndex) Add(pkg command.PackageManifest, repoDir string, pkgDirName string) error { 105 repoIndex.packages[pkg.Name()] = pkg 106 repoIndex.packageDirs[pkg.Name()] = filepath.Join(repoDir, pkgDirName) 107 repoIndex.extractCmds(repoDir) 108 return nil 109 } 110 111 func (repoIndex *defaultRepoIndex) Remove(pkgName string, repoDir string) error { 112 delete(repoIndex.packages, pkgName) 113 delete(repoIndex.packageDirs, pkgName) 114 repoIndex.extractCmds(repoDir) 115 return nil 116 } 117 118 func (repoIndex *defaultRepoIndex) Update(pkg command.PackageManifest, repoDir string, pkgDirName string) error { 119 repoIndex.packages[pkg.Name()] = pkg 120 repoIndex.packageDirs[pkg.Name()] = filepath.Join(repoDir, pkgDirName) 121 repoIndex.extractCmds(repoDir) 122 return nil 123 } 124 125 func (repoIndex *defaultRepoIndex) AllPackages() []command.PackageManifest { 126 pkgs := []command.PackageManifest{} 127 for _, p := range repoIndex.packages { 128 newPkg := p 129 pkgs = append(pkgs, newPkg) 130 } 131 return pkgs 132 } 133 134 func (repoIndex *defaultRepoIndex) Package(name string) (command.PackageManifest, error) { 135 if pkg, exists := repoIndex.packages[name]; exists { 136 return pkg, nil 137 } 138 return nil, fmt.Errorf("cannot find the package '%s'", name) 139 } 140 141 func (repoIndex *defaultRepoIndex) Command(pkg string, group string, name string) (command.Command, error) { 142 if cmd, exist := repoIndex.groupCmds[command.CmdID(repoIndex.id, pkg, group, name)]; exist { 143 return cmd, nil 144 } 145 146 if cmd, exist := repoIndex.executableCmds[command.CmdID(repoIndex.id, pkg, group, name)]; exist { 147 return cmd, nil 148 } 149 150 return nil, fmt.Errorf("cannot find the command %s %s", group, name) 151 } 152 153 func (repoIndex *defaultRepoIndex) AllCommands() []command.Command { 154 cmds := repoIndex.GroupCommands() 155 cmds = append(cmds, repoIndex.ExecutableCommands()...) 156 sort.Slice(cmds, func(i, j int) bool { 157 return cmds[i].ID() < cmds[j].ID() 158 }) 159 return cmds 160 } 161 162 func (repoIndex *defaultRepoIndex) GroupCommands() []command.Command { 163 cmds := make([]command.Command, 0) 164 for _, v := range repoIndex.groupCmds { 165 cmds = append(cmds, v) 166 } 167 sort.Slice(cmds, func(i, j int) bool { 168 return cmds[i].ID() < cmds[j].ID() 169 }) 170 return cmds 171 } 172 173 func (repoIndex *defaultRepoIndex) SystemLoginCommand() command.Command { 174 if cmd, exist := repoIndex.systemCmds[SYSTEM_LOGIN_COMMAND]; exist { 175 return cmd 176 } 177 return nil 178 } 179 180 func (repoIndex *defaultRepoIndex) SystemMetricsCommand() command.Command { 181 if cmd, exist := repoIndex.systemCmds[SYSTEM_METRICS_COMMAND]; exist { 182 return cmd 183 } 184 return nil 185 } 186 187 func (repoIndex *defaultRepoIndex) ExecutableCommands() []command.Command { 188 cmds := make([]command.Command, 0) 189 for _, v := range repoIndex.executableCmds { 190 cmds = append(cmds, v) 191 } 192 sort.Slice(cmds, func(i, j int) bool { 193 return cmds[i].ID() < cmds[j].ID() 194 }) 195 return cmds 196 } 197 198 func (repoIndex *defaultRepoIndex) registerCmd(pkg command.PackageManifest, cmd command.Command, isSystemPkg bool) { 199 switch cmd.Type() { 200 case "group": 201 repoIndex.groupCmds[cmd.ID()] = cmd 202 case "executable": 203 repoIndex.executableCmds[cmd.ID()] = cmd 204 case "system": 205 if isSystemPkg { 206 repoIndex.extractSystemCmds(cmd) 207 } 208 } 209 } 210 211 func (repoIndex *defaultRepoIndex) extractSystemCmds(cmd command.Command) { 212 switch cmd.Name() { 213 case SYSTEM_LOGIN_COMMAND: 214 repoIndex.systemCmds[SYSTEM_LOGIN_COMMAND] = cmd 215 case SYSTEM_METRICS_COMMAND: 216 repoIndex.systemCmds[SYSTEM_METRICS_COMMAND] = cmd 217 } 218 }