1// Code generated by smithy-go-codegen DO NOT EDIT.
2
3package emr
4
5import (
6	"context"
7	"fmt"
8	awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware"
9	"github.com/aws/aws-sdk-go-v2/aws/signer/v4"
10	"github.com/aws/aws-sdk-go-v2/service/emr/types"
11	"github.com/aws/smithy-go/middleware"
12	smithytime "github.com/aws/smithy-go/time"
13	smithyhttp "github.com/aws/smithy-go/transport/http"
14	smithywaiter "github.com/aws/smithy-go/waiter"
15	"github.com/jmespath/go-jmespath"
16	"time"
17)
18
19// Provides cluster-level details including status, hardware and software
20// configuration, VPC settings, and so on.
21func (c *Client) DescribeCluster(ctx context.Context, params *DescribeClusterInput, optFns ...func(*Options)) (*DescribeClusterOutput, error) {
22	if params == nil {
23		params = &DescribeClusterInput{}
24	}
25
26	result, metadata, err := c.invokeOperation(ctx, "DescribeCluster", params, optFns, addOperationDescribeClusterMiddlewares)
27	if err != nil {
28		return nil, err
29	}
30
31	out := result.(*DescribeClusterOutput)
32	out.ResultMetadata = metadata
33	return out, nil
34}
35
36// This input determines which cluster to describe.
37type DescribeClusterInput struct {
38
39	// The identifier of the cluster to describe.
40	//
41	// This member is required.
42	ClusterId *string
43}
44
45// This output contains the description of the cluster.
46type DescribeClusterOutput struct {
47
48	// This output contains the details for the requested cluster.
49	Cluster *types.Cluster
50
51	// Metadata pertaining to the operation's result.
52	ResultMetadata middleware.Metadata
53}
54
55func addOperationDescribeClusterMiddlewares(stack *middleware.Stack, options Options) (err error) {
56	err = stack.Serialize.Add(&awsAwsjson11_serializeOpDescribeCluster{}, middleware.After)
57	if err != nil {
58		return err
59	}
60	err = stack.Deserialize.Add(&awsAwsjson11_deserializeOpDescribeCluster{}, middleware.After)
61	if err != nil {
62		return err
63	}
64	if err = addSetLoggerMiddleware(stack, options); err != nil {
65		return err
66	}
67	if err = awsmiddleware.AddClientRequestIDMiddleware(stack); err != nil {
68		return err
69	}
70	if err = smithyhttp.AddComputeContentLengthMiddleware(stack); err != nil {
71		return err
72	}
73	if err = addResolveEndpointMiddleware(stack, options); err != nil {
74		return err
75	}
76	if err = v4.AddComputePayloadSHA256Middleware(stack); err != nil {
77		return err
78	}
79	if err = addRetryMiddlewares(stack, options); err != nil {
80		return err
81	}
82	if err = addHTTPSignerV4Middleware(stack, options); err != nil {
83		return err
84	}
85	if err = awsmiddleware.AddRawResponseToMetadata(stack); err != nil {
86		return err
87	}
88	if err = awsmiddleware.AddRecordResponseTiming(stack); err != nil {
89		return err
90	}
91	if err = addClientUserAgent(stack); err != nil {
92		return err
93	}
94	if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil {
95		return err
96	}
97	if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil {
98		return err
99	}
100	if err = addOpDescribeClusterValidationMiddleware(stack); err != nil {
101		return err
102	}
103	if err = stack.Initialize.Add(newServiceMetadataMiddleware_opDescribeCluster(options.Region), middleware.Before); err != nil {
104		return err
105	}
106	if err = addRequestIDRetrieverMiddleware(stack); err != nil {
107		return err
108	}
109	if err = addResponseErrorMiddleware(stack); err != nil {
110		return err
111	}
112	if err = addRequestResponseLogging(stack, options); err != nil {
113		return err
114	}
115	return nil
116}
117
118// DescribeClusterAPIClient is a client that implements the DescribeCluster
119// operation.
120type DescribeClusterAPIClient interface {
121	DescribeCluster(context.Context, *DescribeClusterInput, ...func(*Options)) (*DescribeClusterOutput, error)
122}
123
124var _ DescribeClusterAPIClient = (*Client)(nil)
125
126// ClusterRunningWaiterOptions are waiter options for ClusterRunningWaiter
127type ClusterRunningWaiterOptions struct {
128
129	// Set of options to modify how an operation is invoked. These apply to all
130	// operations invoked for this client. Use functional options on operation call to
131	// modify this list for per operation behavior.
132	APIOptions []func(*middleware.Stack) error
133
134	// MinDelay is the minimum amount of time to delay between retries. If unset,
135	// ClusterRunningWaiter will use default minimum delay of 30 seconds. Note that
136	// MinDelay must resolve to a value lesser than or equal to the MaxDelay.
137	MinDelay time.Duration
138
139	// MaxDelay is the maximum amount of time to delay between retries. If unset or set
140	// to zero, ClusterRunningWaiter will use default max delay of 120 seconds. Note
141	// that MaxDelay must resolve to value greater than or equal to the MinDelay.
142	MaxDelay time.Duration
143
144	// LogWaitAttempts is used to enable logging for waiter retry attempts
145	LogWaitAttempts bool
146
147	// Retryable is function that can be used to override the service defined
148	// waiter-behavior based on operation output, or returned error. This function is
149	// used by the waiter to decide if a state is retryable or a terminal state. By
150	// default service-modeled logic will populate this option. This option can thus be
151	// used to define a custom waiter state with fall-back to service-modeled waiter
152	// state mutators.The function returns an error in case of a failure state. In case
153	// of retry state, this function returns a bool value of true and nil error, while
154	// in case of success it returns a bool value of false and nil error.
155	Retryable func(context.Context, *DescribeClusterInput, *DescribeClusterOutput, error) (bool, error)
156}
157
158// ClusterRunningWaiter defines the waiters for ClusterRunning
159type ClusterRunningWaiter struct {
160	client DescribeClusterAPIClient
161
162	options ClusterRunningWaiterOptions
163}
164
165// NewClusterRunningWaiter constructs a ClusterRunningWaiter.
166func NewClusterRunningWaiter(client DescribeClusterAPIClient, optFns ...func(*ClusterRunningWaiterOptions)) *ClusterRunningWaiter {
167	options := ClusterRunningWaiterOptions{}
168	options.MinDelay = 30 * time.Second
169	options.MaxDelay = 120 * time.Second
170	options.Retryable = clusterRunningStateRetryable
171
172	for _, fn := range optFns {
173		fn(&options)
174	}
175	return &ClusterRunningWaiter{
176		client:  client,
177		options: options,
178	}
179}
180
181// Wait calls the waiter function for ClusterRunning waiter. The maxWaitDur is the
182// maximum wait duration the waiter will wait. The maxWaitDur is required and must
183// be greater than zero.
184func (w *ClusterRunningWaiter) Wait(ctx context.Context, params *DescribeClusterInput, maxWaitDur time.Duration, optFns ...func(*ClusterRunningWaiterOptions)) error {
185	if maxWaitDur <= 0 {
186		return fmt.Errorf("maximum wait time for waiter must be greater than zero")
187	}
188
189	options := w.options
190	for _, fn := range optFns {
191		fn(&options)
192	}
193
194	if options.MaxDelay <= 0 {
195		options.MaxDelay = 120 * time.Second
196	}
197
198	if options.MinDelay > options.MaxDelay {
199		return fmt.Errorf("minimum waiter delay %v must be lesser than or equal to maximum waiter delay of %v.", options.MinDelay, options.MaxDelay)
200	}
201
202	ctx, cancelFn := context.WithTimeout(ctx, maxWaitDur)
203	defer cancelFn()
204
205	logger := smithywaiter.Logger{}
206	remainingTime := maxWaitDur
207
208	var attempt int64
209	for {
210
211		attempt++
212		apiOptions := options.APIOptions
213		start := time.Now()
214
215		if options.LogWaitAttempts {
216			logger.Attempt = attempt
217			apiOptions = append([]func(*middleware.Stack) error{}, options.APIOptions...)
218			apiOptions = append(apiOptions, logger.AddLogger)
219		}
220
221		out, err := w.client.DescribeCluster(ctx, params, func(o *Options) {
222			o.APIOptions = append(o.APIOptions, apiOptions...)
223		})
224
225		retryable, err := options.Retryable(ctx, params, out, err)
226		if err != nil {
227			return err
228		}
229		if !retryable {
230			return nil
231		}
232
233		remainingTime -= time.Since(start)
234		if remainingTime < options.MinDelay || remainingTime <= 0 {
235			break
236		}
237
238		// compute exponential backoff between waiter retries
239		delay, err := smithywaiter.ComputeDelay(
240			attempt, options.MinDelay, options.MaxDelay, remainingTime,
241		)
242		if err != nil {
243			return fmt.Errorf("error computing waiter delay, %w", err)
244		}
245
246		remainingTime -= delay
247		// sleep for the delay amount before invoking a request
248		if err := smithytime.SleepWithContext(ctx, delay); err != nil {
249			return fmt.Errorf("request cancelled while waiting, %w", err)
250		}
251	}
252	return fmt.Errorf("exceeded max wait time for ClusterRunning waiter")
253}
254
255func clusterRunningStateRetryable(ctx context.Context, input *DescribeClusterInput, output *DescribeClusterOutput, err error) (bool, error) {
256
257	if err == nil {
258		pathValue, err := jmespath.Search("Cluster.Status.State", output)
259		if err != nil {
260			return false, fmt.Errorf("error evaluating waiter state: %w", err)
261		}
262
263		expectedValue := "RUNNING"
264		value, ok := pathValue.(types.ClusterState)
265		if !ok {
266			return false, fmt.Errorf("waiter comparator expected types.ClusterState value, got %T", pathValue)
267		}
268
269		if string(value) == expectedValue {
270			return false, nil
271		}
272	}
273
274	if err == nil {
275		pathValue, err := jmespath.Search("Cluster.Status.State", output)
276		if err != nil {
277			return false, fmt.Errorf("error evaluating waiter state: %w", err)
278		}
279
280		expectedValue := "WAITING"
281		value, ok := pathValue.(types.ClusterState)
282		if !ok {
283			return false, fmt.Errorf("waiter comparator expected types.ClusterState value, got %T", pathValue)
284		}
285
286		if string(value) == expectedValue {
287			return false, nil
288		}
289	}
290
291	if err == nil {
292		pathValue, err := jmespath.Search("Cluster.Status.State", output)
293		if err != nil {
294			return false, fmt.Errorf("error evaluating waiter state: %w", err)
295		}
296
297		expectedValue := "TERMINATING"
298		value, ok := pathValue.(types.ClusterState)
299		if !ok {
300			return false, fmt.Errorf("waiter comparator expected types.ClusterState value, got %T", pathValue)
301		}
302
303		if string(value) == expectedValue {
304			return false, fmt.Errorf("waiter state transitioned to Failure")
305		}
306	}
307
308	if err == nil {
309		pathValue, err := jmespath.Search("Cluster.Status.State", output)
310		if err != nil {
311			return false, fmt.Errorf("error evaluating waiter state: %w", err)
312		}
313
314		expectedValue := "TERMINATED"
315		value, ok := pathValue.(types.ClusterState)
316		if !ok {
317			return false, fmt.Errorf("waiter comparator expected types.ClusterState value, got %T", pathValue)
318		}
319
320		if string(value) == expectedValue {
321			return false, fmt.Errorf("waiter state transitioned to Failure")
322		}
323	}
324
325	if err == nil {
326		pathValue, err := jmespath.Search("Cluster.Status.State", output)
327		if err != nil {
328			return false, fmt.Errorf("error evaluating waiter state: %w", err)
329		}
330
331		expectedValue := "TERMINATED_WITH_ERRORS"
332		value, ok := pathValue.(types.ClusterState)
333		if !ok {
334			return false, fmt.Errorf("waiter comparator expected types.ClusterState value, got %T", pathValue)
335		}
336
337		if string(value) == expectedValue {
338			return false, fmt.Errorf("waiter state transitioned to Failure")
339		}
340	}
341
342	return true, nil
343}
344
345// ClusterTerminatedWaiterOptions are waiter options for ClusterTerminatedWaiter
346type ClusterTerminatedWaiterOptions struct {
347
348	// Set of options to modify how an operation is invoked. These apply to all
349	// operations invoked for this client. Use functional options on operation call to
350	// modify this list for per operation behavior.
351	APIOptions []func(*middleware.Stack) error
352
353	// MinDelay is the minimum amount of time to delay between retries. If unset,
354	// ClusterTerminatedWaiter will use default minimum delay of 30 seconds. Note that
355	// MinDelay must resolve to a value lesser than or equal to the MaxDelay.
356	MinDelay time.Duration
357
358	// MaxDelay is the maximum amount of time to delay between retries. If unset or set
359	// to zero, ClusterTerminatedWaiter will use default max delay of 120 seconds. Note
360	// that MaxDelay must resolve to value greater than or equal to the MinDelay.
361	MaxDelay time.Duration
362
363	// LogWaitAttempts is used to enable logging for waiter retry attempts
364	LogWaitAttempts bool
365
366	// Retryable is function that can be used to override the service defined
367	// waiter-behavior based on operation output, or returned error. This function is
368	// used by the waiter to decide if a state is retryable or a terminal state. By
369	// default service-modeled logic will populate this option. This option can thus be
370	// used to define a custom waiter state with fall-back to service-modeled waiter
371	// state mutators.The function returns an error in case of a failure state. In case
372	// of retry state, this function returns a bool value of true and nil error, while
373	// in case of success it returns a bool value of false and nil error.
374	Retryable func(context.Context, *DescribeClusterInput, *DescribeClusterOutput, error) (bool, error)
375}
376
377// ClusterTerminatedWaiter defines the waiters for ClusterTerminated
378type ClusterTerminatedWaiter struct {
379	client DescribeClusterAPIClient
380
381	options ClusterTerminatedWaiterOptions
382}
383
384// NewClusterTerminatedWaiter constructs a ClusterTerminatedWaiter.
385func NewClusterTerminatedWaiter(client DescribeClusterAPIClient, optFns ...func(*ClusterTerminatedWaiterOptions)) *ClusterTerminatedWaiter {
386	options := ClusterTerminatedWaiterOptions{}
387	options.MinDelay = 30 * time.Second
388	options.MaxDelay = 120 * time.Second
389	options.Retryable = clusterTerminatedStateRetryable
390
391	for _, fn := range optFns {
392		fn(&options)
393	}
394	return &ClusterTerminatedWaiter{
395		client:  client,
396		options: options,
397	}
398}
399
400// Wait calls the waiter function for ClusterTerminated waiter. The maxWaitDur is
401// the maximum wait duration the waiter will wait. The maxWaitDur is required and
402// must be greater than zero.
403func (w *ClusterTerminatedWaiter) Wait(ctx context.Context, params *DescribeClusterInput, maxWaitDur time.Duration, optFns ...func(*ClusterTerminatedWaiterOptions)) error {
404	if maxWaitDur <= 0 {
405		return fmt.Errorf("maximum wait time for waiter must be greater than zero")
406	}
407
408	options := w.options
409	for _, fn := range optFns {
410		fn(&options)
411	}
412
413	if options.MaxDelay <= 0 {
414		options.MaxDelay = 120 * time.Second
415	}
416
417	if options.MinDelay > options.MaxDelay {
418		return fmt.Errorf("minimum waiter delay %v must be lesser than or equal to maximum waiter delay of %v.", options.MinDelay, options.MaxDelay)
419	}
420
421	ctx, cancelFn := context.WithTimeout(ctx, maxWaitDur)
422	defer cancelFn()
423
424	logger := smithywaiter.Logger{}
425	remainingTime := maxWaitDur
426
427	var attempt int64
428	for {
429
430		attempt++
431		apiOptions := options.APIOptions
432		start := time.Now()
433
434		if options.LogWaitAttempts {
435			logger.Attempt = attempt
436			apiOptions = append([]func(*middleware.Stack) error{}, options.APIOptions...)
437			apiOptions = append(apiOptions, logger.AddLogger)
438		}
439
440		out, err := w.client.DescribeCluster(ctx, params, func(o *Options) {
441			o.APIOptions = append(o.APIOptions, apiOptions...)
442		})
443
444		retryable, err := options.Retryable(ctx, params, out, err)
445		if err != nil {
446			return err
447		}
448		if !retryable {
449			return nil
450		}
451
452		remainingTime -= time.Since(start)
453		if remainingTime < options.MinDelay || remainingTime <= 0 {
454			break
455		}
456
457		// compute exponential backoff between waiter retries
458		delay, err := smithywaiter.ComputeDelay(
459			attempt, options.MinDelay, options.MaxDelay, remainingTime,
460		)
461		if err != nil {
462			return fmt.Errorf("error computing waiter delay, %w", err)
463		}
464
465		remainingTime -= delay
466		// sleep for the delay amount before invoking a request
467		if err := smithytime.SleepWithContext(ctx, delay); err != nil {
468			return fmt.Errorf("request cancelled while waiting, %w", err)
469		}
470	}
471	return fmt.Errorf("exceeded max wait time for ClusterTerminated waiter")
472}
473
474func clusterTerminatedStateRetryable(ctx context.Context, input *DescribeClusterInput, output *DescribeClusterOutput, err error) (bool, error) {
475
476	if err == nil {
477		pathValue, err := jmespath.Search("Cluster.Status.State", output)
478		if err != nil {
479			return false, fmt.Errorf("error evaluating waiter state: %w", err)
480		}
481
482		expectedValue := "TERMINATED"
483		value, ok := pathValue.(types.ClusterState)
484		if !ok {
485			return false, fmt.Errorf("waiter comparator expected types.ClusterState value, got %T", pathValue)
486		}
487
488		if string(value) == expectedValue {
489			return false, nil
490		}
491	}
492
493	if err == nil {
494		pathValue, err := jmespath.Search("Cluster.Status.State", output)
495		if err != nil {
496			return false, fmt.Errorf("error evaluating waiter state: %w", err)
497		}
498
499		expectedValue := "TERMINATED_WITH_ERRORS"
500		value, ok := pathValue.(types.ClusterState)
501		if !ok {
502			return false, fmt.Errorf("waiter comparator expected types.ClusterState value, got %T", pathValue)
503		}
504
505		if string(value) == expectedValue {
506			return false, fmt.Errorf("waiter state transitioned to Failure")
507		}
508	}
509
510	return true, nil
511}
512
513func newServiceMetadataMiddleware_opDescribeCluster(region string) *awsmiddleware.RegisterServiceMetadata {
514	return &awsmiddleware.RegisterServiceMetadata{
515		Region:        region,
516		ServiceID:     ServiceID,
517		SigningName:   "elasticmapreduce",
518		OperationName: "DescribeCluster",
519	}
520}
521