github.com/demonoid81/containerd@v1.3.4/runtime/v2/shim/publisher.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package shim
    18  
    19  import (
    20  	"context"
    21  	"sync"
    22  	"time"
    23  
    24  	v1 "github.com/containerd/containerd/api/services/ttrpc/events/v1"
    25  	"github.com/containerd/containerd/events"
    26  	"github.com/containerd/containerd/namespaces"
    27  	"github.com/containerd/containerd/pkg/ttrpcutil"
    28  	"github.com/containerd/ttrpc"
    29  	"github.com/containerd/typeurl"
    30  	"github.com/sirupsen/logrus"
    31  )
    32  
    33  const (
    34  	queueSize  = 2048
    35  	maxRequeue = 5
    36  )
    37  
    38  type item struct {
    39  	ev    *v1.Envelope
    40  	ctx   context.Context
    41  	count int
    42  }
    43  
    44  func NewPublisher(address string) (*RemoteEventsPublisher, error) {
    45  	client, err := ttrpcutil.NewClient(address)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  
    50  	l := &RemoteEventsPublisher{
    51  		client:  client,
    52  		closed:  make(chan struct{}),
    53  		requeue: make(chan *item, queueSize),
    54  	}
    55  
    56  	go l.processQueue()
    57  	return l, nil
    58  }
    59  
    60  type RemoteEventsPublisher struct {
    61  	client  *ttrpcutil.Client
    62  	closed  chan struct{}
    63  	closer  sync.Once
    64  	requeue chan *item
    65  }
    66  
    67  func (l *RemoteEventsPublisher) Done() <-chan struct{} {
    68  	return l.closed
    69  }
    70  
    71  func (l *RemoteEventsPublisher) Close() (err error) {
    72  	err = l.client.Close()
    73  	l.closer.Do(func() {
    74  		close(l.closed)
    75  	})
    76  	return err
    77  }
    78  
    79  func (l *RemoteEventsPublisher) processQueue() {
    80  	for i := range l.requeue {
    81  		if i.count > maxRequeue {
    82  			logrus.Errorf("evicting %s from queue because of retry count", i.ev.Topic)
    83  			// drop the event
    84  			continue
    85  		}
    86  
    87  		if err := l.forwardRequest(i.ctx, &v1.ForwardRequest{Envelope: i.ev}); err != nil {
    88  			logrus.WithError(err).Error("forward event")
    89  			l.queue(i)
    90  		}
    91  	}
    92  }
    93  
    94  func (l *RemoteEventsPublisher) queue(i *item) {
    95  	go func() {
    96  		i.count++
    97  		// re-queue after a short delay
    98  		time.Sleep(time.Duration(1*i.count) * time.Second)
    99  		l.requeue <- i
   100  	}()
   101  }
   102  
   103  func (l *RemoteEventsPublisher) Publish(ctx context.Context, topic string, event events.Event) error {
   104  	ns, err := namespaces.NamespaceRequired(ctx)
   105  	if err != nil {
   106  		return err
   107  	}
   108  	any, err := typeurl.MarshalAny(event)
   109  	if err != nil {
   110  		return err
   111  	}
   112  	i := &item{
   113  		ev: &v1.Envelope{
   114  			Timestamp: time.Now(),
   115  			Namespace: ns,
   116  			Topic:     topic,
   117  			Event:     any,
   118  		},
   119  		ctx: ctx,
   120  	}
   121  
   122  	if err := l.forwardRequest(i.ctx, &v1.ForwardRequest{Envelope: i.ev}); err != nil {
   123  		l.queue(i)
   124  		return err
   125  	}
   126  
   127  	return nil
   128  }
   129  
   130  func (l *RemoteEventsPublisher) forwardRequest(ctx context.Context, req *v1.ForwardRequest) error {
   131  	_, err := l.client.EventsService().Forward(ctx, req)
   132  	if err == nil {
   133  		return nil
   134  	}
   135  
   136  	if err != ttrpc.ErrClosed {
   137  		return err
   138  	}
   139  
   140  	// Reconnect and retry request
   141  	if err := l.client.Reconnect(); err != nil {
   142  		return err
   143  	}
   144  
   145  	if _, err := l.client.EventsService().Forward(ctx, req); err != nil {
   146  		return err
   147  	}
   148  
   149  	return nil
   150  }