package buildrun import ( "sort" "sync" "git.novox.be/novox/mesh-delivery/sdk/buildstep" ) // LanguageOf is the language each build module's toolchain reports hold. var LanguageOf = map[string]string{GoBuildModule: "go", NodeBuildModule: "typescript"} // ToolchainsHeardState is the builder's old state section of the toolchains it heard (design 47 G §604, §546): one key // ., the toolchain as the build seat said it. const ( ToolchainsHeardState = "toolchains-heard" ToolchainsHeardBucket = "mesh-delivery-builder_" + ToolchainsHeardState ) // ToolchainSet holds each node's toolchain of a language, as the builder heard it (design 47 G §604). Safe for use from // the consumer and from a build at once. type ToolchainSet struct { mu sync.Mutex by map[string]map[string]buildstep.Toolchain // language -> node -> entry } // Set takes a node's toolchain in and answers whether the language's entries changed (a node's version, recipe or // digest, or a node appearing). func (s *ToolchainSet) Set(node string, tc buildstep.Toolchain) bool { s.mu.Lock() defer s.mu.Unlock() if s.by == nil { s.by = map[string]map[string]buildstep.Toolchain{} } if s.by[tc.Language] == nil { s.by[tc.Language] = map[string]buildstep.Toolchain{} } cur, had := s.by[tc.Language][node] s.by[tc.Language][node] = tc return !had || cur.Version != tc.Version || cur.Recipe != tc.Recipe || cur.Digest != tc.Digest } // Delete takes a node's toolchain of a language out and answers whether it was held. func (s *ToolchainSet) Delete(node, language string) bool { s.mu.Lock() defer s.mu.Unlock() if _, had := s.by[language][node]; !had { return false } delete(s.by[language], node) return true } // Entries are the nodes' entries of a language, in no particular order. func (s *ToolchainSet) Entries(language string) []buildstep.NodeToolchain { s.mu.Lock() defer s.mu.Unlock() var out []buildstep.NodeToolchain for node, tc := range s.by[language] { out = append(out, buildstep.NodeToolchain{Node: node, Toolchain: tc}) } return out } // AgreedDigest is a digest of the agreed version and the nodes reporting it. type AgreedDigest struct { Digest string Nodes []string } // Digests are the digests the nodes reporting the agreed toolchain's version report, sorted by digest, each with its // nodes sorted (design 47 G §472, §574). A node that reports no digest is left out. func Digests(entries []buildstep.NodeToolchain, agreed buildstep.Toolchain) []AgreedDigest { by := map[string][]string{} for _, e := range entries { if e.Version == agreed.Version && e.Recipe == agreed.Recipe && e.Digest != "" { by[e.Digest] = append(by[e.Digest], e.Node) } } out := make([]AgreedDigest, 0, len(by)) for d, nodes := range by { sort.Strings(nodes) out = append(out, AgreedDigest{Digest: d, Nodes: nodes}) } sort.Slice(out, func(i, j int) bool { return out[i].Digest < out[j].Digest }) return out } // The builder's tools over the toolchains it heard (design 47 G §604, §546). const ( ToolchainsTool = "builder_toolchains" ToolchainsDropTool = "builder_toolchains_drop" ) // Put takes a change of a build module's toolchains bucket in: a put of a node's entry (key .) or its // delete. It answers whether the language's entries changed (a node's version or recipe, or a node appearing or going). // A key or a value that is not an entry of the module's language is left out. func (s *ToolchainSet) Put(module, key string, value []byte, deleted bool) bool { language, ok := LanguageOf[module] if !ok { return false } node, keyLanguage, err := buildstep.ParseToolchainKey(key) if err != nil || keyLanguage != language { return false } s.mu.Lock() defer s.mu.Unlock() if deleted { if _, had := s.by[language][node]; !had { return false } delete(s.by[language], node) return true } tc, err := buildstep.DecodeToolchain(value) if err != nil || tc.Language != language { return false } if s.by == nil { s.by = map[string]map[string]buildstep.Toolchain{} } if s.by[language] == nil { s.by[language] = map[string]buildstep.Toolchain{} } cur, had := s.by[language][node] s.by[language][node] = tc return !had || cur.Version != tc.Version || cur.Recipe != tc.Recipe || cur.Digest != tc.Digest }