Repository: prometheus/alertmanager
File: silence/silence.go (Go)

Code before:

// Set the specified silence. If a silence with the ID already exists and the modification
// modifies history, the old silence gets expired and a new one is created.
func (s *Silences) Set(sil *pb.Silence) error {
	s.mtx.Lock()
	defer s.mtx.Unlock()

	now := s.nowUTC()
	prev, ok := s.getSilence(sil.Id)
	if sil.Id != "" && !ok {
		return ErrNotFound
	}

	if ok && canUpdate(prev, sil, now) {
		return s.setSilence(sil, now, false)
	}

	// If we got here it's either a new silence or a replacing one.
	if s.limits.MaxSilences != nil {
		if m := s.limits.MaxSilences(); m > 0 && len(s.st)+1 > m {
			return fmt.Errorf("exceeded maximum number of silences: %d (limit: %d)", len(s.st), m)
		}
	}

	if ok && getState(prev, s.nowUTC()) != types.SilenceStateExpired {
		// We cannot update the silence, expire the old one.
		if err := s.expire(prev.Id); err != nil {
			return fmt.Errorf("expire previous silence: %w", err)
		}
	}

	uid, err := uuid.NewV4()
	if err != nil {
		return fmt.Errorf("generate uuid: %w", err)
	}
	sil.Id = uid.String()

	if sil.StartsAt.Before(now) {
		sil.StartsAt = now
	}

	return s.setSilence(sil, now, false)
}

// canUpdate returns true if silence a can be updated to b without
// affecting the historic view of silencing.
func canUpdate(a, b *pb.Silence, now time.Time) bool {
	if !reflect.DeepEqual(a.Matchers, b.Matchers) {
		return false
	}
	// Allowed timestamp modifications depend on the current time.

Review comment (suggestion):
```suggestion
		// We cannot update the silence, expire the old one to leave a history of the silence before modification. 
```

Code after:

// Set the specified silence. If a silence with the ID already exists and the modification
// modifies history, the old silence gets expired and a new one is created.
func (s *Silences) Set(sil *pb.Silence) error {
	s.mtx.Lock()
	defer s.mtx.Unlock()

	now := s.nowUTC()
	prev, ok := s.getSilence(sil.Id)
	if sil.Id != "" && !ok {
		return ErrNotFound
	}

	if ok && canUpdate(prev, sil, now) {
		return s.setSilence(sil, now, false)
	}

	// If we got here it's either a new silence or a replacing one (which would
	// also create a new silence) so we need to make sure we have capacity for
	// the new silence.
	if s.limits.MaxSilences != nil {
		if m := s.limits.MaxSilences(); m > 0 && len(s.st)+1 > m {
			return fmt.Errorf("exceeded maximum number of silences: %d (limit: %d)", len(s.st), m)
		}
	}

	if ok && getState(prev, s.nowUTC()) != types.SilenceStateExpired {
		// We cannot update the silence, expire the old one to leave a history of
		// the silence before modification.
		if err := s.expire(prev.Id); err != nil {
			return fmt.Errorf("expire previous silence: %w", err)
		}
	}

	uid, err := uuid.NewV4()
	if err != nil {
		return fmt.Errorf("generate uuid: %w", err)
	}
	sil.Id = uid.String()

	if sil.StartsAt.Before(now) {
		sil.StartsAt = now
	}

	return s.setSilence(sil, now, false)
}

// canUpdate returns true if silence a can be updated to b without
// affecting the historic view of silencing.
func canUpdate(a, b *pb.Silence, now time.Time) bool {
	if !reflect.DeepEqual(a.Matchers, b.Matchers) {