github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/db/container_owner.go (about)

     1  package db
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  
     7  	sq "github.com/Masterminds/squirrel"
     8  	"github.com/pf-qiu/concourse/v6/atc"
     9  )
    10  
    11  //go:generate counterfeiter . ContainerOwner
    12  
    13  // ContainerOwner designates the data the container should reference that
    14  // identifies its lifecycle. When the owner goes away, the container should
    15  // be garbage collected.
    16  type ContainerOwner interface {
    17  	Find(conn Conn) (sq.Eq, bool, error)
    18  	Create(tx Tx, workerName string) (map[string]interface{}, error)
    19  }
    20  
    21  // NewImageCheckContainerOwner references a container whose image resource this
    22  // container is checking. When the referenced container transitions to another
    23  // state, or disappears, the container can be removed.
    24  func NewImageCheckContainerOwner(
    25  	container CreatingContainer,
    26  	teamID int,
    27  ) ContainerOwner {
    28  	return imageCheckContainerOwner{
    29  		Container: container,
    30  		TeamID:    teamID,
    31  	}
    32  }
    33  
    34  type imageCheckContainerOwner struct {
    35  	Container CreatingContainer
    36  	TeamID    int
    37  }
    38  
    39  func (c imageCheckContainerOwner) Find(Conn) (sq.Eq, bool, error) {
    40  	return sq.Eq(c.sqlMap()), true, nil
    41  }
    42  
    43  func (c imageCheckContainerOwner) Create(Tx, string) (map[string]interface{}, error) {
    44  	return c.sqlMap(), nil
    45  }
    46  
    47  func (c imageCheckContainerOwner) sqlMap() map[string]interface{} {
    48  	return map[string]interface{}{
    49  		"image_check_container_id": c.Container.ID(),
    50  		"team_id":                  c.TeamID,
    51  	}
    52  }
    53  
    54  // NewImageGetContainerOwner references a container whose image resource this
    55  // container is fetching. When the referenced container transitions to another
    56  // state, or disappears, the container can be removed.
    57  func NewImageGetContainerOwner(
    58  	container CreatingContainer,
    59  	teamID int,
    60  ) ContainerOwner {
    61  	return imageGetContainerOwner{
    62  		Container: container,
    63  		TeamID:    teamID,
    64  	}
    65  }
    66  
    67  type imageGetContainerOwner struct {
    68  	Container CreatingContainer
    69  	TeamID    int
    70  }
    71  
    72  func (c imageGetContainerOwner) Find(Conn) (sq.Eq, bool, error) {
    73  	return sq.Eq(c.sqlMap()), true, nil
    74  }
    75  
    76  func (c imageGetContainerOwner) Create(Tx, string) (map[string]interface{}, error) {
    77  	return c.sqlMap(), nil
    78  }
    79  
    80  func (c imageGetContainerOwner) sqlMap() map[string]interface{} {
    81  	return map[string]interface{}{
    82  		"image_get_container_id": c.Container.ID(),
    83  		"team_id":                c.TeamID,
    84  	}
    85  }
    86  
    87  // NewBuildStepContainerOwner references a step within a build. When the build
    88  // becomes non-interceptible or disappears, the container can be removed.
    89  func NewBuildStepContainerOwner(
    90  	buildID int,
    91  	planID atc.PlanID,
    92  	teamID int,
    93  ) ContainerOwner {
    94  	return buildStepContainerOwner{
    95  		BuildID: buildID,
    96  		PlanID:  planID,
    97  		TeamID:  teamID,
    98  	}
    99  }
   100  
   101  type buildStepContainerOwner struct {
   102  	BuildID int
   103  	PlanID  atc.PlanID
   104  	TeamID  int
   105  }
   106  
   107  func (c buildStepContainerOwner) Find(Conn) (sq.Eq, bool, error) {
   108  	return sq.Eq(c.sqlMap()), true, nil
   109  }
   110  
   111  func (c buildStepContainerOwner) Create(Tx, string) (map[string]interface{}, error) {
   112  	return c.sqlMap(), nil
   113  }
   114  
   115  func (c buildStepContainerOwner) sqlMap() map[string]interface{} {
   116  	return map[string]interface{}{
   117  		"build_id": c.BuildID,
   118  		"plan_id":  c.PlanID,
   119  		"team_id":  c.TeamID,
   120  	}
   121  }
   122  
   123  // NewResourceConfigCheckSessionContainerOwner references a resource config and
   124  // worker base resource type, with an expiry. When the resource config or
   125  // worker base resource type disappear, or the expiry is reached, the container
   126  // can be removed.
   127  func NewResourceConfigCheckSessionContainerOwner(
   128  	resourceConfigID int,
   129  	baseResourceTypeID int,
   130  	expiries ContainerOwnerExpiries,
   131  ) ContainerOwner {
   132  	return resourceConfigCheckSessionContainerOwner{
   133  		resourceConfigID:   resourceConfigID,
   134  		baseResourceTypeID: baseResourceTypeID,
   135  		expiries:           expiries,
   136  	}
   137  }
   138  
   139  type resourceConfigCheckSessionContainerOwner struct {
   140  	resourceConfigID   int
   141  	baseResourceTypeID int
   142  	expiries           ContainerOwnerExpiries
   143  }
   144  
   145  type ContainerOwnerExpiries struct {
   146  	Min time.Duration
   147  	Max time.Duration
   148  }
   149  
   150  func (c resourceConfigCheckSessionContainerOwner) Find(conn Conn) (sq.Eq, bool, error) {
   151  	var ids []int
   152  	rows, err := psql.Select("id").
   153  		From("resource_config_check_sessions").
   154  		Where(sq.And{
   155  			sq.Eq{"resource_config_id": c.resourceConfigID},
   156  			sq.Expr("expires_at > NOW()"),
   157  		}).
   158  		RunWith(conn).
   159  		Query()
   160  	if err != nil {
   161  		return nil, false, err
   162  	}
   163  
   164  	for rows.Next() {
   165  		var id int
   166  		err = rows.Scan(&id)
   167  		if err != nil {
   168  			return nil, false, err
   169  		}
   170  
   171  		ids = append(ids, id)
   172  	}
   173  
   174  	if len(ids) == 0 {
   175  		return nil, false, nil
   176  	}
   177  
   178  	return sq.Eq{
   179  		"resource_config_check_session_id": ids,
   180  	}, true, nil
   181  }
   182  
   183  func (c resourceConfigCheckSessionContainerOwner) Create(tx Tx, workerName string) (map[string]interface{}, error) {
   184  	var wbrtID int
   185  	err := psql.Select("id").
   186  		From("worker_base_resource_types").
   187  		Where(sq.Eq{
   188  			"worker_name":           workerName,
   189  			"base_resource_type_id": c.baseResourceTypeID,
   190  		}).
   191  		Suffix("FOR SHARE").
   192  		RunWith(tx).
   193  		QueryRow().
   194  		Scan(&wbrtID)
   195  	if err != nil {
   196  		return nil, fmt.Errorf("get worker base resource type id: %s", err)
   197  	}
   198  
   199  	expiryStmt := fmt.Sprintf(
   200  		"NOW() + LEAST(GREATEST('%d seconds'::interval, NOW() - max(start_time)), '%d seconds'::interval)",
   201  		int(c.expiries.Min.Seconds()),
   202  		int(c.expiries.Max.Seconds()),
   203  	)
   204  
   205  	var rccsID int
   206  	err = psql.Insert("resource_config_check_sessions").
   207  		SetMap(map[string]interface{}{
   208  			"resource_config_id":           c.resourceConfigID,
   209  			"worker_base_resource_type_id": wbrtID,
   210  			"expires_at":                   sq.Expr("(SELECT " + expiryStmt + " FROM workers)"),
   211  		}).
   212  		Suffix(`
   213  			ON CONFLICT (resource_config_id, worker_base_resource_type_id) DO UPDATE SET
   214  				resource_config_id = ?,
   215  				worker_base_resource_type_id = ?
   216  			RETURNING id
   217  		`, c.resourceConfigID, wbrtID).
   218  		RunWith(tx).
   219  		QueryRow().
   220  		Scan(&rccsID)
   221  	if err != nil {
   222  		return nil, fmt.Errorf("upsert resource config check session: %s", err)
   223  	}
   224  
   225  	return map[string]interface{}{
   226  		"resource_config_check_session_id": rccsID,
   227  	}, nil
   228  }