github.com/smugmug/godynamo@v0.0.0-20151122084750-7913028f6623/endpoints/describe_table/describe_table.go (about)

     1  // Support for the DynamoDB DescribeTable endpoint.
     2  //
     3  // example use:
     4  //
     5  // tests/create_table-livestest.go, which contains a DescribeTable invocation
     6  //
     7  package describe_table
     8  
     9  import (
    10  	"encoding/json"
    11  	"errors"
    12  	"fmt"
    13  	"github.com/smugmug/godynamo/authreq"
    14  	"github.com/smugmug/godynamo/aws_const"
    15  	"github.com/smugmug/godynamo/conf"
    16  	ep "github.com/smugmug/godynamo/endpoint"
    17  	"github.com/smugmug/godynamo/types/attributedefinition"
    18  	"github.com/smugmug/godynamo/types/globalsecondaryindex"
    19  	"github.com/smugmug/godynamo/types/keydefinition"
    20  	"github.com/smugmug/godynamo/types/localsecondaryindex"
    21  	"github.com/smugmug/godynamo/types/provisionedthroughput"
    22  	"net/http"
    23  	"time"
    24  )
    25  
    26  const (
    27  	ENDPOINT_NAME      = "DescribeTable"
    28  	DESCTABLE_ENDPOINT = aws_const.ENDPOINT_PREFIX + ENDPOINT_NAME
    29  	ACTIVE             = "ACTIVE"
    30  )
    31  
    32  type DescribeTable struct {
    33  	TableName string
    34  }
    35  
    36  // Describe is an alias for backwards compatibility
    37  type Describe DescribeTable
    38  
    39  type Request DescribeTable
    40  
    41  func NewDescribeTable() *DescribeTable {
    42  	d := new(DescribeTable)
    43  	return d
    44  }
    45  
    46  type Response struct {
    47  	Table struct {
    48  		AttributeDefinitions   attributedefinition.AttributeDefinitions
    49  		CreationDateTime       float64
    50  		GlobalSecondaryIndexes []globalsecondaryindex.GlobalSecondaryIndexDesc
    51  		ItemCount              uint64
    52  		KeySchema              keydefinition.KeySchema
    53  		LocalSecondaryIndexes  []localsecondaryindex.LocalSecondaryIndexDesc
    54  		ProvisionedThroughput  provisionedthroughput.ProvisionedThroughputDesc
    55  		TableName              string
    56  		TableSizeBytes         uint64
    57  		TableStatus            string
    58  	}
    59  }
    60  
    61  func NewResponse() *Response {
    62  	r := new(Response)
    63  	r.Table.AttributeDefinitions = make(attributedefinition.AttributeDefinitions, 0)
    64  	r.Table.GlobalSecondaryIndexes = make([]globalsecondaryindex.GlobalSecondaryIndexDesc, 0)
    65  	r.Table.KeySchema = make(keydefinition.KeySchema, 0)
    66  	r.Table.LocalSecondaryIndexes = make([]localsecondaryindex.LocalSecondaryIndexDesc, 0)
    67  	return r
    68  }
    69  
    70  type StatusResult struct {
    71  	StatusResult bool
    72  }
    73  
    74  // These implementations of EndpointReq use a parameterized conf.
    75  
    76  func (describe_table *DescribeTable) EndpointReqWithConf(c *conf.AWS_Conf) ([]byte, int, error) {
    77  	if describe_table == nil {
    78  		return nil, 0, errors.New("describe_table.(DescribeTable)EndpointReqWithConf: receiver is nil")
    79  	}
    80  	if !conf.IsValid(c) {
    81  		return nil, 0, errors.New("describe_table.EndpointReqWithConf: c is not valid")
    82  	}
    83  	// returns resp_body,code,err
    84  	reqJSON, json_err := json.Marshal(describe_table)
    85  	if json_err != nil {
    86  		return nil, 0, json_err
    87  	}
    88  	return authreq.RetryReqJSON_V4WithConf(reqJSON, DESCTABLE_ENDPOINT, c)
    89  }
    90  
    91  func (describe *Describe) EndpointReqWithConf(c *conf.AWS_Conf) ([]byte, int, error) {
    92  	if describe == nil {
    93  		return nil, 0, errors.New("describe_table.(Describe)EndpointReqWithConf: receiver is nil")
    94  	}
    95  	describe_table := DescribeTable(*describe)
    96  	return describe_table.EndpointReqWithConf(c)
    97  }
    98  
    99  func (req *Request) EndpointReqWithConf(c *conf.AWS_Conf) ([]byte, int, error) {
   100  	if req == nil {
   101  		return nil, 0, errors.New("describe_table.(Request)EndpointReqWithConf: receiver is nil")
   102  	}
   103  	describe_table := DescribeTable(*req)
   104  	return describe_table.EndpointReqWithConf(c)
   105  }
   106  
   107  // These implementations of EndpointReq use the global conf.
   108  
   109  func (describe_table *DescribeTable) EndpointReq() ([]byte, int, error) {
   110  	if describe_table == nil {
   111  		return nil, 0, errors.New("describe_table.(DescribeTable)EndpointReq: receiver is nil")
   112  	}
   113  	return describe_table.EndpointReqWithConf(&conf.Vals)
   114  }
   115  
   116  func (describe *Describe) EndpointReq() ([]byte, int, error) {
   117  	if describe == nil {
   118  		return nil, 0, errors.New("describe_table.(Describe)EndpointReq: receiver is nil")
   119  	}
   120  	describe_table := DescribeTable(*describe)
   121  	return describe_table.EndpointReqWithConf(&conf.Vals)
   122  }
   123  
   124  func (req *Request) EndpointReq() ([]byte, int, error) {
   125  	if req == nil {
   126  		return nil, 0, errors.New("describe_table.(Request)EndpointReq: receiver is nil")
   127  	}
   128  	describe_table := DescribeTable(*req)
   129  	return describe_table.EndpointReqWithConf(&conf.Vals)
   130  }
   131  
   132  // PollTableStatusWithConf allows the caller to poll a table for a specific status.
   133  func PollTableStatusWithConf(tablename string, status string, tries int, c *conf.AWS_Conf) (bool, error) {
   134  	if !conf.IsValid(c) {
   135  		return false, errors.New("describe_table.PollTableStatusWithConf: c is not valid")
   136  	}
   137  	// aws docs informs us to poll the describe endpoint until the table
   138  	// "status" is status for this tablename
   139  	wait := time.Duration(2 * time.Second)
   140  
   141  	for i := 0; i < tries; i++ {
   142  		active, err := IsTableStatusWithConf(tablename, status, c)
   143  		if err != nil {
   144  			e := fmt.Sprintf("describe_table.PollStatus:%s",
   145  				err.Error())
   146  			return false, errors.New(e)
   147  		}
   148  		if active {
   149  			return active, nil
   150  		}
   151  		time.Sleep(wait) // wait for table to become ACTIVE
   152  	}
   153  	return false, nil
   154  }
   155  
   156  // PollTableStatus is the same as PollTableStatusWithConf but uses the global conf.Vals.
   157  func PollTableStatus(tablename string, status string, tries int) (bool, error) {
   158  	return PollTableStatusWithConf(tablename, status, tries, &conf.Vals)
   159  }
   160  
   161  // IsTableStatusWithConf will test the equality status of a table.
   162  func IsTableStatusWithConf(tablename string, status string, c *conf.AWS_Conf) (bool, error) {
   163  	if !conf.IsValid(c) {
   164  		return false, errors.New("describe_table.IsTableStatusWithConf: c is not valid")
   165  	}
   166  	d := ep.Endpoint(&DescribeTable{TableName: tablename})
   167  	s_resp, s_code, s_err := authreq.RetryReq_V4WithConf(d, DESCTABLE_ENDPOINT, c)
   168  	if s_err != nil {
   169  		e := fmt.Sprintf("describe_table.IsTableStatus: "+
   170  			"check on %s err %s",
   171  			tablename, s_err.Error())
   172  		// if not a 500 problem, don't retry
   173  		if !ep.ServerErr(s_code) {
   174  			return false, errors.New(e)
   175  		}
   176  	}
   177  	if s_resp != nil && s_code == http.StatusOK {
   178  		var resp_json Response
   179  		um_err := json.Unmarshal([]byte(s_resp), &resp_json)
   180  		if um_err != nil {
   181  			um_msg := fmt.Sprintf("describe_table.IsTableStatus:"+
   182  				"cannot unmarshal %s, err: %s\ncheck "+
   183  				"table creation of %s manually",
   184  				s_resp, um_err.Error(), tablename)
   185  			return false, errors.New(um_msg)
   186  		}
   187  		return (resp_json.Table.TableStatus == status), nil
   188  	}
   189  	e := fmt.Sprintf("describe_table.IsTableStatus:does %s exist?", tablename)
   190  	return false, errors.New(e)
   191  }
   192  
   193  // IsTableStatus is the same as IsTableStatusWithConf but uses the global conf.Vals.
   194  func IsTableStatus(tablename string, status string) (bool, error) {
   195  	return IsTableStatusWithConf(tablename, status, &conf.Vals)
   196  }
   197  
   198  // TableExistsWithconf test for table exists: exploit the fact that aws reports 4xx for tables that don't exist.
   199  func (desc DescribeTable) TableExistsWithConf(c *conf.AWS_Conf) (bool, error) {
   200  	if !conf.IsValid(c) {
   201  		return false, errors.New("describe_table.TableExistsWithConf: c is not valid")
   202  	}
   203  	_, code, err := desc.EndpointReqWithConf(c)
   204  	if err != nil {
   205  		e := fmt.Sprintf("describe_table.TableExistsWithConf "+
   206  			"%s", err.Error())
   207  		return false, errors.New(e)
   208  	}
   209  	return (code == http.StatusOK), nil
   210  }
   211  
   212  // TableExists is the same as TableExistsWithConf but uses the global conf.Vals.
   213  func (desc DescribeTable) TableExists() (bool, error) {
   214  	return desc.TableExistsWithConf(&conf.Vals)
   215  }