github.com/sacloud/libsacloud/v2@v2.32.3/helper/builder/internet/builder.go (about)

     1  // Copyright 2016-2022 The Libsacloud Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package internet
    16  
    17  import (
    18  	"context"
    19  	"errors"
    20  	"fmt"
    21  
    22  	"github.com/sacloud/libsacloud/v2/helper/plans"
    23  	"github.com/sacloud/libsacloud/v2/sacloud"
    24  	"github.com/sacloud/libsacloud/v2/sacloud/types"
    25  )
    26  
    27  // DefaultNotFoundRetry スイッチ+ルータ作成後のReadで404が返ってこなくなるまでに許容する404エラーの回数
    28  var DefaultNotFoundRetry = 360 // デフォルトの5秒おきリトライの場合30分
    29  
    30  // Builder スイッチ+ルータの構築を行う
    31  type Builder struct {
    32  	Name           string
    33  	Description    string
    34  	Tags           types.Tags
    35  	IconID         types.ID
    36  	NetworkMaskLen int
    37  	BandWidthMbps  int
    38  	EnableIPv6     bool
    39  
    40  	NotFoundRetry int
    41  
    42  	NoWait bool
    43  
    44  	Client *APIClient
    45  }
    46  
    47  // Validate 設定値の検証
    48  func (b *Builder) Validate(ctx context.Context, zone string) error {
    49  	requiredValues := map[string]bool{
    50  		"NetworkMaskLen": b.NetworkMaskLen == 0,
    51  		"BandWidthMbps":  b.BandWidthMbps == 0,
    52  	}
    53  	for key, empty := range requiredValues {
    54  		if empty {
    55  			return fmt.Errorf("%s is required", key)
    56  		}
    57  	}
    58  
    59  	if b.NoWait && b.EnableIPv6 {
    60  		return errors.New("NoWait=true is not supported when EnableIPv6=true")
    61  	}
    62  	return nil
    63  }
    64  
    65  // Build ルータ+スイッチの作成や設定をまとめて行う
    66  func (b *Builder) Build(ctx context.Context, zone string) (*sacloud.Internet, error) {
    67  	if b.NotFoundRetry == 0 {
    68  		b.NotFoundRetry = DefaultNotFoundRetry
    69  	}
    70  
    71  	if err := b.Validate(ctx, zone); err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	internet, err := b.Client.Internet.Create(ctx, zone, &sacloud.InternetCreateRequest{
    76  		Name:           b.Name,
    77  		Description:    b.Description,
    78  		Tags:           b.Tags,
    79  		IconID:         b.IconID,
    80  		NetworkMaskLen: b.NetworkMaskLen,
    81  		BandWidthMbps:  b.BandWidthMbps,
    82  	})
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  
    87  	if b.NoWait {
    88  		return internet, nil
    89  	}
    90  
    91  	// [HACK] ルータ作成直後は GET /internet/:id が404を返すことへの対応
    92  	waiter := sacloud.WaiterForApplianceUp(func() (interface{}, error) {
    93  		return b.Client.Internet.Read(ctx, zone, internet.ID)
    94  	}, b.NotFoundRetry)
    95  	if _, err := waiter.WaitForState(ctx); err != nil {
    96  		return internet, err
    97  	}
    98  
    99  	if b.EnableIPv6 {
   100  		_, err = b.Client.Internet.EnableIPv6(ctx, zone, internet.ID)
   101  		if err != nil {
   102  			return internet, err
   103  		}
   104  	}
   105  
   106  	return b.Client.Internet.Read(ctx, zone, internet.ID)
   107  }
   108  
   109  // Update スイッチ+ルータの更新
   110  func (b *Builder) Update(ctx context.Context, zone string, id types.ID) (*sacloud.Internet, error) {
   111  	if err := b.Validate(ctx, zone); err != nil {
   112  		return nil, err
   113  	}
   114  
   115  	// check Internet is exists
   116  	internet, err := b.Client.Internet.Read(ctx, zone, id)
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  
   121  	if b.NetworkMaskLen != internet.NetworkMaskLen {
   122  		return nil, fmt.Errorf("unsupported operation: NetworkMaskLen is changed: current: %d new: %d", internet.NetworkMaskLen, b.NetworkMaskLen)
   123  	}
   124  
   125  	if internet.BandWidthMbps != b.BandWidthMbps {
   126  		b.Tags = plans.AppendPreviousIDTagIfAbsent(b.Tags, internet.ID)
   127  		// 成功するとIDが変更となる
   128  		internet, err = b.Client.Internet.UpdateBandWidth(ctx, zone, internet.ID, &sacloud.InternetUpdateBandWidthRequest{
   129  			BandWidthMbps: b.BandWidthMbps,
   130  		})
   131  		if err != nil {
   132  			return nil, err
   133  		}
   134  	}
   135  
   136  	internet, err = b.Client.Internet.Update(ctx, zone, internet.ID, &sacloud.InternetUpdateRequest{
   137  		Name:        b.Name,
   138  		Description: b.Description,
   139  		Tags:        b.Tags,
   140  		IconID:      b.IconID,
   141  	})
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  
   146  	currentIPv6Enabled := len(internet.Switch.IPv6Nets) > 0
   147  	if b.EnableIPv6 != currentIPv6Enabled {
   148  		if currentIPv6Enabled {
   149  			if err := b.Client.Internet.DisableIPv6(ctx, zone, internet.ID, internet.Switch.IPv6Nets[0].ID); err != nil {
   150  				return nil, err
   151  			}
   152  		} else {
   153  			if _, err := b.Client.Internet.EnableIPv6(ctx, zone, internet.ID); err != nil {
   154  				return nil, err
   155  			}
   156  		}
   157  	}
   158  
   159  	return b.Client.Internet.Read(ctx, zone, internet.ID)
   160  }