class SyncYaml2DictToolSLZ(serializers.Serializer): dict = serializers.JSONField( initial={}, style={ "base_template": "textarea.html", "rows": 10 } ) yaml = YamlField( initial=[], label="Yaml", help_text="Yaml format data", style={ "base_template": "textarea.html", "rows": 10 } ) def create(self, validated_data): dict_list = utils.sync_yaml2dict(validated_data["dict"], validated_data["yaml"]) return { "yaml": validated_data["yaml"], "dict": dict_list } class Meta: fields = ( "yaml", "dict", )
class AppDetailSLZ(AppBaseSLZ): namespace_info = NamespaceInfoField(write_only=True, label="Namespace") chart_info = serializers.JSONField(read_only=True, label="Chart Info") chart_version = serializers.PrimaryKeyRelatedField( queryset=ChartVersion.objects.all(), write_only=True) release = ChartReleaseSLZ(read_only=True) answers = HelmValueField(initial=[], read_only=True, label="Answers", help_text="JSON format data", source="get_answers", style={ "base_template": "textarea.html", "rows": 10 }) customs = HelmValueField(initial=[], read_only=True, label="Customs", help_text="JSON format data", source="get_customs", style={ "base_template": "textarea.html", "rows": 10 }) valuefile = YamlField(initial="", read_only=True, default="", label="ValueFile", help_text="Yaml format data", source="get_valuefile", style={ "base_template": "textarea.html", "rows": 10 }) valuefile_name = serializers.CharField(source="get_valuefile_name", read_only=True) cmd_flags = serializers.JSONField(source="get_cmd_flags", read_only=True) class Meta: model = App fields = ("id", "name", "namespace", "namespace_id", "cluster_id", "namespace_info", "chart_version", "chart_info", "answers", "customs", "valuefile", "project_id", "chart", "transitioning_result", "transitioning_message", "transitioning_on", "transitioning_action", "release", "creator", "updator", "created", "updated", "valuefile_name", "cmd_flags")
class SyncDict2YamlToolSLZ(serializers.Serializer): dict = serializers.JSONField(initial={}, style={"base_template": "textarea.html", "rows": 10}) yaml = YamlField( initial=[], label="Yaml", help_text="Yaml format data", style={"base_template": "textarea.html", "rows": 10} ) def create(self, validated_data): """转换数据 NOTE: 兼容老版本处理,并且不允许重复KEY;当处理yaml出现异常时,抛出异常 """ try: content = utils.sync_dict2yaml(validated_data["dict"], validated_data["yaml"]) except YAMLFutureWarning as e: raise serializers.ValidationError(e) return {"yaml": content, "dict": validated_data["dict"]} class Meta: fields = ( "yaml", "dict", )
class AppCreatePreviewSLZ(AppMixin, serializers.Serializer): """ 创建预览 """ name = serializers.CharField(write_only=True) namespace_info = NamespaceInfoField(write_only=True, label="Namespace") chart_version = serializers.PrimaryKeyRelatedField(queryset=ChartVersion.objects.all(), write_only=True) answers = HelmValueField( initial=[], write_only=True, label="Answers", help_text="JSON format data", source="get_answers", style={"base_template": "textarea.html", "rows": 10}, ) customs = HelmValueField( initial=[], write_only=True, label="Customs", help_text="JSON format data", source="get_customs", style={"base_template": "textarea.html", "rows": 10}, ) valuefile = YamlField( initial="", write_only=True, allow_blank=True, default="", label="ValueFile", help_text="Yaml format data", style={"base_template": "textarea.html", "rows": 10}, ) content = serializers.JSONField(read_only=True) notes = serializers.JSONField(read_only=True) cmd_flags = serializers.JSONField(required=False, default=[]) def create(self, validated_data): """ 生成应用的预览数据,这个时候应用没有创建,release也没有创建 """ namespace_info = self.get_ns_info_by_id(validated_data["namespace_info"]) cluster_id = namespace_info["cluster_id"] check_cluster_perm( user=self.context["request"].user, project_id=namespace_info["project_id"], cluster_id=cluster_id, request=self.context["request"], ) # prepare parameters parameters = merge_rancher_answers(validated_data["get_answers"], validated_data["get_customs"]) valuefile = get_valuefile_with_bcs_variable_injected( access_token=self.context["request"].user.token.access_token, project_id=namespace_info["project_id"], namespace_id=namespace_info["id"], valuefile=validated_data["valuefile"], cluster_id=cluster_id, ) # inject bcs info now = datetime.datetime.now() username = self.context["request"].user.username # 组装注入的参数 bcs_inject_data = bcs_helm_utils.BCSInjectData( source_type="helm", creator=username, updator=username, version=validated_data["chart_version"].version, project_id=self.project_id, app_id=self.context["request"].project.cc_app_id, cluster_id=cluster_id, namespace=namespace_info["name"], stdlog_data_id=bcs_helm_utils.get_stdlog_data_id(self.project_id), image_pull_secret=bcs_helm_utils.provide_image_pull_secrets(namespace_info["name"]), ) client = KubeHelmClient(helm_bin=settings.HELM3_BIN) try: extra_params = {"cmd_flags": validated_data["cmd_flags"]} content, notes = client.template_with_ytt_renderer( files=validated_data["chart_version"].files, namespace=namespace_info["name"], name=validated_data.get("name"), parameters=parameters, valuefile=valuefile, cluster_id=cluster_id, bcs_inject_data=bcs_inject_data, **extra_params ) except helm_exceptions.HelmBaseException: # raise ParseError(str(e)) # NOTE: 现阶段为防止出现未测试到的情况,允许出错时,按照先前流程渲染;后续删除 content, notes = _template_with_bcs_renderer( client, validated_data["chart_version"].files, validated_data.get("name"), namespace_info["name"], namespace_info["id"], parameters, valuefile, cluster_id, username, now, validated_data["chart_version"].version, self.access_token, self.project_id, ) return {"content": preview_parse(content, namespace_info["name"]), "notes": notes} class Meta: fields = ( "name", "namespace_info", "chart_version", "answers", "customs", "valuefile", "content", "notes", "cmd_flags", ) read_only_fields = ( "content", "notes", )
class AppReleasePreviewSLZ(AppMixin, serializers.Serializer): """ 发布预览 """ upgrade_verion = UpgradeVersionField(write_only=True, required=True) answers = HelmValueField( initial=[], write_only=True, label="Answers", help_text="JSON format data", source="get_answers", style={"base_template": "textarea.html", "rows": 10}, ) customs = HelmValueField( initial=[], write_only=True, label="Customs", help_text="JSON format data", source="get_customs", style={"base_template": "textarea.html", "rows": 10}, ) valuefile = YamlField( initial="", write_only=True, allow_blank=True, required=False, default="", label="ValueFile", help_text="Yaml format data", style={"base_template": "textarea.html", "rows": 10}, ) content = serializers.JSONField(read_only=True) notes = serializers.JSONField(read_only=True) difference = serializers.JSONField(read_only=True) chart_version_changed = serializers.BooleanField(read_only=True) old_content = serializers.JSONField(read_only=True) # 方便前端渲染 new_content = serializers.JSONField(read_only=True) def create(self, validated_data): """ 应用更新时的预览数据,这个时候目标release还没有创建 """ instance = App.objects.get(id=self.app_id) check_cluster_perm( user=self.context["request"].user, project_id=instance.project_id, cluster_id=instance.cluster_id, request=self.context["request"], ) # 标记Chart中的values.yaml是否发生变化,用于提醒用户 chart_version_changed = False # prepare parameters parameters = merge_rancher_answers(validated_data["get_answers"], validated_data["get_customs"]) chart_version_id = validated_data["upgrade_verion"] chart_version_id = int(chart_version_id) if chart_version_id == KEEP_TEMPLATE_UNCHANGED: files = instance.release.chartVersionSnapshot.files else: chart_version_changed = True chart_version = ChartVersion.objects.get(id=chart_version_id) files = chart_version.files valuefile = get_valuefile_with_bcs_variable_injected( access_token=self.context["request"].user.token.access_token, project_id=instance.project_id, namespace_id=instance.namespace_id, valuefile=validated_data["valuefile"], cluster_id=instance.cluster_id, ) now = datetime.datetime.now() username = self.context["request"].user.username # 组装注入的参数 bcs_inject_data = bcs_helm_utils.BCSInjectData( source_type="helm", creator=username, updator=username, version=instance.release.chartVersionSnapshot.version, project_id=self.project_id, app_id=self.context["request"].project.cc_app_id, cluster_id=instance.cluster_id, namespace=instance.namespace, stdlog_data_id=bcs_helm_utils.get_stdlog_data_id(self.project_id), image_pull_secret=bcs_helm_utils.provide_image_pull_secrets(instance.namespace), ) # 默认为使用helm3 client client = KubeHelmClient(helm_bin=settings.HELM3_BIN) try: content, notes = client.template_with_ytt_renderer( files=files, namespace=instance.namespace, name=instance.name, parameters=parameters, valuefile=valuefile, cluster_id=instance.cluster_id, bcs_inject_data=bcs_inject_data, ) except helm_exceptions.HelmBaseException: # raise ParseError(str(e)) # NOTE: 现阶段为防止出现未测试到的情况,允许出错时,按照先前流程渲染;后续删除 content, notes = _template_with_bcs_renderer( client, files, instance.name, instance.namespace, instance.namespace_id, parameters, valuefile, instance.cluster_id, username, now, instance.release.chartVersionSnapshot.version, self.access_token, instance.project_id, ) # compute diff old_content = instance.release.content if not old_content: old_content, _ = instance.render_app( username=self.context["request"].user.username, access_token=self.access_token ) difference = simple_diff(old_content, content, instance.namespace) return { "content": preview_parse(content, instance.namespace), "notes": notes, "difference": difference, "chart_version_changed": chart_version_changed, "old_content": old_content, "new_content": content, } class Meta: fields = ( "name", "namespace_info", "chart_version", "answers", "customs", "valuefile", "content", "notes", "difference", "chart_version_changed", ) read_only_fields = ( "content", "notes", "chart_version_changed", )
class AppUpgradeSLZ(AppBaseSLZ): upgrade_verion = UpgradeVersionField(write_only=True, required=True) answers = HelmValueField( label="Answers", help_text="JSON format data", source="get_answers", style={"base_template": "textarea.html", "rows": 10}, ) customs = HelmValueField( label="Customs", help_text="JSON format data", source="get_customs", style={"base_template": "textarea.html", "rows": 10}, ) valuefile = YamlField( initial="", default="", label="ValueFile", allow_blank=True, help_text="Yaml format data", source="get_valuefile", style={"base_template": "textarea.html", "rows": 10}, ) valuefile_name = serializers.CharField( source="get_valuefile_name", write_only=True, default=DEFAULT_VALUES_FILE_NAME ) cmd_flags = serializers.JSONField(required=False, default=[]) def update(self, instance, validated_data): # update sys variable sys_variables = collect_system_variable( access_token=self.context["request"].user.token.access_token, project_id=instance.project_id, namespace_id=instance.namespace_id, ) return instance.upgrade_app( access_token=self.access_token, chart_version_id=validated_data["upgrade_verion"], answers=validated_data["get_answers"], customs=validated_data["get_customs"], valuefile=validated_data.get("get_valuefile"), updator=self.request_username, sys_variables=sys_variables, valuefile_name=validated_data.get("get_valuefile_name"), cmd_flags=validated_data["cmd_flags"], ) class Meta: model = App fields = ( "name", "namespace", "upgrade_verion", "answers", "customs", "valuefile", "project_id", "cluster_id", "chart", "transitioning_result", "transitioning_message", "transitioning_on", "transitioning_action", "id", "valuefile_name", "cmd_flags", ) read_only_fields = ( "name", "namespace", "project_id", "cluster_id", "chart", "transitioning_result", "transitioning_message", "transitioning_on", "transitioning_action", ) extra_kwargs = { "answers": {"write_only": True}, "customs": {"write_only": True}, "valuefile": {"write_only": True}, "upgrade_verion": {"write_only": True}, "valuefile_name": {"write_only": True}, }
class AppSLZ(AppBaseSLZ): name = serializers.RegexField( RESOURCE_NAME_REGEX, error_messages={"invalid": _('不符合k8s资源名规范, 只能由小写字母数字或者-组成,正则:{}').format(RESOURCE_NAME_REGEX)}, ) namespace_info = NamespaceInfoField(write_only=True, label="Namespace") chart_version = serializers.PrimaryKeyRelatedField(queryset=ChartVersion.objects.all(), write_only=True) answers = HelmValueField( initial=[], write_only=True, label="Answers", help_text="JSON format data", source="get_answers", style={"base_template": "textarea.html", "rows": 10}, ) customs = HelmValueField( initial=[], write_only=True, label="Customs", help_text="JSON format data", source="get_customs", style={"base_template": "textarea.html", "rows": 10}, ) valuefile = YamlField( initial="", write_only=True, allow_blank=True, default="", label="ValueFile", help_text="Yaml format data", source="get_valuefile", style={"base_template": "textarea.html", "rows": 10}, ) valuefile_name = serializers.CharField( source="get_valuefile_name", write_only=True, default=DEFAULT_VALUES_FILE_NAME ) current_version = serializers.CharField(source="get_current_version", read_only=True) cmd_flags = serializers.JSONField(required=False, default=[]) def create(self, validated_data): namespace_info = self.get_ns_info_by_id(validated_data["namespace_info"]) check_cluster_perm( user=self.context["request"].user, project_id=namespace_info["project_id"], cluster_id=namespace_info["cluster_id"], request=self.context["request"], ) # 检查集群已经成功注册到 bcs, 否则让用户先完成注册逻辑 bcs_client = get_bcs_client( project_id=namespace_info["project_id"], cluster_id=namespace_info["cluster_id"], access_token=self.context["request"].user.token.access_token, ) bcs_client.get_cluster_credential() sys_variables = collect_system_variable( access_token=self.context["request"].user.token.access_token, project_id=namespace_info["project_id"], namespace_id=namespace_info["id"], ) return App.objects.initialize_app( access_token=self.access_token, name=validated_data.get("name"), project_id=self.project_id, cluster_id=namespace_info["cluster_id"], namespace_id=namespace_info["id"], namespace=namespace_info["name"], chart_version=validated_data["chart_version"], answers=validated_data["get_answers"], customs=validated_data["get_customs"], valuefile=validated_data.get("get_valuefile"), creator=self.request_username, updator=self.request_username, sys_variables=sys_variables, valuefile_name=validated_data.get('get_valuefile_name'), cmd_flags=validated_data["cmd_flags"], ) def validate_name(self, value): """make sure app name is starts with alphabet character, if the name used as resource name, numeric will forbidded by api server """ if value and (not value.islower() or not value[:1].islower()): raise serializers.ValidationError("name must starts with lower alphabet character") return value class Meta: model = App fields = ( "name", "namespace", "namespace_id", "cluster_id", "namespace_info", "chart_version", "answers", "customs", "valuefile", "project_id", "chart", "transitioning_result", "transitioning_message", "transitioning_on", "transitioning_action", "created", "updated", "creator", "updator", "current_version", "id", "valuefile_name", "cmd_flags", ) read_only_fields = ( "namespace", "namespace_id", "cluster_id", "project_id", "chart", "transitioning_result", "transitioning_message", "transitioning_on", "transitioning_action", "created", "updated", "creator", "updator", "current_version", )
class AppCreatePreviewSLZ(AppMixin, serializers.Serializer): """ 创建预览 """ name = serializers.CharField(write_only=True) namespace_info = NamespaceInfoField(write_only=True, label="Namespace") chart_version = serializers.PrimaryKeyRelatedField( queryset=ChartVersion.objects.all(), write_only=True) answers = HelmValueField(initial=[], write_only=True, label="Answers", help_text="JSON format data", source="get_answers", style={ "base_template": "textarea.html", "rows": 10 }) customs = HelmValueField(initial=[], write_only=True, label="Customs", help_text="JSON format data", source="get_customs", style={ "base_template": "textarea.html", "rows": 10 }) valuefile = YamlField(initial="", write_only=True, allow_blank=True, default="", label="ValueFile", help_text="Yaml format data", style={ "base_template": "textarea.html", "rows": 10 }) content = serializers.JSONField(read_only=True) notes = serializers.JSONField(read_only=True) def create(self, validated_data): """ 生成应用的预览数据,这个时候应用没有创建,release也没有创建 """ namespace_info = self.get_ns_info_by_id( validated_data["namespace_info"]) check_cluster_perm(user=self.context["request"].user, project_id=namespace_info["project_id"], cluster_id=namespace_info["cluster_id"], request=self.context["request"]) # prepare parameters parameters = merge_rancher_answers(validated_data["get_answers"], validated_data["get_customs"]) valuefile = get_valuefile_with_bcs_variable_injected( access_token=self.context["request"].user.token.access_token, project_id=namespace_info["project_id"], namespace_id=namespace_info["id"], valuefile=validated_data["valuefile"]) client = KubeHelmClient(helm_bin=settings.HELM_BIN) try: content, notes = client.template( files=validated_data["chart_version"].files, namespace=namespace_info["name"], name=validated_data.get("name"), parameters=parameters, valuefile=valuefile, ) except helm_exceptions.HelmBaseException as e: raise ParseError(str(e)) # inject bcs info now = datetime.datetime.now() username = self.context["request"].user.username content = bcs_info_injector.inject_bcs_info( access_token=self.access_token, project_id=self.project_id, cluster_id=namespace_info["cluster_id"], namespace_id=namespace_info["id"], namespace=namespace_info["name"], creator=username, updator=username, created_at=now, updated_at=now, resources=content, version=validated_data["chart_version"].version) return { "content": preview_parse(content, namespace_info["name"]), "notes": notes } class Meta: fields = ( "name", "namespace_info", "chart_version", "answers", "customs", "valuefile", "content", "notes", ) read_only_fields = ( "content", "notes", )
class AppReleasePreviewSLZ(AppMixin, serializers.Serializer): """ 发布预览 """ upgrade_verion = UpgradeVersionField(write_only=True, required=True) answers = HelmValueField(initial=[], write_only=True, label="Answers", help_text="JSON format data", source="get_answers", style={ "base_template": "textarea.html", "rows": 10 }) customs = HelmValueField(initial=[], write_only=True, label="Customs", help_text="JSON format data", source="get_customs", style={ "base_template": "textarea.html", "rows": 10 }) valuefile = YamlField(initial="", write_only=True, allow_blank=True, required=False, default="", label="ValueFile", help_text="Yaml format data", style={ "base_template": "textarea.html", "rows": 10 }) content = serializers.JSONField(read_only=True) notes = serializers.JSONField(read_only=True) difference = serializers.JSONField(read_only=True) chart_version_changed = serializers.BooleanField(read_only=True) old_content = serializers.JSONField(read_only=True) def create(self, validated_data): """ 应用更新时的预览数据,这个时候目标release还没有创建 """ instance = App.objects.get(id=self.app_id) check_cluster_perm(user=self.context["request"].user, project_id=instance.project_id, cluster_id=instance.cluster_id, request=self.context["request"]) # 标记Chart中的values.yaml是否发生变化,用于提醒用户 chart_version_changed = False # prepare parameters parameters = merge_rancher_answers(validated_data["get_answers"], validated_data["get_customs"]) chart_version_id = validated_data["upgrade_verion"] chart_version_id = int(chart_version_id) if chart_version_id == KEEP_TEMPLATE_UNCHANGED: files = instance.release.chartVersionSnapshot.files else: chart_version_changed = True chart_version = ChartVersion.objects.get(id=chart_version_id) files = chart_version.files valuefile = get_valuefile_with_bcs_variable_injected( access_token=self.context["request"].user.token.access_token, project_id=instance.project_id, namespace_id=instance.namespace_id, valuefile=validated_data["valuefile"]) client = KubeHelmClient(helm_bin=settings.HELM_BIN) try: content, notes = client.template( files=files, namespace=instance.namespace, name=instance.name, parameters=parameters, valuefile=valuefile, ) except helm_exceptions.HelmBaseException as e: raise ParseError(str(e)) # inject bcs info now = datetime.datetime.now() content = bcs_info_injector.inject_bcs_info( access_token=self.access_token, project_id=instance.project_id, cluster_id=instance.cluster_id, namespace_id=instance.namespace_id, namespace=instance.namespace, creator=instance.creator, updator=self.context["request"].user.username, created_at=instance.created, updated_at=now, resources=content, version=instance.release.chartVersionSnapshot.version, ) # compute diff old_content = instance.release.content if not old_content: old_content, _ = instance.render_app( username=self.context["request"].user.username, access_token=self.access_token) difference = simple_diff(old_content, content, instance.namespace) return { "content": preview_parse(content, instance.namespace), "notes": notes, "difference": difference, "chart_version_changed": chart_version_changed, "old_content": old_content } class Meta: fields = ( "name", "namespace_info", "chart_version", "answers", "customs", "valuefile", "content", "notes", "difference", "chart_version_changed", ) read_only_fields = ( "content", "notes", "chart_version_changed", )