// pruneLocks removes every expired locks from the database func pruneLocks() { now := time.Now() // Delete every expired locks it, _ := cayley.StartPath(store, "locked").In("locked").Save("locked_until", "locked_until").Save("locked_by", "locked_by").BuildIterator().Optimize() defer it.Close() for cayley.RawNext(it) { tags := make(map[string]graph.Value) it.TagResults(tags) n := store.NameOf(it.Result()) t := store.NameOf(tags["locked_until"]) o := store.NameOf(tags["locked_by"]) tt, _ := strconv.ParseInt(t, 10, 64) if now.Unix() > tt { log.Debugf("lock %s owned by %s has expired.", n, o) tr := cayley.NewTransaction() tr.RemoveQuad(cayley.Quad(n, "locked", "locked", "")) tr.RemoveQuad(cayley.Quad(n, "locked_until", t, "")) tr.RemoveQuad(cayley.Quad(n, "locked_by", o, "")) err := store.ApplyTransaction(tr) if err != nil { log.Errorf("failed transaction (pruneLocks): %s", err) continue } log.Debugf("lock %s has been successfully pruned.", n) } } if it.Err() != nil { log.Errorf("failed query in Unlock: %s", it.Err()) } }
func (nd *Node) WriteData(data []byte, offset int64) error { if offset%BLOCK_SIZE != 0 { return errors.New(fmt.Sprintf("%d is not a valid offset for block size %d", offset, BLOCK_SIZE)) } hash := Hash(data) transaction := graph.NewTransaction() // Determine if we already have a block for this offset linkName := fmt.Sprint("offset-", offset) if existingBlockHash := nd.BlockWithOffset(offset); existingBlockHash != "" { transaction.RemoveQuad(cayley.Quad(nd.Id, linkName, string(existingBlockHash), "")) } transaction.AddQuad(cayley.Quad(nd.Id, linkName, hash, "")) if err := nd.graph.ApplyTransaction(transaction); err != nil { return err } if _, err := Write(hash, data); err != nil { return err } return nil }
// UpdateFlag creates a flag or update an existing flag's value func UpdateFlag(name, value string) error { if name == "" || value == "" { log.Warning("could not insert a flag which has an empty name or value") return cerrors.NewBadRequestError("could not insert a flag which has an empty name or value") } // Initialize transaction t := cayley.NewTransaction() // Get current flag value currentValue, err := GetFlagValue(name) if err != nil { return err } // Build transaction name = "flag:" + name if currentValue != "" { t.RemoveQuad(cayley.Quad(name, "value", currentValue, "")) } t.AddQuad(cayley.Quad(name, "value", value, "")) // Apply transaction if err = store.ApplyTransaction(t); err != nil { log.Errorf("failed transaction (UpdateFlag): %s", err) return ErrTransaction } // Return return nil }
// pruneLocks removes every expired locks from the database func pruneLocks() { now := time.Now() // Delete every expired locks tr := cayley.NewTransaction() it, _ := cayley.StartPath(store, "locked").In("locked").Save("locked_until", "locked_until").Save("locked_by", "locked_by").BuildIterator().Optimize() defer it.Close() for cayley.RawNext(it) { tags := make(map[string]graph.Value) it.TagResults(tags) n := store.NameOf(it.Result()) t := store.NameOf(tags["locked_until"]) o := store.NameOf(tags["locked_by"]) tt, _ := strconv.ParseInt(t, 10, 64) if now.Unix() > tt { log.Debugf("Lock %s owned by %s has expired.", n, o) tr.RemoveQuad(cayley.Quad(n, "locked", "locked", "")) tr.RemoveQuad(cayley.Quad(n, "locked_until", t, "")) tr.RemoveQuad(cayley.Quad(n, "locked_by", o, "")) } } store.ApplyTransaction(tr) }
// Lock tries to set a temporary lock in the database. // If a lock already exists with the given name/owner, then the lock is renewed // // Lock does not block, instead, it returns true and its expiration time // is the lock has been successfully acquired or false otherwise func Lock(name string, duration time.Duration, owner string) (bool, time.Time) { pruneLocks() until := time.Now().Add(duration) untilString := strconv.FormatInt(until.Unix(), 10) // Try to get the expiration time of a lock with the same name/owner currentExpiration, err := toValue(cayley.StartPath(store, name).Has("locked_by", owner).Out("locked_until")) if err == nil && currentExpiration != "" { // Renew our lock if currentExpiration == untilString { return true, until } t := cayley.NewTransaction() t.RemoveQuad(cayley.Quad(name, "locked_until", currentExpiration, "")) t.AddQuad(cayley.Quad(name, "locked_until", untilString, "")) // It is not necessary to verify if the lock is ours again in the transaction // because if someone took it, the lock's current expiration probably changed and the transaction will fail return store.ApplyTransaction(t) == nil, until } t := cayley.NewTransaction() t.AddQuad(cayley.Quad(name, "locked", "locked", "")) // Necessary to make the transaction fails if the lock already exists (and has not been pruned) t.AddQuad(cayley.Quad(name, "locked_until", untilString, "")) t.AddQuad(cayley.Quad(name, "locked_by", owner, "")) glog.SetStderrThreshold("FATAL") success := store.ApplyTransaction(t) == nil glog.SetStderrThreshold("ERROR") return success, until }
func (ng *NodeGraph) removeNode(nd *Node) (err error) { if len(nd.Children()) > 0 { return errors.New("Can't delete node with children, must delete children first") } transaction := cayley.NewTransaction() if nd.Mode() > 0 { transaction.RemoveQuad(cayley.Quad(nd.Id, modeLink, fmt.Sprint(int(nd.Mode())), "")) } if !nd.MTime().IsZero() { transaction.RemoveQuad(cayley.Quad(nd.Id, mTimeLink, fmt.Sprint(nd.MTime().Format(timeFormat)), "")) } if nd.Name() != "" { transaction.RemoveQuad(cayley.Quad(nd.Id, nameLink, nd.Name(), "")) } if nd.Parent() != nil { transaction.RemoveQuad(cayley.Quad(nd.Id, parentLink, nd.Parent().Id, "")) } if nd.Type() != "" { transaction.RemoveQuad(cayley.Quad(nd.Id, typeLink, nd.Type(), "")) } err = ng.ApplyTransaction(transaction) if err == nil { nd.mode = os.FileMode(0) nd.mTime = time.Time{} nd.name = "" nd.parentId = "" nd.Id = "" } return }
// Unlock unlocks a lock specified by its name if I own it func Unlock(name, owner string) { unlocked := 0 it, _ := cayley.StartPath(store, name).Has("locked", "locked").Has("locked_by", owner).Save("locked_until", "locked_until").BuildIterator().Optimize() defer it.Close() for cayley.RawNext(it) { tags := make(map[string]graph.Value) it.TagResults(tags) t := cayley.NewTransaction() t.RemoveQuad(cayley.Quad(name, "locked", "locked", "")) t.RemoveQuad(cayley.Quad(name, "locked_until", store.NameOf(tags["locked_until"]), "")) t.RemoveQuad(cayley.Quad(name, "locked_by", owner, "")) err := store.ApplyTransaction(t) if err != nil { log.Errorf("failed transaction (Unlock): %s", err) } unlocked++ } if it.Err() != nil { log.Errorf("failed query in Unlock: %s", it.Err()) } if unlocked > 1 { // We should never see this, it would mean that our database doesn't ensure quad uniqueness // and that the entire lock system is jeopardized. log.Errorf("found inconsistency in Unlock: matched %d times a locked named: %s", unlocked, name) } }
func (nd *Node) save() (err error) { if nd.name == "" && nd.Name() == "" { return errors.New("Cannot add nameless file") } else if nd.Parent() == nil && nd.parentId == "" && nd.Id != RootNodeId { return errors.New("Cannot add file without parent") } staleQuads := graph.NewTransaction() newQuads := graph.NewTransaction() if name := nd.Name(); nd.name != name && name != "" && nd.name != "" { staleQuads.RemoveQuad(cayley.Quad(nd.Id, nameLink, name, "")) } if nd.name != "" && nd.name != nd.Name() { newQuads.AddQuad(cayley.Quad(nd.Id, nameLink, nd.name, "")) nd.name = "" } if mimeType := nd.Type(); nd.mimeType != mimeType && mimeType != "" && nd.mimeType != "" { staleQuads.RemoveQuad(cayley.Quad(nd.Id, typeLink, mimeType, "")) } if nd.mimeType != "" && nd.mimeType != nd.Type() { newQuads.AddQuad(cayley.Quad(nd.Id, typeLink, nd.mimeType, "")) nd.mimeType = "" } if mode := int(nd.Mode()); int(nd.mode) != mode && mode != 0 && int(nd.mode) != 0 { staleQuads.RemoveQuad(cayley.Quad(nd.Id, modeLink, fmt.Sprint(mode), "")) } if int(nd.mode) > 0 && nd.mode != nd.Mode() { newQuads.AddQuad(cayley.Quad(nd.Id, modeLink, fmt.Sprint(int(nd.mode)), "")) nd.mode = os.FileMode(0) } if mTime := nd.MTime(); nd.mTime != mTime && !mTime.IsZero() && !nd.mTime.IsZero() { staleQuads.RemoveQuad(cayley.Quad(nd.Id, mTimeLink, nd.MTime().Format(timeFormat), "")) } if !nd.mTime.IsZero() && nd.mTime != nd.MTime() { newQuads.AddQuad(cayley.Quad(nd.Id, mTimeLink, nd.mTime.Format(timeFormat), "")) nd.mTime = time.Time{} } if parent := nd.Parent(); parent != nil && parent.Id != nd.parentId && nd.parentId != "" { staleQuads.RemoveQuad(cayley.Quad(nd.Id, parentLink, nd.Parent().Id, "")) } else if parent != nil && parent.Id == nd.parentId { nd.parentId = "" } if nd.parentId != "" { newQuads.AddQuad(cayley.Quad(nd.Id, parentLink, nd.parentId, "")) nd.parentId = "" } if err = nd.graph.ApplyTransaction(staleQuads); err != nil { return } else if err = nd.graph.ApplyTransaction(newQuads); err != nil { return } return nil }
// MarkNotificationAsSent marks a notification as sent. func MarkNotificationAsSent(node string) { // Initialize transaction t := cayley.NewTransaction() t.RemoveQuad(cayley.Quad(node, "isSent", strconv.FormatBool(false), "")) t.AddQuad(cayley.Quad(node, "isSent", strconv.FormatBool(true), "")) // Apply transaction store.ApplyTransaction(t) }
/* Calls the extractArticleData package to obtain Article Data and then adds the information to the database */ func submitLink(w http.ResponseWriter, r *http.Request, store *cayley.Handle) { data := extractArticleData.GetDataFromArticle(r.FormValue("UploadLink")) store.AddQuad(cayley.Quad(data.Link, "has_parent", data.Parent, "")) store.AddQuad(cayley.Quad(data.Link, "has_date", data.Date, "")) store.AddQuad(cayley.Quad(data.Link, "has_content", data.Content, "")) store.AddQuad(cayley.Quad(data.Link, "has_title", data.Title, "")) store.AddQuad(cayley.Quad(data.Link, "has_description", data.Description, "")) store.AddQuad(cayley.Quad(data.Link, "has_image", data.Image, "")) for _, entity := range data.Entities { store.AddQuad(cayley.Quad(data.Link, "has_entity", entity.Name, "")) for _, class := range entity.Classes { store.AddQuad(cayley.Quad(entity.Name, "has_class", class, "")) } for _, category := range entity.Categories { store.AddQuad(cayley.Quad(entity.Name, "has_category", category, "")) } } }
func main() { //graph.InitQuadStore("bolt", dbPath, nil) store, err := cayley.NewGraph("bolt", dbPath, nil) if err != nil { fmt.Println("error in creating database", err) } err = store.AddQuad(cayley.Quad("food", "is", "good", "")) // if err != nil { // fmt.Println("write error", err) // } err = store.AddQuad(cayley.Quad("nothing", "is", "good", "")) }
func (s *Storage) SaveUser(u *User) { s.AddQuad(cayley.Quad(u.Name, "is", "user", "")) p := cayley.StartPath(u.getStorage(), u.Name).Out("free at") iterationTime := u.IterationTime() it := p.BuildIterator() for cayley.RawNext(it) { s.RemoveQuad(cayley.Quad(u.Name, "free at", s.NameOf(it.Result()), "")) } s.AddQuad(cayley.Quad(u.Name, "free at", strconv.FormatInt(iterationTime, 10), "")) }
func (db Db) AddUser(r io.Reader) (string, error) { uuid := uuid.NewUUID() if uuid == nil { log.Fatal("uuid is nil") } fmt.Println(uuid) decoder := json.NewDecoder(r) var u User err := decoder.Decode(&u) if err != nil { return "", fmt.Errorf("invalid json") } fmt.Println(u) ok := validEmail(u.Email) if !ok { return "", fmt.Errorf("invalid email") } db.Store.AddQuad(cayley.Quad("person:"+uuid.String(), "type", "Person", "")) // store.AddQuad(cayley.Quad("person:UUID", "email", u.email, "")) // store.AddQuad(cayley.Quad("person:UUID", "password", u.password, "")) return db.location, nil }
// Unlock unlocks a lock specified by its name if I own it func Unlock(name, owner string) { pruneLocks() t := cayley.NewTransaction() it, _ := cayley.StartPath(store, name).Has("locked", "locked").Has("locked_by", owner).Save("locked_until", "locked_until").BuildIterator().Optimize() defer it.Close() for cayley.RawNext(it) { tags := make(map[string]graph.Value) it.TagResults(tags) t.RemoveQuad(cayley.Quad(name, "locked", "locked", "")) t.RemoveQuad(cayley.Quad(name, "locked_until", store.NameOf(tags["locked_until"]), "")) t.RemoveQuad(cayley.Quad(name, "locked_by", owner, "")) } store.ApplyTransaction(t) }
func (s *localSignallingLayer) LinkServiceToAPI(serviceIdentifier string, version string, apiVersion string) error { var err = s.base.LinkServiceToAPI(serviceIdentifier, version, apiVersion) if err == nil { var apiNodeID = utils.CreateAPIVersionKey(apiVersion) var serviceVersionNodeID = utils.CreateServiceVersionKey(serviceIdentifier, version) err = s.graph.AddQuad(cayley.Quad(apiNodeID, utils.ContainsVersionRel, serviceVersionNodeID, "")) if err != nil { return err } err = s.graph.AddQuad(cayley.Quad(serviceVersionNodeID, utils.InApiRel, apiNodeID, "")) if err != nil { return err } } return err }
func (s *mgoDecorator) LinkServiceToAPI(serviceIdentifier string, version string, apiVersion string) error { var err = s.base.LinkServiceToAPI(serviceIdentifier, version, apiVersion) if err != nil { return err } var apiNodeID = utils.CreateAPIVersionKey(apiVersion) var serviceVersionNodeID = utils.CreateServiceVersionKey(serviceIdentifier, version) err = s.store.AddQuad(cayley.Quad(apiNodeID, utils.ContainsVersionRel, serviceVersionNodeID, "")) if err != nil { return err } err = s.store.AddQuad(cayley.Quad(serviceVersionNodeID, utils.InApiRel, apiNodeID, "")) if err != nil { return err } return nil }
// DeleteVulnerability deletes the vulnerability having the given ID func DeleteVulnerability(id string) error { vulnerability, err := FindOneVulnerability(id, FieldVulnerabilityAll) if err != nil { return err } t := cayley.NewTransaction() t.RemoveQuad(cayley.Quad(vulnerability.Node, FieldVulnerabilityID, vulnerability.ID, "")) t.RemoveQuad(cayley.Quad(vulnerability.Node, FieldVulnerabilityLink, vulnerability.Link, "")) t.RemoveQuad(cayley.Quad(vulnerability.Node, FieldVulnerabilityPriority, string(vulnerability.Priority), "")) t.RemoveQuad(cayley.Quad(vulnerability.Node, FieldVulnerabilityDescription, vulnerability.Description, "")) for _, p := range vulnerability.FixedInNodes { t.RemoveQuad(cayley.Quad(vulnerability.Node, FieldVulnerabilityFixedIn, p, "")) } if err := store.ApplyTransaction(t); err != nil { log.Errorf("failed transaction (DeleteVulnerability): %s", err) return ErrTransaction } return nil }
// Healthcheck simply adds and then remove a quad in Cayley to ensure it is working // It returns true when everything is ok func Healthcheck() health.Status { var err error if store != nil { t := cayley.NewTransaction() q := cayley.Quad("cayley", "is", "healthy", "") t.AddQuad(q) t.RemoveQuad(q) glog.SetStderrThreshold("FATAL") // TODO REMOVE ME err = store.ApplyTransaction(t) glog.SetStderrThreshold("ERROR") // TODO REMOVE ME } return health.Status{IsEssential: true, IsHealthy: err == nil, Details: nil} }
// InsertNotifications stores multiple Notification in the database // It uses the given NotificationWrapper to convert these notifications to // something that can be stored in the database. func InsertNotifications(notifications []Notification, wrapper NotificationWrapper) error { if len(notifications) == 0 { return nil } // Do not send notifications if there are too many of them (first update for example) if len(notifications) > maxNotifications { log.Noticef("Ignoring %d notifications", len(notifications)) return nil } // Initialize transaction t := cayley.NewTransaction() // Iterate over all the vulnerabilities we need to insert for _, notification := range notifications { // Wrap notification wrappedNotification, err := wrapper.Wrap(notification) if err != nil { return err } node := "notification:" + uuid.New() t.AddQuad(cayley.Quad(node, FieldIs, "notification", "")) t.AddQuad(cayley.Quad(node, "type", wrappedNotification.Type, "")) t.AddQuad(cayley.Quad(node, "data", wrappedNotification.Data, "")) t.AddQuad(cayley.Quad(node, "isSent", strconv.FormatBool(false), "")) } // Apply transaction if err := store.ApplyTransaction(t); err != nil { log.Errorf("failed transaction (InsertNotifications): %s", err) return ErrTransaction } return nil }
func (s *mgoDecorator) AddServiceVersion(serviceIdentifier string, version string) error { var err = s.base.AddServiceVersion(serviceIdentifier, version) if err != nil { return err } session, err := createMgoSession(s.dial) if err != nil { return fmt.Errorf("Could not establish a connection to mgo: %s", err.Error()) } defer session.Close() c := session.DB("test").C("serviceVersions") var subject = serviceVersion{ Id: utils.CreateServiceVersionKey(serviceIdentifier, version), Service: serviceIdentifier, Version: version} err = c.Insert(subject) if err != nil { return err } var serviceNodeID = utils.CreateServiceDefinitionKey(serviceIdentifier) err = s.store.AddQuad(cayley.Quad(serviceNodeID, utils.ContainsVersionRel, subject.Id, "")) if err != nil { return err } err = s.store.AddQuad(cayley.Quad(subject.Id, utils.OfServiceRel, serviceNodeID, "")) if err != nil { return err } return nil }
func open() { path := "/tmp/user-db" // Initialize the database graph.InitQuadStore("bolt", path, nil) // Open and use the database store, err := cayley.NewGraph("bolt", path, nil) if err != nil { log.Fatalln(err) } store.AddQuad(cayley.Quad("person:sophie", "type", "Person", "")) store.AddQuad(cayley.Quad("person:sophie", "name", "Sophie Grégoire", "")) store.AddQuad(cayley.Quad("person:sophie", "born", "1974", "")) store.AddQuad(cayley.Quad("person:sophie", "lives in", "country:canada", "")) store.AddQuad(cayley.Quad("person:justin", "type", "Person", "")) store.AddQuad(cayley.Quad("person:justin", "name", "Justin Trudeau", "")) store.AddQuad(cayley.Quad("person:justin", "born", "1972", "")) store.AddQuad(cayley.Quad("person:justin", "lives in", "country:canada", "")) store.AddQuad(cayley.Quad("person:justin", "in love with", "person:sophie", "")) }
func TestToValue(t *testing.T) { Open("memstore", "") defer Close() // toValue() v, err := toValue(cayley.StartPath(store, "tests").Out("are")) assert.Nil(t, err, "toValue should work even if the requested path leads to nothing") assert.Equal(t, "", v, "toValue should return an empty string if the requested path leads to nothing") store.AddQuad(cayley.Quad("tests", "are", "awesome", "")) v, err = toValue(cayley.StartPath(store, "tests").Out("are")) assert.Nil(t, err, "toValue should have worked") assert.Equal(t, "awesome", v, "toValue did not return the expected value") store.AddQuad(cayley.Quad("tests", "are", "running", "")) v, err = toValue(cayley.StartPath(store, "tests").Out("are")) assert.NotNil(t, err, "toValue should return an error and an empty string if the path leads to multiple values") assert.Equal(t, "", v, "toValue should return an error and an empty string if the path leads to multiple values") // toValues() vs, err := toValues(cayley.StartPath(store, "CoreOS").Out(FieldIs)) assert.Nil(t, err, "toValues should work even if the requested path leads to nothing") assert.Len(t, vs, 0, "toValue should return an empty array if the requested path leads to nothing") words := []string{"powerful", "lightweight"} for i, word := range words { store.AddQuad(cayley.Quad("CoreOS", FieldIs, word, "")) v, err := toValues(cayley.StartPath(store, "CoreOS").Out(FieldIs)) assert.Nil(t, err, "toValues should have worked") assert.Len(t, v, i+1, "toValues did not return the right amount of values") for _, e := range words[:i+1] { assert.Contains(t, v, e, "toValues did not return the values we expected") } } // toValue(s)() and empty values store.AddQuad(cayley.Quad("bob", "likes", "", "")) v, err = toValue(cayley.StartPath(store, "bob").Out("likes")) assert.Nil(t, err, "toValue should work even if the requested path leads to nothing") assert.Equal(t, "", v, "toValue should return an empty string if the requested path leads to nothing") store.AddQuad(cayley.Quad("bob", "likes", "running", "")) v, err = toValue(cayley.StartPath(store, "bob").Out("likes")) assert.Nil(t, err, "toValue should have worked") assert.Equal(t, "running", v, "toValue did not return the expected value") store.AddQuad(cayley.Quad("bob", "likes", "swimming", "")) va, err := toValues(cayley.StartPath(store, "bob").Out("likes")) assert.Nil(t, err, "toValues should have worked") assert.Len(t, va, 2, "toValues should have returned 2 values") }
// InsertVulnerabilities inserts or updates several vulnerabilities in the database in one transaction // It ensures that a vulnerability can't be fixed by two packages belonging the same Branch. // During an update, if the vulnerability was previously fixed by a version in a branch and a new package of that branch is specified, the previous one is deleted // Otherwise, it simply adds the defined packages, there is currently no way to delete affected packages. // // ID, Link, Priority and FixedInNodes fields have to be specified. Description is optionnal. func InsertVulnerabilities(vulnerabilities []*Vulnerability) ([]Notification, error) { if len(vulnerabilities) == 0 { return []Notification{}, nil } // Create required data structure var err error t := cayley.NewTransaction() cachedVulnerabilities := make(map[string]*Vulnerability) newVulnerabilityNotifications := make(map[string]*NewVulnerabilityNotification) vulnerabilityPriorityIncreasedNotifications := make(map[string]*VulnerabilityPriorityIncreasedNotification) vulnerabilityPackageChangedNotifications := make(map[string]*VulnerabilityPackageChangedNotification) // Iterate over all the vulnerabilities we need to insert/update for _, vulnerability := range vulnerabilities { // Is the vulnerability already existing ? existingVulnerability, _ := cachedVulnerabilities[vulnerability.ID] if existingVulnerability == nil { existingVulnerability, err = FindOneVulnerability(vulnerability.ID, FieldVulnerabilityAll) if err != nil && err != cerrors.ErrNotFound { return []Notification{}, err } if existingVulnerability != nil { cachedVulnerabilities[vulnerability.ID] = existingVulnerability } } // Don't allow inserting/updating a vulnerability which is fixed in two packages of the same branch if len(vulnerability.FixedInNodes) > 0 { fixedInPackages, err := FindAllPackagesByNodes(vulnerability.FixedInNodes, []string{FieldPackageOS, FieldPackageName}) if err != nil { return []Notification{}, err } fixedInBranches := make(map[string]struct{}) for _, fixedInPackage := range fixedInPackages { branch := fixedInPackage.Branch() if _, branchExists := fixedInBranches[branch]; branchExists { log.Warningf("could not insert vulnerability %s because it is fixed in two packages of the same branch", vulnerability.ID) return []Notification{}, cerrors.NewBadRequestError("could not insert a vulnerability which is fixed in two packages of the same branch") } fixedInBranches[branch] = struct{}{} } } // Insert/Update vulnerability if existingVulnerability == nil { // The vulnerability does not exist, create it // Verify parameters if vulnerability.ID == "" || vulnerability.Link == "" || vulnerability.Priority == "" { log.Warningf("could not insert an incomplete vulnerability [ID: %s, Link: %s, Priority: %s]", vulnerability.ID, vulnerability.Link, vulnerability.Priority) return []Notification{}, cerrors.NewBadRequestError("Could not insert an incomplete vulnerability") } if !vulnerability.Priority.IsValid() { log.Warningf("could not insert a vulnerability which has an invalid priority [ID: %s, Link: %s, Priority: %s]. Valid priorities are: %v.", vulnerability.ID, vulnerability.Link, vulnerability.Priority, types.Priorities) return []Notification{}, cerrors.NewBadRequestError("Could not insert a vulnerability which has an invalid priority") } if len(vulnerability.FixedInNodes) == 0 { log.Warningf("could not insert a vulnerability which doesn't affect any package [ID: %s].", vulnerability.ID) return []Notification{}, cerrors.NewBadRequestError("could not insert a vulnerability which doesn't affect any package") } // Insert it vulnerability.Node = vulnerability.GetNode() cachedVulnerabilities[vulnerability.ID] = vulnerability t.AddQuad(cayley.Quad(vulnerability.Node, FieldIs, FieldVulnerabilityIsValue, "")) t.AddQuad(cayley.Quad(vulnerability.Node, FieldVulnerabilityID, vulnerability.ID, "")) t.AddQuad(cayley.Quad(vulnerability.Node, FieldVulnerabilityLink, vulnerability.Link, "")) t.AddQuad(cayley.Quad(vulnerability.Node, FieldVulnerabilityPriority, string(vulnerability.Priority), "")) t.AddQuad(cayley.Quad(vulnerability.Node, FieldVulnerabilityDescription, vulnerability.Description, "")) for _, p := range vulnerability.FixedInNodes { t.AddQuad(cayley.Quad(vulnerability.Node, FieldVulnerabilityFixedIn, p, "")) } // Add a notification newVulnerabilityNotifications[vulnerability.ID] = &NewVulnerabilityNotification{VulnerabilityID: vulnerability.ID} } else { // The vulnerability already exists, update it if vulnerability.Link != "" && existingVulnerability.Link != vulnerability.Link { t.RemoveQuad(cayley.Quad(existingVulnerability.Node, FieldVulnerabilityLink, existingVulnerability.Link, "")) t.AddQuad(cayley.Quad(existingVulnerability.Node, FieldVulnerabilityLink, vulnerability.Link, "")) existingVulnerability.Link = vulnerability.Link } if vulnerability.Priority != "" && vulnerability.Priority != types.Unknown && existingVulnerability.Priority != vulnerability.Priority { if !vulnerability.Priority.IsValid() { log.Warningf("could not update a vulnerability which has an invalid priority [ID: %s, Link: %s, Priority: %s]. Valid priorities are: %v.", vulnerability.ID, vulnerability.Link, vulnerability.Priority, types.Priorities) return []Notification{}, cerrors.NewBadRequestError("Could not update a vulnerability which has an invalid priority") } // Add a notification about the priority change if the new priority is higher and the vulnerability is not new if vulnerability.Priority.Compare(existingVulnerability.Priority) > 0 { if _, newVulnerabilityNotificationExists := newVulnerabilityNotifications[vulnerability.ID]; !newVulnerabilityNotificationExists { // Any priorityChangeNotification already ? if existingPriorityNotification, _ := vulnerabilityPriorityIncreasedNotifications[vulnerability.ID]; existingPriorityNotification != nil { // There is a priority change notification, replace it but keep the old priority field vulnerabilityPriorityIncreasedNotifications[vulnerability.ID] = &VulnerabilityPriorityIncreasedNotification{OldPriority: existingPriorityNotification.OldPriority, NewPriority: vulnerability.Priority, VulnerabilityID: existingVulnerability.ID} } else { // No previous notification, just add a new one vulnerabilityPriorityIncreasedNotifications[vulnerability.ID] = &VulnerabilityPriorityIncreasedNotification{OldPriority: existingVulnerability.Priority, NewPriority: vulnerability.Priority, VulnerabilityID: existingVulnerability.ID} } } } t.RemoveQuad(cayley.Quad(existingVulnerability.Node, FieldVulnerabilityPriority, string(existingVulnerability.Priority), "")) t.AddQuad(cayley.Quad(existingVulnerability.Node, FieldVulnerabilityPriority, string(vulnerability.Priority), "")) existingVulnerability.Priority = vulnerability.Priority } if vulnerability.Description != "" && existingVulnerability.Description != vulnerability.Description { t.RemoveQuad(cayley.Quad(existingVulnerability.Node, FieldVulnerabilityDescription, existingVulnerability.Description, "")) t.AddQuad(cayley.Quad(existingVulnerability.Node, FieldVulnerabilityDescription, vulnerability.Description, "")) existingVulnerability.Description = vulnerability.Description } if len(vulnerability.FixedInNodes) > 0 && len(utils.CompareStringLists(vulnerability.FixedInNodes, existingVulnerability.FixedInNodes)) != 0 { var removedNodes []string var addedNodes []string existingVulnerabilityFixedInPackages, err := FindAllPackagesByNodes(existingVulnerability.FixedInNodes, []string{FieldPackageOS, FieldPackageName, FieldPackageVersion}) if err != nil { return []Notification{}, err } vulnerabilityFixedInPackages, err := FindAllPackagesByNodes(vulnerability.FixedInNodes, []string{FieldPackageOS, FieldPackageName, FieldPackageVersion}) if err != nil { return []Notification{}, err } for _, p := range vulnerabilityFixedInPackages { // Any already existing link ? fixedInLinkAlreadyExists := false for _, ep := range existingVulnerabilityFixedInPackages { if *p == *ep { // This exact link already exists, we won't insert it again fixedInLinkAlreadyExists = true } else if p.Branch() == ep.Branch() { // A link to this package branch already exist and is not the same version, we will delete it t.RemoveQuad(cayley.Quad(existingVulnerability.Node, FieldVulnerabilityFixedIn, ep.Node, "")) var index int for i, n := range existingVulnerability.FixedInNodes { if n == ep.Node { index = i break } } existingVulnerability.FixedInNodes = append(existingVulnerability.FixedInNodes[index:], existingVulnerability.FixedInNodes[index+1:]...) removedNodes = append(removedNodes, ep.Node) } } if fixedInLinkAlreadyExists == false { t.AddQuad(cayley.Quad(existingVulnerability.Node, FieldVulnerabilityFixedIn, p.Node, "")) existingVulnerability.FixedInNodes = append(existingVulnerability.FixedInNodes, p.Node) addedNodes = append(addedNodes, p.Node) } } // Add notification about the FixedIn modification if the vulnerability is not new if len(removedNodes) > 0 || len(addedNodes) > 0 { if _, newVulnerabilityNotificationExists := newVulnerabilityNotifications[vulnerability.ID]; !newVulnerabilityNotificationExists { // Any VulnerabilityPackageChangedNotification already ? if existingPackageNotification, _ := vulnerabilityPackageChangedNotifications[vulnerability.ID]; existingPackageNotification != nil { // There is a priority change notification, add the packages modifications to it existingPackageNotification.AddedFixedInNodes = append(existingPackageNotification.AddedFixedInNodes, addedNodes...) existingPackageNotification.RemovedFixedInNodes = append(existingPackageNotification.RemovedFixedInNodes, removedNodes...) } else { // No previous notification, just add a new one vulnerabilityPackageChangedNotifications[vulnerability.ID] = &VulnerabilityPackageChangedNotification{VulnerabilityID: vulnerability.ID, AddedFixedInNodes: addedNodes, RemovedFixedInNodes: removedNodes} } } } } } } // Apply transaction if err = store.ApplyTransaction(t); err != nil { log.Errorf("failed transaction (InsertVulnerabilities): %s", err) return []Notification{}, ErrTransaction } // Group all notifications var allNotifications []Notification for _, notification := range newVulnerabilityNotifications { allNotifications = append(allNotifications, notification) } for _, notification := range vulnerabilityPriorityIncreasedNotifications { allNotifications = append(allNotifications, notification) } for _, notification := range vulnerabilityPackageChangedNotifications { allNotifications = append(allNotifications, notification) } return allNotifications, nil }
// InsertLayer insert a single layer in the database // // ID, and EngineVersion fields are required. // ParentNode, OS, InstalledPackagesNodes and RemovedPackagesNodes are optional, // SuccessorsNodes is unnecessary. // // The ID MUST be unique for two different layers. // // // If the Layer already exists, nothing is done, except if the provided engine // version is higher than the existing one, in which case, the OS, // InstalledPackagesNodes and RemovedPackagesNodes fields will be replaced. // // The layer should only contains the newly installed/removed packages // There is no safeguard that prevents from marking a package as newly installed // while it has already been installed in one of its parent. func InsertLayer(layer *Layer) error { // Verify parameters if layer.ID == "" { log.Warning("could not insert a layer which has an empty ID") return cerrors.NewBadRequestError("could not insert a layer which has an empty ID") } // Create required data structures t := cayley.NewTransaction() layer.Node = layer.GetNode() // Try to find an existing layer existingLayer, err := FindOneLayerByNode(layer.Node, FieldLayerAll) if err != nil && err != cerrors.ErrNotFound { return err } if existingLayer != nil && existingLayer.EngineVersion >= layer.EngineVersion { // The layer exists and has an equal or higher engine verison, do nothing return nil } if existingLayer == nil { // Create case: add permanent nodes t.AddQuad(cayley.Quad(layer.Node, FieldIs, FieldLayerIsValue, "")) t.AddQuad(cayley.Quad(layer.Node, FieldLayerID, layer.ID, "")) t.AddQuad(cayley.Quad(layer.Node, FieldLayerParent, layer.ParentNode, "")) } else { // Update case: remove everything before we add updated data t.RemoveQuad(cayley.Quad(layer.Node, FieldLayerOS, existingLayer.OS, "")) for _, pkg := range existingLayer.InstalledPackagesNodes { t.RemoveQuad(cayley.Quad(layer.Node, FieldLayerInstalledPackages, pkg, "")) } for _, pkg := range existingLayer.RemovedPackagesNodes { t.RemoveQuad(cayley.Quad(layer.Node, FieldLayerRemovedPackages, pkg, "")) } t.RemoveQuad(cayley.Quad(layer.Node, FieldLayerEngineVersion, strconv.Itoa(existingLayer.EngineVersion), "")) } // Add OS/Packages t.AddQuad(cayley.Quad(layer.Node, FieldLayerOS, layer.OS, "")) for _, pkg := range layer.InstalledPackagesNodes { t.AddQuad(cayley.Quad(layer.Node, FieldLayerInstalledPackages, pkg, "")) } for _, pkg := range layer.RemovedPackagesNodes { t.AddQuad(cayley.Quad(layer.Node, FieldLayerRemovedPackages, pkg, "")) } t.AddQuad(cayley.Quad(layer.Node, FieldLayerEngineVersion, strconv.Itoa(layer.EngineVersion), "")) // Apply transaction if err = store.ApplyTransaction(t); err != nil { log.Errorf("failed transaction (InsertLayer): %s", err) return ErrTransaction } return nil }
func (a *ACL) groupAddQuad(subject, predicate, object, label string) error { return a.groupstore.AddQuad(cayley.Quad(subject, predicate, object, label)) }
func (a *ACL) profileAddQuad(subject, predicate, object, label string) error { return a.profilestore.AddQuad(cayley.Quad(subject, predicate, object, label)) }
// InsertPackages inserts several packages in the database in one transaction // Packages are stored in linked lists, one per Branch. Each linked list has a start package and an end package defined with types.MinVersion/types.MaxVersion versions // // OS, Name and Version fields have to be specified. // If the insertion is successfull, the Node field is filled and represents the graph node identifier. func InsertPackages(packageParameters []*Package) error { if len(packageParameters) == 0 { return nil } // Verify parameters for _, pkg := range packageParameters { if pkg.OS == "" || pkg.Name == "" || pkg.Version.String() == "" { log.Warningf("could not insert an incomplete package [OS: %s, Name: %s, Version: %s]", pkg.OS, pkg.Name, pkg.Version) return cerrors.NewBadRequestError("could not insert an incomplete package") } } // Iterate over all the packages we need to insert for _, packageParameter := range packageParameters { t := cayley.NewTransaction() // Is the package already existing ? pkg, err := FindOnePackage(packageParameter.OS, packageParameter.Name, packageParameter.Version, []string{}) if err != nil && err != cerrors.ErrNotFound { return err } if pkg != nil { packageParameter.Node = pkg.Node continue } // Get all packages of the same branch (both from local cache and database) branchPackages, err := FindAllPackagesByBranch(packageParameter.OS, packageParameter.Name, []string{FieldPackageOS, FieldPackageName, FieldPackageVersion, FieldPackageNextVersion}) if err != nil { return err } if len(branchPackages) == 0 { // The branch does not exist yet insertingStartPackage := packageParameter.Version == types.MinVersion insertingEndPackage := packageParameter.Version == types.MaxVersion // Create and insert a end package endPackage := &Package{ OS: packageParameter.OS, Name: packageParameter.Name, Version: types.MaxVersion, } endPackage.Node = endPackage.GetNode() t.AddQuad(cayley.Quad(endPackage.Node, FieldIs, FieldPackageIsValue, "")) t.AddQuad(cayley.Quad(endPackage.Node, FieldPackageOS, endPackage.OS, "")) t.AddQuad(cayley.Quad(endPackage.Node, FieldPackageName, endPackage.Name, "")) t.AddQuad(cayley.Quad(endPackage.Node, FieldPackageVersion, endPackage.Version.String(), "")) t.AddQuad(cayley.Quad(endPackage.Node, FieldPackageNextVersion, "", "")) // Create the inserted package if it is different than a start/end package var newPackage *Package if !insertingStartPackage && !insertingEndPackage { newPackage = &Package{ OS: packageParameter.OS, Name: packageParameter.Name, Version: packageParameter.Version, } newPackage.Node = newPackage.GetNode() t.AddQuad(cayley.Quad(newPackage.Node, FieldIs, FieldPackageIsValue, "")) t.AddQuad(cayley.Quad(newPackage.Node, FieldPackageOS, newPackage.OS, "")) t.AddQuad(cayley.Quad(newPackage.Node, FieldPackageName, newPackage.Name, "")) t.AddQuad(cayley.Quad(newPackage.Node, FieldPackageVersion, newPackage.Version.String(), "")) t.AddQuad(cayley.Quad(newPackage.Node, FieldPackageNextVersion, endPackage.Node, "")) packageParameter.Node = newPackage.Node } // Create and insert a start package startPackage := &Package{ OS: packageParameter.OS, Name: packageParameter.Name, Version: types.MinVersion, } startPackage.Node = startPackage.GetNode() t.AddQuad(cayley.Quad(startPackage.Node, FieldIs, FieldPackageIsValue, "")) t.AddQuad(cayley.Quad(startPackage.Node, FieldPackageOS, startPackage.OS, "")) t.AddQuad(cayley.Quad(startPackage.Node, FieldPackageName, startPackage.Name, "")) t.AddQuad(cayley.Quad(startPackage.Node, FieldPackageVersion, startPackage.Version.String(), "")) if !insertingStartPackage && !insertingEndPackage { t.AddQuad(cayley.Quad(startPackage.Node, FieldPackageNextVersion, newPackage.Node, "")) } else { t.AddQuad(cayley.Quad(startPackage.Node, FieldPackageNextVersion, endPackage.Node, "")) } // Set package node if insertingEndPackage { packageParameter.Node = endPackage.Node } else if insertingStartPackage { packageParameter.Node = startPackage.Node } } else { // The branch already exists // Create the package newPackage := &Package{OS: packageParameter.OS, Name: packageParameter.Name, Version: packageParameter.Version} newPackage.Node = "package:" + utils.Hash(newPackage.Key()) packageParameter.Node = newPackage.Node t.AddQuad(cayley.Quad(newPackage.Node, FieldIs, FieldPackageIsValue, "")) t.AddQuad(cayley.Quad(newPackage.Node, FieldPackageOS, newPackage.OS, "")) t.AddQuad(cayley.Quad(newPackage.Node, FieldPackageName, newPackage.Name, "")) t.AddQuad(cayley.Quad(newPackage.Node, FieldPackageVersion, newPackage.Version.String(), "")) // Sort branchPackages by version (including the new package) branchPackages = append(branchPackages, newPackage) sort.Sort(ByVersion(branchPackages)) // Find my prec/succ GraphID in the sorted slice now newPackageKey := newPackage.Key() var pred, succ *Package var found bool for _, p := range branchPackages { equal := p.Key() == newPackageKey if !equal && !found { pred = p } else if found { succ = p break } else if equal { found = true continue } } if pred == nil || succ == nil { log.Warningf("could not find any package predecessor/successor of: [OS: %s, Name: %s, Version: %s].", packageParameter.OS, packageParameter.Name, packageParameter.Version) return cerrors.NewBadRequestError("could not find package predecessor/successor") } // Link the new packages with the branch t.RemoveQuad(cayley.Quad(pred.Node, FieldPackageNextVersion, succ.Node, "")) pred.NextVersionNode = newPackage.Node t.AddQuad(cayley.Quad(pred.Node, FieldPackageNextVersion, newPackage.Node, "")) newPackage.NextVersionNode = succ.Node t.AddQuad(cayley.Quad(newPackage.Node, FieldPackageNextVersion, succ.Node, "")) } // Apply transaction if err := store.ApplyTransaction(t); err != nil { log.Errorf("failed transaction (InsertPackages): %s", err) return ErrTransaction } } // Return return nil }