github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/healthz/ready.go (about)

     1  package healthz
     2  
     3  import (
     4  	"context"
     5  	"net/http"
     6  
     7  	"github.com/kyma-incubator/compass/components/director/pkg/log"
     8  	"github.com/kyma-incubator/compass/components/director/pkg/persistence"
     9  	"github.com/pkg/errors"
    10  )
    11  
    12  // Repository missing godoc
    13  //go:generate mockery --name=Repository --output=automock --outpkg=automock --case=underscore --disable-version-string
    14  type Repository interface {
    15  	GetVersion(ctx context.Context) (string, bool, error)
    16  }
    17  
    18  // ReadyConfig missing godoc
    19  type ReadyConfig struct {
    20  	SchemaMigrationVersion string `envconfig:"APP_SCHEMA_MIGRATION_VERSION"`
    21  }
    22  
    23  // Ready missing godoc
    24  type Ready struct {
    25  	transactioner    persistence.Transactioner
    26  	schemaCompatible bool
    27  	cfg              ReadyConfig
    28  	repo             Repository
    29  }
    30  
    31  // NewReady missing godoc
    32  func NewReady(transactioner persistence.Transactioner, cfg ReadyConfig, repository Repository) *Ready {
    33  	return &Ready{
    34  		transactioner:    transactioner,
    35  		schemaCompatible: false,
    36  		cfg:              cfg,
    37  		repo:             repository,
    38  	}
    39  }
    40  
    41  // NewReadinessHandler returns handler that returns OK if the db schema is compatible and successful
    42  // db ping is performed or InternalServerError otherwise
    43  func NewReadinessHandler(r *Ready) func(writer http.ResponseWriter, request *http.Request) {
    44  	return func(writer http.ResponseWriter, request *http.Request) {
    45  		if r.schemaCompatible = r.checkSchemaCompatibility(request.Context()); !r.schemaCompatible {
    46  			writer.WriteHeader(http.StatusInternalServerError)
    47  			return
    48  		}
    49  
    50  		if err := r.transactioner.PingContext(request.Context()); err != nil {
    51  			writer.WriteHeader(http.StatusInternalServerError)
    52  			return
    53  		}
    54  
    55  		writer.WriteHeader(http.StatusOK)
    56  	}
    57  }
    58  
    59  func (r *Ready) checkSchemaCompatibility(ctx context.Context) bool {
    60  	if r.schemaCompatible {
    61  		return true
    62  	}
    63  
    64  	tx, err := r.transactioner.Begin()
    65  	if err != nil {
    66  		log.C(ctx).Errorf(errors.Wrap(err, "while starting transaction").Error())
    67  		return false
    68  	}
    69  	defer r.transactioner.RollbackUnlessCommitted(ctx, tx)
    70  
    71  	ctx = persistence.SaveToContext(ctx, tx)
    72  
    73  	schemaVersion, dirty, err := r.repo.GetVersion(ctx)
    74  	if err != nil {
    75  		log.C(ctx).Error(err.Error())
    76  		return false
    77  	}
    78  
    79  	if err := tx.Commit(); err != nil {
    80  		log.C(ctx).Error(errors.Wrap(err, "while committing transaction").Error())
    81  		return false
    82  	}
    83  
    84  	if r.cfg.SchemaMigrationVersion != schemaVersion || dirty {
    85  		log.C(ctx).Errorf("Incompatible schema version. Expected: %s, Current (version, dirty): (%s, %v)", r.cfg.SchemaMigrationVersion, schemaVersion, dirty)
    86  		return false
    87  	}
    88  
    89  	return true
    90  }